From 9070ca2132d4ae25e0b5126d7e96baf6703a4919 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:43:54 +0100 Subject: [PATCH 001/215] add to release spec --- src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs | 5 +++++ src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs | 1 + .../Nethermind.Specs.Test/OverridableReleaseSpec.cs | 1 + .../Nethermind.Specs/ChainSpecStyle/ChainParameters.cs | 1 + .../Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs | 2 ++ .../ChainSpecStyle/Json/ChainSpecParamsJson.cs | 1 + src/Nethermind/Nethermind.Specs/ReleaseSpec.cs | 2 ++ 7 files changed, 13 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 85cc83f6033..b97d43e548c 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -513,5 +513,10 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// RIP-7728: L1SLOAD precompile for reading L1 storage from L2 /// public bool IsRip7728Enabled { get; } + + /// + /// EIP-7928: Block-Level Access Lists + /// + public bool IsEip7928Enabled { get; } } } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index 3d7759d6824..2434ba4862c 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -152,4 +152,5 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public bool IsEip7939Enabled => spec.IsEip7939Enabled; public bool IsEip7907Enabled => spec.IsEip7907Enabled; public bool IsRip7728Enabled => spec.IsRip7728Enabled; + public bool IsEip7928Enabled => spec.IsEip7928Enabled; } diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 2768d4c3e8e..c354ab068a3 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -196,5 +196,6 @@ public ulong Eip4844TransitionTimestamp public bool IsEip7939Enabled => spec.IsEip7939Enabled; public bool IsEip7907Enabled => spec.IsEip7907Enabled; public bool IsRip7728Enabled => spec.IsRip7728Enabled; + public bool IsEip7928Enabled => spec.IsEip7928Enabled; } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index ac09082a725..4cb47fe691e 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -172,4 +172,5 @@ public class ChainParameters #endregion public ulong? Rip7728TransitionTimestamp { get; set; } + public ulong? Eip7928TransitionTimestamp { get; set; } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 617c92ff9bc..f63fa545d95 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -195,6 +195,8 @@ bool GetForInnerPathExistence(KeyValuePair o) => Eip7934MaxRlpBlockSize = chainSpecJson.Params.Eip7934MaxRlpBlockSize ?? Eip7934Constants.DefaultMaxRlpBlockSize, Rip7728TransitionTimestamp = chainSpecJson.Params.Rip7728TransitionTimestamp, + + Eip7928TransitionTimestamp = chainSpecJson.Params.Eip7928TransitionTimestamp, }; chainSpec.Parameters.Eip152Transition ??= GetTransitionForExpectedPricing("blake2_f", "price.blake2_f.gas_per_round", 1); diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index 2b11d0e01d4..ed186bd7aa5 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -175,4 +175,5 @@ public class ChainSpecParamsJson public ulong? Eip7594TransitionTimestamp { get; set; } public ulong? Eip7939TransitionTimestamp { get; set; } public ulong? Rip7728TransitionTimestamp { get; set; } + public ulong? Eip7928TransitionTimestamp { get; set; } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 4b6dacdcf64..9a18167a7a3 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -159,5 +159,7 @@ public Address Eip2935ContractAddress Array? IReleaseSpec.EvmInstructionsTraced { get; set; } public bool IsEip7939Enabled { get; set; } public bool IsRip7728Enabled { get; set; } + + public bool IsEip7928Enabled { get; set; } } } From ea3f4d233d0654245dd6a4fc5fb7359f2d1890d6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:46:07 +0100 Subject: [PATCH 002/215] add hash to header --- src/Nethermind/Nethermind.Core/BlockHeader.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core/BlockHeader.cs b/src/Nethermind/Nethermind.Core/BlockHeader.cs index 8948f3791c6..5a74fec72a2 100644 --- a/src/Nethermind/Nethermind.Core/BlockHeader.cs +++ b/src/Nethermind/Nethermind.Core/BlockHeader.cs @@ -73,13 +73,14 @@ public BlockHeader( public Hash256? WithdrawalsRoot { get; set; } public Hash256? ParentBeaconBlockRoot { get; set; } public Hash256? RequestsHash { get; set; } + public Hash256? BlockAccessListHash { get; set; } public ulong? BlobGasUsed { get; set; } public ulong? ExcessBlobGas { get; set; } public bool HasBody => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash) || (UnclesHash is not null && UnclesHash != Keccak.OfAnEmptySequenceRlp) || (WithdrawalsRoot is not null && WithdrawalsRoot != Keccak.EmptyTreeHash); - public bool HasTransactions => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash); + public bool HasTransactions => TxRoot is not null && TxRoot != Keccak.EmptyTreeHash; public string SealEngineType { get; set; } = Core.SealEngineType.Ethash; public bool IsPostMerge { get; set; } @@ -122,6 +123,10 @@ public string ToString(string indent) { builder.AppendLine($"{indent}RequestsHash: {RequestsHash}"); } + if (BlockAccessListHash is not null) + { + builder.AppendLine($"{indent}BlockAccessListHash: {BlockAccessListHash}"); + } return builder.ToString(); } From 5fae29d0ac81be363db39fccb0ead736acb89524 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 11 Aug 2025 17:51:15 +0100 Subject: [PATCH 003/215] add to block body --- src/Nethermind/Nethermind.Core/Block.cs | 1 + src/Nethermind/Nethermind.Core/BlockBody.cs | 18 ++++++------------ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index aae9fdbbb6f..6c25efae61d 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -116,6 +116,7 @@ public Transaction[] Transactions public Hash256? ParentBeaconBlockRoot => Header.ParentBeaconBlockRoot; // do not add setter here public Hash256? RequestsHash => Header.RequestsHash; // do not add setter here + public byte[]? BlockAccessList => Body.BlockAccessList; // do not add setter here [JsonIgnore] public byte[][]? ExecutionRequests { get; set; } diff --git a/src/Nethermind/Nethermind.Core/BlockBody.cs b/src/Nethermind/Nethermind.Core/BlockBody.cs index 619a0edd8ac..6104ca6f206 100644 --- a/src/Nethermind/Nethermind.Core/BlockBody.cs +++ b/src/Nethermind/Nethermind.Core/BlockBody.cs @@ -3,15 +3,8 @@ namespace Nethermind.Core { - public class BlockBody + public class BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null) { - public BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null) - { - Transactions = transactions ?? []; - Uncles = uncles ?? []; - Withdrawals = withdrawals; - } - public BlockBody() : this(null, null, null) { } public BlockBody WithChangedTransactions(Transaction[] transactions) => new(transactions, Uncles, Withdrawals); @@ -20,13 +13,14 @@ public BlockBody() : this(null, null, null) { } public BlockBody WithChangedWithdrawals(Withdrawal[]? withdrawals) => new(Transactions, Uncles, withdrawals); - public static BlockBody WithOneTransactionOnly(Transaction tx) => new(new[] { tx }, null, null); + public static BlockBody WithOneTransactionOnly(Transaction tx) => new([tx], null, null); - public Transaction[] Transactions { get; internal set; } + public Transaction[] Transactions { get; internal set; } = transactions ?? []; - public BlockHeader[] Uncles { get; } + public BlockHeader[] Uncles { get; } = uncles ?? []; - public Withdrawal[]? Withdrawals { get; } + public Withdrawal[]? Withdrawals { get; } = withdrawals; + public byte[]? BlockAccessList { get; } public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0; } From 273905e03aedd6164b81090af8ebb3a2b0ca42e0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:21:07 +0100 Subject: [PATCH 004/215] BAL validation --- .../Messages/BlockErrorMessages.cs | 6 +++ .../Processing/BranchProcessor.cs | 6 --- .../Validators/BlockValidator.cs | 49 +++++++++++++++++++ .../Nethermind.Core/Specs/IReleaseSpec.cs | 2 + 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs index 8bcca457d05..ec1db39e983 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs @@ -150,4 +150,10 @@ public static string InvalidDepositEventLayout(string error) => public static string ExceededBlockSizeLimit(int limit) => $"ExceededBlockSizeLimit: Exceeded block size limit of {limit} bytes."; + + public const string MissingBlockLevelAccessList = "MissingBlockLevelAccessList: Must be present in block body."; + + public static string InvalidBlockLevelAccessListRoot(Hash256 expected, Hash256 actual) => + $"InvalidBlockLevelAccessListRoot: expected {expected}, got {actual}"; + } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BranchProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BranchProcessor.cs index 7dd0351fa37..2a0cad1c5e0 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BranchProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BranchProcessor.cs @@ -6,12 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Blockchain.BeaconBlockRoot; -using Nethermind.Blockchain.Blocks; -using Nethermind.Blockchain.Receipts; -using Nethermind.Consensus.ExecutionRequests; -using Nethermind.Consensus.Rewards; -using Nethermind.Consensus.Validators; -using Nethermind.Consensus.Withdrawals; using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 0f8e98ac4f5..35384b53474 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -391,6 +391,43 @@ public virtual bool ValidateBodyAgainstHeader(BlockHeader header, BlockBody toBe return true; } + public virtual bool ValidateBlockLevelAccessList(Block block, IReleaseSpec spec, out string? error) + { + if (spec.BlockLevelAccessListsEnabled && block.BlockAccessList is null) + { + error = BlockErrorMessages.MissingBlockLevelAccessList; + + if (_logger.IsWarn) _logger.Warn($"Block level access list cannot be null in block {block.Hash} when EIP-7928 activated."); + + return false; + } + + if (!spec.BlockLevelAccessListsEnabled && block.BlockAccessList is not null) + { + error = BlockErrorMessages.WithdrawalsNotEnabled; + + if (_logger.IsWarn) _logger.Warn($"Block level access list must be null in block {block.Hash} when EIP-7928 not activated."); + + return false; + } + + if (block.BlockAccessList is not null) + { + if (!ValidateBlockLevelAccessListHashMatces(block.Header, block.Body, out Hash256 blockLevelAccessListRoot)) + { + error = BlockErrorMessages.InvalidBlockLevelAccessListRoot(block.Header.BlockAccessListHash, blockLevelAccessListRoot); + if (_logger.IsWarn) _logger.Warn($"Block level access list root hash mismatch in block {block.ToString(Block.Format.FullHashAndNumber)}: expected {block.Header.BlockAccessListHash}, got {blockLevelAccessListRoot}"); + + return false; + } + } + + error = null; + + return true; + + } + public static bool ValidateTxRootMatchesTxs(Block block, out Hash256 txRoot) => ValidateTxRootMatchesTxs(block.Header, block.Body, out txRoot); @@ -417,6 +454,18 @@ public static bool ValidateWithdrawalsHashMatches(BlockHeader header, BlockBody return (withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash) == header.WithdrawalsRoot; } + public static bool ValidateBlockLevelAccessListHashMatces(BlockHeader header, BlockBody body, out Hash256? blockLevelAccessListRoot) + { + if (body.BlockAccessList is null) + { + blockLevelAccessListRoot = null; + return header.BlockAccessListHash is null; + } + + // todo: SSZ encode here + return (blockLevelAccessListRoot = Hash256.Zero) == header.BlockAccessListHash; + } + private static string Invalid(Block block) => $"Invalid block {block.ToString(Block.Format.FullHashAndNumber)}:"; } diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index b97d43e548c..2587533f7bb 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -466,6 +466,8 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec public bool RequestsEnabled => ConsolidationRequestsEnabled || WithdrawalRequestsEnabled || DepositsEnabled; + bool BlockLevelAccessListsEnabled => IsEip7928Enabled; + public bool IsEip7594Enabled { get; } /// From 0246d856cdcacfe2ffe96adbb70b579c469c379f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:24:21 +0100 Subject: [PATCH 005/215] add to constuctor --- src/Nethermind/Nethermind.Core/BlockBody.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockBody.cs b/src/Nethermind/Nethermind.Core/BlockBody.cs index 6104ca6f206..a443e1d24f5 100644 --- a/src/Nethermind/Nethermind.Core/BlockBody.cs +++ b/src/Nethermind/Nethermind.Core/BlockBody.cs @@ -3,7 +3,7 @@ namespace Nethermind.Core { - public class BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null) + public class BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null, byte[]? blockLevelAccessList = null) { public BlockBody() : this(null, null, null) { } @@ -20,7 +20,7 @@ public BlockBody() : this(null, null, null) { } public BlockHeader[] Uncles { get; } = uncles ?? []; public Withdrawal[]? Withdrawals { get; } = withdrawals; - public byte[]? BlockAccessList { get; } + public byte[]? BlockAccessList { get; } = blockLevelAccessList ?? []; public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0; } From db756e64e28c7fd58f997cb1b92c967f0a8ee111 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 12 Aug 2025 16:16:13 +0100 Subject: [PATCH 006/215] set BAL at end of processing --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 9 +++------ src/Nethermind/Nethermind.Core/BlockBody.cs | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 72f350bffab..6f670dbcd63 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -2,11 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using System.Threading; -using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; using Nethermind.Blockchain.Blocks; @@ -17,8 +15,6 @@ using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Core.Threading; using Nethermind.Crypto; @@ -31,8 +27,6 @@ using Nethermind.State; using static Nethermind.Consensus.Processing.IBlockProcessor; -using Metrics = Nethermind.Blockchain.Metrics; - namespace Nethermind.Consensus.Processing; public partial class BlockProcessor( @@ -104,6 +98,7 @@ protected virtual TxReceipt[] ProcessBlock( IReleaseSpec spec, CancellationToken token) { + BlockBody body = block.Body; BlockHeader header = block.Header; ReceiptsTracer.SetOtherTracer(blockTracer); @@ -154,6 +149,8 @@ protected virtual TxReceipt[] ProcessBlock( } header.Hash = header.CalculateHash(); + // create from block.AccountChanges and encode + body.BlockAccessList = []; return receipts; } diff --git a/src/Nethermind/Nethermind.Core/BlockBody.cs b/src/Nethermind/Nethermind.Core/BlockBody.cs index a443e1d24f5..0485b8f51dd 100644 --- a/src/Nethermind/Nethermind.Core/BlockBody.cs +++ b/src/Nethermind/Nethermind.Core/BlockBody.cs @@ -20,7 +20,7 @@ public BlockBody() : this(null, null, null) { } public BlockHeader[] Uncles { get; } = uncles ?? []; public Withdrawal[]? Withdrawals { get; } = withdrawals; - public byte[]? BlockAccessList { get; } = blockLevelAccessList ?? []; + public byte[]? BlockAccessList { get; internal set; } = blockLevelAccessList ?? []; public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0; } From 8c6f4e2304b0b7882b9dad14ba87bb90cb005ba8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 12 Aug 2025 17:28:25 +0100 Subject: [PATCH 007/215] constants, ssz containers --- ...sor.BlockValidationTransactionsExecutor.cs | 2 - .../Nethermind.Core/Eip7928Constants.cs | 13 +++ .../BlockAccessLists/BlockAccessLists.cs | 95 +++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.Core/Eip7928Constants.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index bff0bd56ef7..7293bb5240b 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -9,10 +9,8 @@ using Nethermind.Blockchain; using Nethermind.Blockchain.Tracing; using Nethermind.Core; -using Nethermind.Core.Specs; using Nethermind.Evm; using Nethermind.Evm.State; -using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; using Metrics = Nethermind.Evm.Metrics; diff --git a/src/Nethermind/Nethermind.Core/Eip7928Constants.cs b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs new file mode 100644 index 00000000000..cafa85007c4 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public static class Eip7928Constants +{ + public const int MaxTxs = 30_000; + public const int MaxSlots = 300_000; + public const int MaxAccounts = 300_000; + public const int MaxCodeSize = 24_576; + public const int MaxCodeChanges = 1; +} diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs b/src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs new file mode 100644 index 00000000000..f385d2ee262 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Core; + +// move somewhere +namespace Nethermind.Serialization.Ssz; + +// Single storage write: tx_index -> new_value +[SszSerializable] +public struct StorageChange +{ + public ushort TxIndex { get; set; } + [SszVector(32)] + public byte[] NewValue { get; set; } +} + +// Single balance change: tx_index -> post_balance +[SszSerializable] +public struct BalanceChange +{ + public ushort TxIndex { get; set; } + public UInt128 PostBalance { get; set; } +} + +// Single nonce change: tx_index -> new_nonce +[SszSerializable] +public struct NonceChange +{ + public ushort TxIndex { get; set; } + public ulong NewNonce { get; set; } +} + +// Single code change: tx_index -> new_code +[SszSerializable] +public struct CodeChange +{ + public ushort TxIndex { get; set; } + + [SszList(Eip7928Constants.MaxCodeSize)] + public byte[] NewCode { get; set; } +} + +[SszSerializable] +public struct SlotChanges +{ + [SszVector(32)] + public byte[] Slot { get; set; } + + [SszList(Eip7928Constants.MaxTxs)] + public ulong Changes { get; set; } +} + +[SszSerializable] +public struct StorageKey +{ + [SszVector(32)] + public byte[] Key { get; set; } +} + +[SszSerializable] +public struct AccoutChanges +{ + [SszVector(20)] + public byte[] Address { get; set; } + + // Storage changes (slot -> [tx_index -> new_value]) + [SszList(Eip7928Constants.MaxSlots)] + public List StorageChanges { get; set; } + + // Read-only storage keys + [SszList(Eip7928Constants.MaxSlots)] + public List StorageReads { get; set; } + + // Balance changes ([tx_index -> post_balance]) + [SszList(Eip7928Constants.MaxTxs)] + public List BalanceChanges { get; set; } + + // Nonce changes ([tx_index -> new_nonce]) + [SszList(Eip7928Constants.MaxTxs)] + public List NonceChanges { get; set; } + + // Code changes ([tx_index -> new_code]) + [SszList(Eip7928Constants.MaxCodeChanges)] + public List CodeChanges { get; set; } +} + +[SszSerializable] +public struct BlockAccessList +{ + [SszList(Eip7928Constants.MaxAccounts)] + public List AccountChanges { get; set; } +} From dbed6d22fbcd76ba1d7ac7d2bdba63a6e3986fe8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 15 Aug 2025 12:39:10 +0100 Subject: [PATCH 008/215] add block access tracer --- .../Tracing/BlockAccessTracer.cs | 238 ++++++++++++++++++ .../Processing/BlockProcessor.cs | 7 +- 2 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs new file mode 100644 index 00000000000..00d3247be31 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -0,0 +1,238 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; + +namespace Nethermind.Blockchain.Tracing; + +public struct Access +{ + +} + +public class BlockAccessTracer : IBlockTracer, ITxTracer, IJournal +{ + // private IBlockTracer _otherTracer = NullBlockTracer.Instance; + protected Block Block = null!; + public bool IsTracingReceipt => false; + public bool IsTracingActions => false; + public bool IsTracingOpLevelStorage => false; + public bool IsTracingMemory => false; + public bool IsTracingInstructions => false; + public bool IsTracingRefunds => false; + public bool IsTracingCode => false; + public bool IsTracingStack => false; + public bool IsTracingState => true; + public bool IsTracingStorage => true; + + public bool IsTracingBlockHash => false; + public bool IsTracingAccess => true; + public bool IsTracingFees => false; + public bool IsTracingLogs => false; + + public void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) + { + // _txReceipts.Add(BuildReceipt(recipient, gasSpent.SpentGas, StatusCode.Success, logs, stateRoot)); + } + + public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) + { + // _txReceipts.Add(BuildFailedReceipt(recipient, gasSpent.SpentGas, error, stateRoot)); + + } + + protected TxReceipt BuildFailedReceipt(Address recipient, long gasSpent, string error, Hash256? stateRoot) + { + TxReceipt receipt = BuildReceipt(recipient, gasSpent, StatusCode.Failure, [], stateRoot); + receipt.Error = error; + return receipt; + } + + protected virtual TxReceipt BuildReceipt(Address recipient, long spentGas, byte statusCode, LogEntry[] logEntries, Hash256? stateRoot) + { + Transaction transaction = CurrentTx!; + TxReceipt txReceipt = new() + { + Logs = logEntries, + TxType = transaction.Type, + // Bloom calculated in parallel with other receipts + GasUsedTotal = Block.GasUsed, + StatusCode = statusCode, + Recipient = transaction.IsContractCreation ? null : recipient, + BlockHash = Block.Hash, + BlockNumber = Block.Number, + Index = _currentIndex, + GasUsed = spentGas, + Sender = transaction.SenderAddress, + ContractAddress = transaction.IsContractCreation ? recipient : null, + TxHash = transaction.Hash, + PostTransactionState = stateRoot + }; + + return txReceipt; + } + + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) {} + + public void ReportOperationError(EvmExceptionType error) {} + + + public void ReportOperationRemainingGas(long gas) {} + + public void ReportLog(LogEntry log) {} + + public void SetOperationMemorySize(ulong newSize) {} + + public void ReportMemoryChange(long offset, in ReadOnlySpan data) {} + + public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) + { + _accesses.Add(new()); + } + + public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) {} + + public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) {} + + public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) {} + + public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) + { + _accesses.Add(new()); + } + + public void ReportCodeChange(Address address, byte[] before, byte[] after) + { + _accesses.Add(new()); + } + + public void ReportNonceChange(Address address, UInt256? before, UInt256? after) + { + _accesses.Add(new()); + } + + public void ReportAccountRead(Address address) + { + _accesses.Add(new()); + } + + public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) + { + _accesses.Add(new()); + } + + public void ReportStorageRead(in StorageCell storageCell) + { + _accesses.Add(new()); + } + + public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) {} + + public void ReportActionEnd(long gas, ReadOnlyMemory output) {} + + public void ReportActionError(EvmExceptionType exceptionType) {} + + public void ReportActionRevert(long gasLeft, ReadOnlyMemory output) {} + + public void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory deployedCode) {} + + public void ReportByteCode(ReadOnlyMemory byteCode) {} + + public void ReportGasUpdateForVmTrace(long refund, long gasAvailable) {} + + public void ReportRefund(long refund) {} + + public void ReportExtraGasPressure(long extraGasPressure) {} + + public void ReportAccess(IReadOnlyCollection
accessedAddresses, IReadOnlyCollection accessedStorageCells) + { + _accesses.Add(new()); + } + + public void SetOperationStack(TraceStack stack) {} + + public void ReportStackPush(in ReadOnlySpan stackItem) {} + + public void ReportBlockHash(Hash256 blockHash) {} + + public void SetOperationMemory(TraceMemory memoryTrace) {} + + public void ReportFees(UInt256 fees, UInt256 burntFees) {} + + // private ITxTracer _currentTxTracer = NullTxTracer.Instance; + protected int _currentIndex { get; private set; } + // private readonly List _txReceipts = new(); + private readonly List _accesses = []; + protected Transaction? CurrentTx; + public IReadOnlyList Accesses => _accesses; + // public IReadOnlyList TxReceipts => _txReceipts; + // public TxReceipt LastReceipt => _txReceipts[^1]; + public bool IsTracingRewards => false; + + // public ITxTracer InnerTracer => _currentTxTracer; + + public int TakeSnapshot() => _accesses.Count; + + public void Restore(int snapshot) + { + // int numToRemove = _txReceipts.Count - snapshot; + + // for (int i = 0; i < numToRemove; i++) + // { + // _txReceipts.RemoveAt(_txReceipts.Count - 1); + // } + + // Block.Header.GasUsed = _txReceipts.Count > 0 ? _txReceipts.Last().GasUsedTotal : 0; + } + + public void ReportReward(Address author, string rewardType, UInt256 rewardValue) {} + + public void StartNewBlockTrace(Block block) + { + Block = block; + _currentIndex = 0; + _accesses.Clear(); + } + + public ITxTracer StartNewTxTrace(Transaction? tx) + { + return this; + } + + public void EndTxTrace() + { + _currentIndex++; + } + + public void EndBlockTrace() + { + // _otherTracer.EndBlockTrace(); + // if (_txReceipts.Count > 0) + // { + // Bloom blockBloom = new(); + // Block.Header.Bloom = blockBloom; + // for (int index = 0; index < _txReceipts.Count; index++) + // { + // TxReceipt? receipt = _txReceipts[index]; + // blockBloom.Accumulate(receipt.Bloom!); + // } + // } + } + + // public void SetOtherTracer(IBlockTracer blockTracer) + // { + // _otherTracer = blockTracer; + // } + + public void Dispose() + { + // _currentTxTracer.Dispose(); + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 6f670dbcd63..a6e14d4907c 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -52,6 +52,7 @@ public partial class BlockProcessor( /// to any block-specific tracers. ///
protected BlockReceiptsTracer ReceiptsTracer { get; set; } = new(); + protected BlockAccessTracer BlockAccessTracer { get; set; } = new(); public event EventHandler TransactionProcessed { @@ -101,7 +102,10 @@ protected virtual TxReceipt[] ProcessBlock( BlockBody body = block.Body; BlockHeader header = block.Header; - ReceiptsTracer.SetOtherTracer(blockTracer); + CompositeBlockTracer compositeBlockTracer = new(); + compositeBlockTracer.Add(blockTracer); + compositeBlockTracer.Add(BlockAccessTracer); + ReceiptsTracer.SetOtherTracer(compositeBlockTracer); ReceiptsTracer.StartNewBlockTrace(block); blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); @@ -150,6 +154,7 @@ protected virtual TxReceipt[] ProcessBlock( header.Hash = header.CalculateHash(); // create from block.AccountChanges and encode + System.Collections.Generic.IReadOnlyList accesses = ReceiptsTracer.GetTracer().Accesses; body.BlockAccessList = []; return receipts; From 7d7cb298e4a243b648f283c6a792eaf064597226 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:09:27 +0100 Subject: [PATCH 009/215] construct BAL in tracer --- .../Tracing}/BlockAccessLists.cs | 39 ++++---- .../Tracing/BlockAccessTracer.cs | 90 +++++++++---------- .../Processing/BlockProcessor.cs | 5 +- 3 files changed, 59 insertions(+), 75 deletions(-) rename src/Nethermind/{Nethermind.Serialization.Ssz/BlockAccessLists => Nethermind.Blockchain/Tracing}/BlockAccessLists.cs (69%) diff --git a/src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs similarity index 69% rename from src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs rename to src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs index f385d2ee262..cd9157c396e 100644 --- a/src/Nethermind/Nethermind.Serialization.Ssz/BlockAccessLists/BlockAccessLists.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs @@ -5,20 +5,17 @@ using System.Collections.Generic; using Nethermind.Core; -// move somewhere -namespace Nethermind.Serialization.Ssz; +namespace Nethermind.Blockchain.Tracing; // Single storage write: tx_index -> new_value -[SszSerializable] public struct StorageChange { public ushort TxIndex { get; set; } - [SszVector(32)] + // [SszVector(32)] public byte[] NewValue { get; set; } } // Single balance change: tx_index -> post_balance -[SszSerializable] public struct BalanceChange { public ushort TxIndex { get; set; } @@ -26,7 +23,6 @@ public struct BalanceChange } // Single nonce change: tx_index -> new_nonce -[SszSerializable] public struct NonceChange { public ushort TxIndex { get; set; } @@ -34,62 +30,57 @@ public struct NonceChange } // Single code change: tx_index -> new_code -[SszSerializable] public struct CodeChange { public ushort TxIndex { get; set; } - [SszList(Eip7928Constants.MaxCodeSize)] + // [SszList(Eip7928Constants.MaxCodeSize)] public byte[] NewCode { get; set; } } -[SszSerializable] public struct SlotChanges { - [SszVector(32)] + // [SszVector(32)] public byte[] Slot { get; set; } - [SszList(Eip7928Constants.MaxTxs)] + // [SszList(Eip7928Constants.MaxTxs)] public ulong Changes { get; set; } } -[SszSerializable] public struct StorageKey { - [SszVector(32)] + // [SszVector(32)] public byte[] Key { get; set; } } -[SszSerializable] -public struct AccoutChanges +public struct AccountChanges { - [SszVector(20)] + // [SszVector(20)] public byte[] Address { get; set; } // Storage changes (slot -> [tx_index -> new_value]) - [SszList(Eip7928Constants.MaxSlots)] + // [SszList(Eip7928Constants.MaxSlots)] public List StorageChanges { get; set; } // Read-only storage keys - [SszList(Eip7928Constants.MaxSlots)] + // [SszList(Eip7928Constants.MaxSlots)] public List StorageReads { get; set; } // Balance changes ([tx_index -> post_balance]) - [SszList(Eip7928Constants.MaxTxs)] + // [SszList(Eip7928Constants.MaxTxs)] public List BalanceChanges { get; set; } // Nonce changes ([tx_index -> new_nonce]) - [SszList(Eip7928Constants.MaxTxs)] + // [SszList(Eip7928Constants.MaxTxs)] public List NonceChanges { get; set; } // Code changes ([tx_index -> new_code]) - [SszList(Eip7928Constants.MaxCodeChanges)] + // [SszList(Eip7928Constants.MaxCodeChanges)] public List CodeChanges { get; set; } } -[SszSerializable] public struct BlockAccessList { - [SszList(Eip7928Constants.MaxAccounts)] - public List AccountChanges { get; set; } + // [SszList(Eip7928Constants.MaxAccounts)] + public Dictionary AccountChanges { get; set; } } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 00d3247be31..bc841465a0a 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -12,11 +12,6 @@ namespace Nethermind.Blockchain.Tracing; -public struct Access -{ - -} - public class BlockAccessTracer : IBlockTracer, ITxTracer, IJournal { // private IBlockTracer _otherTracer = NullBlockTracer.Instance; @@ -48,37 +43,6 @@ public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, } - protected TxReceipt BuildFailedReceipt(Address recipient, long gasSpent, string error, Hash256? stateRoot) - { - TxReceipt receipt = BuildReceipt(recipient, gasSpent, StatusCode.Failure, [], stateRoot); - receipt.Error = error; - return receipt; - } - - protected virtual TxReceipt BuildReceipt(Address recipient, long spentGas, byte statusCode, LogEntry[] logEntries, Hash256? stateRoot) - { - Transaction transaction = CurrentTx!; - TxReceipt txReceipt = new() - { - Logs = logEntries, - TxType = transaction.Type, - // Bloom calculated in parallel with other receipts - GasUsedTotal = Block.GasUsed, - StatusCode = statusCode, - Recipient = transaction.IsContractCreation ? null : recipient, - BlockHash = Block.Hash, - BlockNumber = Block.Number, - Index = _currentIndex, - GasUsed = spentGas, - Sender = transaction.SenderAddress, - ContractAddress = transaction.IsContractCreation ? recipient : null, - TxHash = transaction.Hash, - PostTransactionState = stateRoot - }; - - return txReceipt; - } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) {} public void ReportOperationError(EvmExceptionType error) {} @@ -94,7 +58,7 @@ public void ReportMemoryChange(long offset, in ReadOnlySpan data) {} public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) { - _accesses.Add(new()); + //_bal.AccountChanges[].StorageChanges() } public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) {} @@ -105,32 +69,61 @@ public void ReportSelfDestruct(Address address, UInt256 balance, Address refundA public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { - _accesses.Add(new()); + BalanceChange balanceChange = new() + { + TxIndex = (ushort)_currentIndex, + PostBalance = (ulong)after // why not 256 bit? + }; + _bal.AccountChanges[address].BalanceChanges.Add(balanceChange); } public void ReportCodeChange(Address address, byte[] before, byte[] after) { - _accesses.Add(new()); + CodeChange codeChange = new() + { + TxIndex = (ushort)_currentIndex, + NewCode = after + }; + _bal.AccountChanges[address].CodeChanges.Add(codeChange); } public void ReportNonceChange(Address address, UInt256? before, UInt256? after) { - _accesses.Add(new()); + NonceChange nonceChange = new() + { + TxIndex = (ushort)_currentIndex, + NewNonce = (ulong)after + }; + _bal.AccountChanges[address].NonceChanges.Add(nonceChange); } public void ReportAccountRead(Address address) { - _accesses.Add(new()); + if (!_bal.AccountChanges.ContainsKey(address)) + { + _bal.AccountChanges.Add(address, new()); + } } public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) { - _accesses.Add(new()); + StorageChange storageChange = new() + { + TxIndex = (ushort)_currentIndex, + NewValue = after + }; + Address address = Address.Zero; + _bal.AccountChanges[address].StorageChanges.Add(storageChange); } public void ReportStorageRead(in StorageCell storageCell) { - _accesses.Add(new()); + StorageKey storageKey = new() + { + Key = storageCell.Hash.ToByteArray() + }; + Address address = Address.Zero; + _bal.AccountChanges[address].StorageReads.Add(storageKey); } public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) {} @@ -153,7 +146,7 @@ public void ReportExtraGasPressure(long extraGasPressure) {} public void ReportAccess(IReadOnlyCollection
accessedAddresses, IReadOnlyCollection accessedStorageCells) { - _accesses.Add(new()); + // _bal.Add(new()); } public void SetOperationStack(TraceStack stack) {} @@ -169,16 +162,15 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) {} // private ITxTracer _currentTxTracer = NullTxTracer.Instance; protected int _currentIndex { get; private set; } // private readonly List _txReceipts = new(); - private readonly List _accesses = []; + private BlockAccessList _bal = new(); protected Transaction? CurrentTx; - public IReadOnlyList Accesses => _accesses; - // public IReadOnlyList TxReceipts => _txReceipts; + public BlockAccessList BlockAccessList => _bal; // public TxReceipt LastReceipt => _txReceipts[^1]; public bool IsTracingRewards => false; // public ITxTracer InnerTracer => _currentTxTracer; - public int TakeSnapshot() => _accesses.Count; + public int TakeSnapshot() => _bal.AccountChanges.Count; public void Restore(int snapshot) { @@ -198,7 +190,7 @@ public void StartNewBlockTrace(Block block) { Block = block; _currentIndex = 0; - _accesses.Clear(); + _bal = new(); } public ITxTracer StartNewTxTrace(Transaction? tx) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index a6e14d4907c..da71b1747bb 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -118,6 +118,8 @@ protected virtual TxReceipt[] ProcessBlock( _stateProvider.Commit(spec, commitRoots: false); + BlockAccessList bal = ReceiptsTracer.GetTracer().BlockAccessList; + CalculateBlooms(receipts); if (spec.IsEip4844Enabled) @@ -153,8 +155,7 @@ protected virtual TxReceipt[] ProcessBlock( } header.Hash = header.CalculateHash(); - // create from block.AccountChanges and encode - System.Collections.Generic.IReadOnlyList accesses = ReceiptsTracer.GetTracer().Accesses; + // RLP encode bal body.BlockAccessList = []; return receipts; From ec347c17d64b33c0eac8f36f3fd2a13e63bc67c7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:15:10 +0100 Subject: [PATCH 010/215] call byte encode, move BAL to core --- .../Nethermind.Blockchain/Tracing/BlockAccessTracer.cs | 1 + .../Nethermind.Consensus/Processing/BlockProcessor.cs | 4 ++-- .../BlockAccessList.cs} | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) rename src/Nethermind/{Nethermind.Blockchain/Tracing/BlockAccessLists.cs => Nethermind.Core/BlockAccessList.cs} (95%) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index bc841465a0a..a7b32afdbe8 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; using Nethermind.Evm; using Nethermind.Evm.Tracing; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index da71b1747bb..f41d34de2e2 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -15,6 +15,7 @@ using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Specs; using Nethermind.Core.Threading; using Nethermind.Crypto; @@ -155,8 +156,7 @@ protected virtual TxReceipt[] ProcessBlock( } header.Hash = header.CalculateHash(); - // RLP encode bal - body.BlockAccessList = []; + body.BlockAccessList = bal.Bytes; return receipts; } diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs similarity index 95% rename from src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs rename to src/Nethermind/Nethermind.Core/BlockAccessList.cs index cd9157c396e..f597fe9f8d5 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessLists.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -3,9 +3,8 @@ using System; using System.Collections.Generic; -using Nethermind.Core; -namespace Nethermind.Blockchain.Tracing; +namespace Nethermind.Core.BlockAccessLists; // Single storage write: tx_index -> new_value public struct StorageChange @@ -83,4 +82,7 @@ public struct BlockAccessList { // [SszList(Eip7928Constants.MaxAccounts)] public Dictionary AccountChanges { get; set; } + + // RLP encode bal + public byte[] Bytes => []; } From 675485ce18ed7cec398e1d03d9e13d2fac7b78b8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:45:27 +0100 Subject: [PATCH 011/215] block access tracer improvements & tests --- .../BlockAccessListTests.cs | 80 +++++++++++++++++++ .../TransactionProcessorTests.cs | 10 +-- .../Tracing/BlockAccessTracer.cs | 56 +++++++++++-- .../Nethermind.Core/BlockAccessList.cs | 23 +++--- .../TransactionProcessor.cs | 1 + 5 files changed, 146 insertions(+), 24 deletions(-) create mode 100644 src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs new file mode 100644 index 00000000000..8cd90d36997 --- /dev/null +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Blockchain.Tracing; +using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using NUnit.Framework; + +//move all to correct folder +namespace Nethermind.Evm.Test +{ + [TestFixture] + public class BlockAccessListTests() : TransactionProcessorTests(true) + { + [Test] + public void Empty_account_changes() + { + Block block = Build.A.Block.WithTransactions(Build.A.Transaction.TestObject).TestObject; + + BlockAccessTracer tracer = new(); + tracer.StartNewBlockTrace(block); + tracer.StartNewTxTrace(block.Transactions[0]); + tracer.MarkAsSuccess(TestItem.AddressA, 100, [], [], TestItem.KeccakF); + + Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); + } + + [Test] + public void Balance_and_nonce_changes() + { + ulong gasPrice = 2; + long gasLimit = 100000; + Transaction tx = Build.A.Transaction + .WithTo(TestItem.AddressB) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .TestObject; + + Block block = Build.A.Block + .WithTransactions(tx) + .WithBaseFeePerGas(1) + .WithBeneficiary(TestItem.AddressC).TestObject; + + BlockReceiptsTracer blockReceiptsTracer = new(); + BlockAccessTracer accessTracer = new(); + blockReceiptsTracer.SetOtherTracer(accessTracer); + Execute(tx, block, blockReceiptsTracer); + + Dictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; + Assert.That(accountChanges, Has.Count.EqualTo(3)); + + List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; + List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; + // refund? + using (Assert.EnterMultipleScope()) + { + Assert.That(senderBalanceChanges, Has.Count.EqualTo(2)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * (ulong)gasLimit)); + Assert.That(senderBalanceChanges[1].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + + Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); + Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); + + } + + // zero balance change should not be recorded + List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + Assert.That(toBalanceChanges, Is.Empty); + + List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + } + } +} diff --git a/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs index e515a2480ec..77392d405a6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs @@ -11,7 +11,6 @@ using Nethermind.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; -using Nethermind.Db; using Nethermind.Int256; using Nethermind.Evm.Tracing; using Nethermind.Blockchain.Tracing.GethStyle; @@ -21,7 +20,6 @@ using Nethermind.Serialization.Json; using Nethermind.Specs.Forks; using Nethermind.Evm.State; -using Nethermind.Trie.Pruning; using NUnit.Framework; using Nethermind.Config; using System.Collections.Generic; @@ -36,12 +34,12 @@ namespace Nethermind.Evm.Test; [TestFixture(false)] [Todo(Improve.Refactor, "Check why fixture test cases did not work")] [Parallelizable(ParallelScope.Self)] -public class TransactionProcessorTests +public abstract class TransactionProcessorTests { private readonly bool _isEip155Enabled; private readonly ISpecProvider _specProvider; private IEthereumEcdsa _ethereumEcdsa; - private ITransactionProcessor _transactionProcessor; + protected ITransactionProcessor _transactionProcessor; private IWorldState _stateProvider; public TransactionProcessorTests(bool eip155Enabled) @@ -50,7 +48,7 @@ public TransactionProcessorTests(bool eip155Enabled) _specProvider = MainnetSpecProvider.Instance; } - private static readonly UInt256 AccountBalance = 1.Ether(); + protected static readonly UInt256 AccountBalance = 1.Ether(); [SetUp] public void Setup() @@ -735,7 +733,7 @@ private BlockReceiptsTracer BuildTracer(Block block, Transaction tx, bool stateD return tracer; } - private TransactionResult Execute(Transaction tx, Block block, BlockReceiptsTracer? tracer = null) + protected TransactionResult Execute(Transaction tx, Block block, BlockReceiptsTracer? tracer = null) { tracer?.StartNewBlockTrace(block); tracer?.StartNewTxTrace(tx); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index a7b32afdbe8..7f1174ecbc9 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -73,9 +73,23 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after BalanceChange balanceChange = new() { TxIndex = (ushort)_currentIndex, - PostBalance = (ulong)after // why not 256 bit? + // PostBalance = (ulong)after // why not 256 bit? + PostBalance = after!.Value }; - _bal.AccountChanges[address].BalanceChanges.Add(balanceChange); + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + // show should refunds be handled? + + // don't add zero balance transfers, but add empty account changes + if ((before ?? 0) != after) + { + accountChanges.BalanceChanges.Add(balanceChange); + } } public void ReportCodeChange(Address address, byte[] before, byte[] after) @@ -85,7 +99,14 @@ public void ReportCodeChange(Address address, byte[] before, byte[] after) TxIndex = (ushort)_currentIndex, NewCode = after }; - _bal.AccountChanges[address].CodeChanges.Add(codeChange); + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + accountChanges.CodeChanges.Add(codeChange); } public void ReportNonceChange(Address address, UInt256? before, UInt256? after) @@ -95,14 +116,21 @@ public void ReportNonceChange(Address address, UInt256? before, UInt256? after) TxIndex = (ushort)_currentIndex, NewNonce = (ulong)after }; - _bal.AccountChanges[address].NonceChanges.Add(nonceChange); + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + accountChanges.NonceChanges.Add(nonceChange); } public void ReportAccountRead(Address address) { if (!_bal.AccountChanges.ContainsKey(address)) { - _bal.AccountChanges.Add(address, new()); + _bal.AccountChanges.Add(address, new(address)); } } @@ -114,7 +142,14 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ NewValue = after }; Address address = Address.Zero; - _bal.AccountChanges[address].StorageChanges.Add(storageChange); + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + accountChanges.StorageChanges.Add(storageChange); } public void ReportStorageRead(in StorageCell storageCell) @@ -124,7 +159,14 @@ public void ReportStorageRead(in StorageCell storageCell) Key = storageCell.Hash.ToByteArray() }; Address address = Address.Zero; - _bal.AccountChanges[address].StorageReads.Add(storageKey); + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + accountChanges.StorageReads.Add(storageKey); } public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) {} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index f597fe9f8d5..1db7a375963 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Collections.Generic; +using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; @@ -18,7 +18,8 @@ public struct StorageChange public struct BalanceChange { public ushort TxIndex { get; set; } - public UInt128 PostBalance { get; set; } + // actually UInt128 + public UInt256 PostBalance { get; set; } } // Single nonce change: tx_index -> new_nonce @@ -52,36 +53,36 @@ public struct StorageKey public byte[] Key { get; set; } } -public struct AccountChanges +public struct AccountChanges(Address address) { // [SszVector(20)] - public byte[] Address { get; set; } + public byte[] Address { get; set; } = address.Bytes; // Storage changes (slot -> [tx_index -> new_value]) // [SszList(Eip7928Constants.MaxSlots)] - public List StorageChanges { get; set; } + public List StorageChanges { get; set; } = []; // Read-only storage keys // [SszList(Eip7928Constants.MaxSlots)] - public List StorageReads { get; set; } + public List StorageReads { get; set; } = []; // Balance changes ([tx_index -> post_balance]) // [SszList(Eip7928Constants.MaxTxs)] - public List BalanceChanges { get; set; } + public List BalanceChanges { get; set; } = []; // Nonce changes ([tx_index -> new_nonce]) // [SszList(Eip7928Constants.MaxTxs)] - public List NonceChanges { get; set; } + public List NonceChanges { get; set; } = []; // Code changes ([tx_index -> new_code]) // [SszList(Eip7928Constants.MaxCodeChanges)] - public List CodeChanges { get; set; } + public List CodeChanges { get; set; } = []; } -public struct BlockAccessList +public struct BlockAccessList() { // [SszList(Eip7928Constants.MaxAccounts)] - public Dictionary AccountChanges { get; set; } + public Dictionary AccountChanges { get; set; } = []; // RLP encode bal public byte[] Bytes => []; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 0e5458317f6..51e09fd8efa 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -867,6 +867,7 @@ protected virtual GasConsumed Refund(Transaction tx, BlockHeader header, IReleas spentGas = Math.Max(spentGas, floorGas); // If noValidation we didn't charge for gas, so do not refund + // report to tracer?? if (!opts.HasFlag(ExecutionOptions.SkipValidation)) WorldState.AddToBalance(tx.SenderAddress!, (ulong)(tx.GasLimit - spentGas) * gasPrice, spec); From 65bcb30501b349668595a3df495a239e70f7d315 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 18 Aug 2025 19:08:20 +0100 Subject: [PATCH 012/215] add to execution payload --- src/Nethermind/Nethermind.Core/Block.cs | 7 +++++-- .../Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 8 +++++--- src/Nethermind/Nethermind.State/StateProvider.cs | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 6c25efae61d..a064acc71b2 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -12,6 +12,8 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Int256; +using Nethermind.Core.BlockAccessLists; +using Nethermind.Serialization.Json; namespace Nethermind.Core; @@ -27,10 +29,11 @@ public Block(BlockHeader header, BlockBody body) public Block(BlockHeader header, IEnumerable transactions, IEnumerable uncles, - IEnumerable? withdrawals = null) + IEnumerable? withdrawals = null, + byte[]? blockAccessList = null) { Header = header ?? throw new ArgumentNullException(nameof(header)); - Body = new(transactions.ToArray(), uncles.ToArray(), withdrawals?.ToArray()); + Body = new(transactions.ToArray(), uncles.ToArray(), withdrawals?.ToArray(), blockAccessList); } public Block(BlockHeader header) : this( diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 6e958ad8079..a3bbb7eb2e4 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -12,7 +11,6 @@ using Nethermind.Serialization.Rlp; using Nethermind.State.Proofs; using System.Text.Json.Serialization; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Quic; using Nethermind.Core.ExecutionRequest; namespace Nethermind.Merge.Plugin.Data; @@ -53,6 +51,8 @@ public class ExecutionPayload : IForkValidator, IExecutionPayloadParams, IExecut public ulong Timestamp { get; set; } + public byte[] BlockAccessList { get; set; } = []; + protected byte[][] _encodedTransactions = []; /// @@ -126,6 +126,7 @@ public byte[][] Transactions Timestamp = block.Timestamp, BaseFeePerGas = block.BaseFeePerGas, Withdrawals = block.Withdrawals, + BlockAccessList = block.BlockAccessList!, }; executionPayload.SetTransactions(block.Transactions); return executionPayload; @@ -168,9 +169,10 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) TotalDifficulty = totalDifficulty, TxRoot = TxTrie.CalculateRoot(transactions.Transactions), WithdrawalsRoot = BuildWithdrawalsRoot(), + BlockAccessListHash = new Hash256(BlockAccessList) //valuehash }; - return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals)); + return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, BlockAccessList)); } protected virtual Hash256? BuildWithdrawalsRoot() diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 549792456cb..5167e75cbc4 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -57,6 +57,7 @@ internal class StateProvider private readonly bool _populatePreBlockCache; private bool _needsStateRootUpdate; + // get from block access list public StateProvider(IScopedTrieStore? trieStore, IKeyValueStoreWithBatching codeDb, ILogManager logManager, From 4cd315bf0a904a45ba83217a22f2e310ab54556f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:02:49 +0100 Subject: [PATCH 013/215] update structures --- .../BlockAccessListTests.cs | 2 +- .../Tracing/BlockAccessTracer.cs | 9 ++++---- .../Nethermind.Core/BlockAccessList.cs | 21 ++++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 8cd90d36997..4e33f9b5f2c 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -51,7 +51,7 @@ public void Balance_and_nonce_changes() blockReceiptsTracer.SetOtherTracer(accessTracer); Execute(tx, block, blockReceiptsTracer); - Dictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; + SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; Assert.That(accountChanges, Has.Count.EqualTo(3)); List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 7f1174ecbc9..e728f9d58fc 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -72,8 +72,7 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after { BalanceChange balanceChange = new() { - TxIndex = (ushort)_currentIndex, - // PostBalance = (ulong)after // why not 256 bit? + BlockAccessIndex = (ushort)_currentIndex, PostBalance = after!.Value }; @@ -96,7 +95,7 @@ public void ReportCodeChange(Address address, byte[] before, byte[] after) { CodeChange codeChange = new() { - TxIndex = (ushort)_currentIndex, + BlockAccessIndex = (ushort)_currentIndex, NewCode = after }; @@ -113,7 +112,7 @@ public void ReportNonceChange(Address address, UInt256? before, UInt256? after) { NonceChange nonceChange = new() { - TxIndex = (ushort)_currentIndex, + BlockAccessIndex = (ushort)_currentIndex, NewNonce = (ulong)after }; @@ -138,7 +137,7 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ { StorageChange storageChange = new() { - TxIndex = (ushort)_currentIndex, + BlockAccessIndex = (ushort)_currentIndex, NewValue = after }; Address address = Address.Zero; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index 1db7a375963..ff6af62b787 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -6,38 +6,39 @@ namespace Nethermind.Core.BlockAccessLists; -// Single storage write: tx_index -> new_value +// # StorageChange: [block_access_index, new_value] public struct StorageChange { - public ushort TxIndex { get; set; } + public ushort BlockAccessIndex { get; set; } // [SszVector(32)] public byte[] NewValue { get; set; } } -// Single balance change: tx_index -> post_balance +// BalanceChange: [block_access_index, post_balance] public struct BalanceChange { - public ushort TxIndex { get; set; } - // actually UInt128 + public ushort BlockAccessIndex { get; set; } public UInt256 PostBalance { get; set; } } -// Single nonce change: tx_index -> new_nonce +// NonceChange: [block_access_index, new_nonce] public struct NonceChange { - public ushort TxIndex { get; set; } + public ushort BlockAccessIndex { get; set; } public ulong NewNonce { get; set; } } -// Single code change: tx_index -> new_code +// CodeChange: [block_access_index, new_code] public struct CodeChange { - public ushort TxIndex { get; set; } + public ushort BlockAccessIndex { get; set; } // [SszList(Eip7928Constants.MaxCodeSize)] public byte[] NewCode { get; set; } } +// SlotChanges: [slot, [changes]] +// All changes to a single storage slot public struct SlotChanges { // [SszVector(32)] @@ -82,7 +83,7 @@ public struct AccountChanges(Address address) public struct BlockAccessList() { // [SszList(Eip7928Constants.MaxAccounts)] - public Dictionary AccountChanges { get; set; } = []; + public SortedDictionary AccountChanges { get; set; } = []; // RLP encode bal public byte[] Bytes => []; From 6f9aabf54c9fe41afcaf21662ae45b0957785a3d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:48:14 +0100 Subject: [PATCH 014/215] only record last change of each tx --- .../Tracing/BlockAccessTracer.cs | 87 ++++++++++++++----- .../Nethermind.Core/BlockAccessList.cs | 13 +-- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index e728f9d58fc..ffbdbb9fd7f 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; @@ -57,11 +58,6 @@ public void SetOperationMemorySize(ulong newSize) {} public void ReportMemoryChange(long offset, in ReadOnlySpan data) {} - public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) - { - //_bal.AccountChanges[].StorageChanges() - } - public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) {} public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) {} @@ -72,7 +68,7 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after { BalanceChange balanceChange = new() { - BlockAccessIndex = (ushort)_currentIndex, + BlockAccessIndex = _blockAccessIndex, PostBalance = after!.Value }; @@ -85,17 +81,24 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after // show should refunds be handled? // don't add zero balance transfers, but add empty account changes - if ((before ?? 0) != after) + if ((before ?? 0) == after) + { + return; + } + + List balanceChanges = accountChanges.BalanceChanges; + if (balanceChanges is not [] && balanceChanges[^1].BlockAccessIndex == _blockAccessIndex) { - accountChanges.BalanceChanges.Add(balanceChange); + balanceChanges.RemoveAt(balanceChanges.Count - 1); } + balanceChanges.Add(balanceChange); } public void ReportCodeChange(Address address, byte[] before, byte[] after) { CodeChange codeChange = new() { - BlockAccessIndex = (ushort)_currentIndex, + BlockAccessIndex = _blockAccessIndex, NewCode = after }; @@ -105,14 +108,19 @@ public void ReportCodeChange(Address address, byte[] before, byte[] after) _bal.AccountChanges.Add(address, accountChanges); } - accountChanges.CodeChanges.Add(codeChange); + List codeChanges = accountChanges.CodeChanges; + if (codeChanges is not [] && codeChanges[^1].BlockAccessIndex == _blockAccessIndex) + { + codeChanges.RemoveAt(codeChanges.Count - 1); + } + codeChanges.Add(codeChange); } public void ReportNonceChange(Address address, UInt256? before, UInt256? after) { NonceChange nonceChange = new() { - BlockAccessIndex = (ushort)_currentIndex, + BlockAccessIndex = _blockAccessIndex, NewNonce = (ulong)after }; @@ -122,7 +130,12 @@ public void ReportNonceChange(Address address, UInt256? before, UInt256? after) _bal.AccountChanges.Add(address, accountChanges); } - accountChanges.NonceChanges.Add(nonceChange); + List nonceChanges = accountChanges.NonceChanges; + if (nonceChanges is not [] && nonceChanges[^1].BlockAccessIndex == _blockAccessIndex) + { + nonceChanges.RemoveAt(nonceChanges.Count - 1); + } + nonceChanges.Add(nonceChange); } public void ReportAccountRead(Address address) @@ -133,13 +146,22 @@ public void ReportAccountRead(Address address) } } - public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) + public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) { - StorageChange storageChange = new() + // get address? + Address address = Address.Zero; + + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) { - BlockAccessIndex = (ushort)_currentIndex, - NewValue = after - }; + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + StorageChange(accountChanges, key, value); + } + + public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) + { Address address = Address.Zero; if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) @@ -148,7 +170,30 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ _bal.AccountChanges.Add(address, accountChanges); } - accountChanges.StorageChanges.Add(storageChange); + if (!Enumerable.SequenceEqual(before, after)) + { + StorageChange(accountChanges, storageCell.Hash.BytesAsSpan, after.AsSpan()); + } + } + + private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) + { + StorageChange storageChange = new() + { + BlockAccessIndex = _blockAccessIndex, + NewValue = value.ToArray() + }; + + StorageKey storageKey = new(key); + if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) + { + storageChanges = new(); + } + else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) + { + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + } + storageChanges.Changes.Add(storageChange); } public void ReportStorageRead(in StorageCell storageCell) @@ -202,7 +247,7 @@ public void SetOperationMemory(TraceMemory memoryTrace) {} public void ReportFees(UInt256 fees, UInt256 burntFees) {} // private ITxTracer _currentTxTracer = NullTxTracer.Instance; - protected int _currentIndex { get; private set; } + private ushort _blockAccessIndex = 1; // private readonly List _txReceipts = new(); private BlockAccessList _bal = new(); protected Transaction? CurrentTx; @@ -231,7 +276,7 @@ public void ReportReward(Address author, string rewardType, UInt256 rewardValue) public void StartNewBlockTrace(Block block) { Block = block; - _currentIndex = 0; + _blockAccessIndex = 1; _bal = new(); } @@ -242,7 +287,7 @@ public ITxTracer StartNewTxTrace(Transaction? tx) public void EndTxTrace() { - _currentIndex++; + _blockAccessIndex++; } public void EndBlockTrace() diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index ff6af62b787..0bd42a06b3d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using Nethermind.Int256; @@ -39,19 +40,19 @@ public struct CodeChange // SlotChanges: [slot, [changes]] // All changes to a single storage slot -public struct SlotChanges +public struct SlotChanges() { // [SszVector(32)] - public byte[] Slot { get; set; } + // public byte[] Slot { get; set; } // [SszList(Eip7928Constants.MaxTxs)] - public ulong Changes { get; set; } + public List Changes { get; set; } = []; } -public struct StorageKey +public struct StorageKey(ReadOnlySpan key) { // [SszVector(32)] - public byte[] Key { get; set; } + public byte[] Key { get; set; } = key.ToArray(); } public struct AccountChanges(Address address) @@ -61,7 +62,7 @@ public struct AccountChanges(Address address) // Storage changes (slot -> [tx_index -> new_value]) // [SszList(Eip7928Constants.MaxSlots)] - public List StorageChanges { get; set; } = []; + public SortedDictionary StorageChanges { get; set; } = []; // Read-only storage keys // [SszList(Eip7928Constants.MaxSlots)] From d884b36db1c299127a2a426fc157b0052056ac62 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:54:52 +0100 Subject: [PATCH 015/215] fix test --- .../BlockAccessListTests.cs | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 4e33f9b5f2c..5a94de1ee71 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -56,25 +56,23 @@ public void Balance_and_nonce_changes() List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - // refund? + List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + using (Assert.EnterMultipleScope()) { - Assert.That(senderBalanceChanges, Has.Count.EqualTo(2)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * (ulong)gasLimit)); - Assert.That(senderBalanceChanges[1].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - } - - // zero balance change should not be recorded - List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - Assert.That(toBalanceChanges, Is.Empty); + // zero balance change should not be recorded + Assert.That(toBalanceChanges, Is.Empty); - List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + } } } } From 0eb0736c1705b1bbcd08ef07e072d0d1674005dc Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:06:40 +0100 Subject: [PATCH 016/215] change storage tracing --- .../Tracing/BlockAccessTracer.cs | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index ffbdbb9fd7f..ee791fee743 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -58,7 +58,19 @@ public void SetOperationMemorySize(ulong newSize) {} public void ReportMemoryChange(long offset, in ReadOnlySpan data) {} - public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) {} + public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) + { + if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _bal.AccountChanges.Add(address, accountChanges); + } + + if (currentValue != newValue) + { + StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, newValue); + } + } public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) {} @@ -148,21 +160,12 @@ public void ReportAccountRead(Address address) public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) { - // get address? - Address address = Address.Zero; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - StorageChange(accountChanges, key, value); + // no address } public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) { - Address address = Address.Zero; + Address address = storageCell.Address; if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) { @@ -176,26 +179,6 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ } } - private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) - { - StorageChange storageChange = new() - { - BlockAccessIndex = _blockAccessIndex, - NewValue = value.ToArray() - }; - - StorageKey storageKey = new(key); - if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) - { - storageChanges = new(); - } - else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) - { - storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); - } - storageChanges.Changes.Add(storageChange); - } - public void ReportStorageRead(in StorageCell storageCell) { StorageKey storageKey = new() @@ -314,4 +297,25 @@ public void Dispose() { // _currentTxTracer.Dispose(); } + + private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) + { + StorageChange storageChange = new() + { + BlockAccessIndex = _blockAccessIndex, + NewValue = value.ToArray() + }; + + StorageKey storageKey = new(key); + if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) + { + storageChanges = new(); + } + else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) + { + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + } + storageChanges.Changes.Add(storageChange); + } + } From 603d56a163c40bffedb4076a73043c55ee3a084f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 14:50:28 +0100 Subject: [PATCH 017/215] RLP encode bal --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 3 ++- src/Nethermind/Nethermind.Core/BlockAccessList.cs | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index f41d34de2e2..a5aeb87cbd3 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -24,6 +24,7 @@ using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Serialization.Rlp; using Nethermind.Specs.Forks; using Nethermind.State; using static Nethermind.Consensus.Processing.IBlockProcessor; @@ -156,7 +157,7 @@ protected virtual TxReceipt[] ProcessBlock( } header.Hash = header.CalculateHash(); - body.BlockAccessList = bal.Bytes; + body.BlockAccessList = Rlp.Encode(bal).Bytes; return receipts; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index 0bd42a06b3d..8d3e53442bd 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -85,7 +85,4 @@ public struct BlockAccessList() { // [SszList(Eip7928Constants.MaxAccounts)] public SortedDictionary AccountChanges { get; set; } = []; - - // RLP encode bal - public byte[] Bytes => []; } From 1726b9b343149cd8871fbb982dedd76eb15ed3ef Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:13:47 +0100 Subject: [PATCH 018/215] prewarm addresses --- .../Processing/BlockCachePreWarmer.cs | 85 +++++++++++++------ src/Nethermind/Nethermind.Core/Block.cs | 1 + 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 951b704f84b..0bab7208dac 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -21,6 +21,8 @@ using Nethermind.Core.Collections; using Nethermind.State; using Nethermind.Trie; +using Nethermind.Serialization.Rlp; +using Nethermind.Core.BlockAccessLists; namespace Nethermind.Consensus.Processing; @@ -276,7 +278,7 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block) { if (SystemTxAccessLists is not null) { - var env = envPool.Get(); + IReadOnlyTxProcessorSource env = envPool.Get(); try { using IReadOnlyTxProcessingScope scope = env.Build(parent); @@ -293,56 +295,82 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block) } } - AddressWarmingState baseState = new(envPool, block, parent); - - ParallelUnbalancedWork.For( - 0, - block.Transactions.Length, - parallelOptions, - baseState.InitThreadState, - static (i, state) => + WarmupTransactionAddresses(envPool, block.Transactions, true); + if (block.BlockAccessList is not null) { - Transaction tx = state.Block.Transactions[i]; - Address? sender = tx.SenderAddress; - + block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); + IReadOnlyTxProcessorSource env = envPool.Get(); try { - if (sender is not null) - { - state.Scope.WorldState.WarmUp(sender); - } + using IReadOnlyTxProcessingScope scope = env.Build(parent); - Address to = tx.To; - if (to is not null) + foreach (Address address in block.DecodedBlockAccessList.Value.AccountChanges.Keys) { - state.Scope.WorldState.WarmUp(to); + scope.WorldState.WarmUp(address); } + + // warmup storage locations } - catch (MissingTrieNodeException) + finally { + envPool.Return(env); } - - return state; - }, - AddressWarmingState.FinallyAction); + } } catch (OperationCanceledException) { // Ignore, block completed cancel } } + + private void WarmupTransactionAddresses(ObjectPool envPool, Transaction[] transactions, bool warmToAddress) + { + AddressWarmingState baseState = new(envPool, transactions, parent, warmToAddress); + + ParallelUnbalancedWork.For( + 0, + transactions.Length, + parallelOptions, + baseState.InitThreadState, + static (i, state) => + { + Transaction tx = state.Transactions[i]; + Address? sender = tx.SenderAddress; + + try + { + if (sender is not null) + { + state.Scope.WorldState.WarmUp(sender); + } + + Address? to = state.WarmToAddress ? null : tx.To; + if (to is not null) + { + state.Scope.WorldState.WarmUp(to); + } + } + catch (MissingTrieNodeException) + { + } + + return state; + }, + AddressWarmingState.FinallyAction); + } } - private readonly struct AddressWarmingState(ObjectPool envPool, Block block, BlockHeader parent) : IDisposable + private readonly struct AddressWarmingState(ObjectPool envPool, Transaction[] transactions, BlockHeader parent, bool warmToAddress) : IDisposable { public static Action FinallyAction { get; } = DisposeThreadState; public readonly ObjectPool EnvPool = envPool; - public readonly Block Block = block; + public readonly Transaction[] Transactions = transactions; public readonly IReadOnlyTxProcessorSource? Env; public readonly IReadOnlyTxProcessingScope? Scope; + public readonly bool WarmToAddress = warmToAddress; - public AddressWarmingState(ObjectPool envPool, Block block, BlockHeader parent, IReadOnlyTxProcessorSource env, IReadOnlyTxProcessingScope scope) : this(envPool, block, parent) + public AddressWarmingState(ObjectPool envPool, Transaction[] transactions, BlockHeader parent, IReadOnlyTxProcessorSource env, IReadOnlyTxProcessingScope scope, bool warmToAddress) : this(envPool, transactions, parent, warmToAddress) { Env = env; Scope = scope; @@ -351,7 +379,8 @@ public AddressWarmingState(ObjectPool envPool, Block public AddressWarmingState InitThreadState() { IReadOnlyTxProcessorSource env = EnvPool.Get(); - return new(EnvPool, Block, parent, env, scope: env.Build(parent)); + IReadOnlyTxProcessingScope scope = env.Build(parent); + return new(EnvPool, Transactions, parent, env, scope, WarmToAddress); } public void Dispose() diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index a064acc71b2..7ee9f96a136 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -149,6 +149,7 @@ public Transaction[] Transactions _ => ToShortHashAndNumber() }; + public BlockAccessList? DecodedBlockAccessList; private string ExtraDataToString() { if (ExtraData is null) From 8ac6add81a8c891af64b05b26f9fb47f030de25a Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:55:21 +0100 Subject: [PATCH 019/215] use bal as state provider --- ...sor.BlockValidationTransactionsExecutor.cs | 8 +- src/Nethermind/Nethermind.Core/Block.cs | 5 +- .../Nethermind.State/BlockAccessWorldState.cs | 230 ++++++++++++++++++ 3 files changed, 238 insertions(+), 5 deletions(-) create mode 100644 src/Nethermind/Nethermind.State/BlockAccessWorldState.cs diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index 7293bb5240b..d749f666498 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using Nethermind.Blockchain; using Nethermind.Blockchain.Tracing; @@ -12,7 +11,7 @@ using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Evm.TransactionProcessing; - +using Nethermind.State; using Metrics = Nethermind.Evm.Metrics; namespace Nethermind.Consensus.Processing @@ -41,18 +40,19 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing Transaction currentTx = block.Transactions[i]; ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); } - return receiptsTracer.TxReceipts.ToArray(); + return [.. receiptsTracer.TxReceipts]; } protected virtual void ProcessTransaction(Block block, Transaction currentTx, int index, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions) { + BlockAccessWorldState blockAccessStateProvider = new(block.DecodedBlockAccessList!.Value, (ushort)(index + 1), stateProvider); TransactionResult result = transactionProcessor.ProcessTransaction(currentTx, receiptsTracer, processingOptions, stateProvider); if (!result) ThrowInvalidBlockException(result, block.Header, currentTx, index); TransactionProcessed?.Invoke(this, new TxProcessedEventArgs(index, currentTx, receiptsTracer.TxReceipts[index])); } [DoesNotReturn, StackTraceHidden] - private void ThrowInvalidBlockException(TransactionResult result, BlockHeader header, Transaction currentTx, int index) + private static void ThrowInvalidBlockException(TransactionResult result, BlockHeader header, Transaction currentTx, int index) { throw new InvalidBlockException(header, $"Transaction {currentTx.Hash} at index {index} failed with error {result.Error}"); } diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 7ee9f96a136..c9dde1df74c 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -131,6 +131,10 @@ public Transaction[] Transactions public int? EncodedSize { get; set; } + [JsonIgnore] + public BlockAccessList? DecodedBlockAccessList { get; set; } + + [JsonIgnore] internal volatile int TransactionProcessed; @@ -149,7 +153,6 @@ public Transaction[] Transactions _ => ToShortHashAndNumber() }; - public BlockAccessList? DecodedBlockAccessList; private string ExtraDataToString() { if (ExtraData is null) diff --git a/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs b/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs new file mode 100644 index 00000000000..bfb452692d1 --- /dev/null +++ b/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs @@ -0,0 +1,230 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Specs; +using Nethermind.Evm.State; +using Nethermind.Evm.Tracing.State; +using Nethermind.Int256; + +namespace Nethermind.State; + +public class BlockAccessWorldState(BlockAccessList blockAccessList, ushort blockAccessIndex, IWorldState innerWorldState) : IWorldState +{ + public Hash256 StateRoot => throw new NotImplementedException(); + + public bool AccountExists(Address address) + => innerWorldState.AccountExists(address); + + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + throw new NotImplementedException(); + } + + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + throw new NotImplementedException(); + } + + public void ClearStorage(Address address) + { + throw new NotImplementedException(); + } + + public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) + { + throw new NotImplementedException(); + } + + public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) + { + throw new NotImplementedException(); + } + + public void CommitTree(long blockNumber) + { + throw new NotImplementedException(); + } + + public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) + { + throw new NotImplementedException(); + } + + public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) + { + throw new NotImplementedException(); + } + + public void DecrementNonce(Address address, UInt256 delta) + { + throw new NotImplementedException(); + } + + public void DeleteAccount(Address address) + { + throw new NotImplementedException(); + } + + public ReadOnlySpan Get(in StorageCell storageCell) + { + throw new NotImplementedException(); + } + + public ArrayPoolList? GetAccountChanges() + { + throw new NotImplementedException(); + } + + public ref readonly UInt256 GetBalance(Address address) + { + throw new NotImplementedException(); + } + + public byte[]? GetCode(Address address) + { + if (!blockAccessList.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + return innerWorldState.GetCode(address); + } + + List codeChanges = accountChanges.CodeChanges; + CodeChange? lastChange = null; + foreach (CodeChange codeChange in codeChanges) + { + if (codeChange.BlockAccessIndex >= blockAccessIndex) + { + break; + } + lastChange = codeChange; + } + + if (lastChange is null) + { + return innerWorldState.GetCode(address); + } + + return lastChange.Value.NewCode; + } + + public byte[]? GetCode(in ValueHash256 codeHash) + { + throw new NotImplementedException(); + } + + public ref readonly ValueHash256 GetCodeHash(Address address) + { + throw new NotImplementedException(); + } + + public byte[] GetOriginal(in StorageCell storageCell) + { + throw new NotImplementedException(); + } + + public ReadOnlySpan GetTransientState(in StorageCell storageCell) + { + throw new NotImplementedException(); + } + + public bool HasStateForBlock(BlockHeader? baseBlock) + { + throw new NotImplementedException(); + } + + public void IncrementNonce(Address address, UInt256 delta) + { + throw new NotImplementedException(); + } + + public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + { + throw new NotImplementedException(); + } + + public bool IsContract(Address address) + { + throw new NotImplementedException(); + } + + public bool IsDeadAccount(Address address) + { + throw new NotImplementedException(); + } + + public void RecalculateStateRoot() + { + throw new NotImplementedException(); + } + + public void Reset(bool resetBlockChanges = true) + { + throw new NotImplementedException(); + } + + public void ResetTransient() + { + throw new NotImplementedException(); + } + + public void Restore(Snapshot snapshot) + { + throw new NotImplementedException(); + } + + public void Set(in StorageCell storageCell, byte[] newValue) + { + throw new NotImplementedException(); + } + + public void SetBaseBlock(BlockHeader? header) + { + throw new NotImplementedException(); + } + + public void SetNonce(Address address, in UInt256 nonce) + { + throw new NotImplementedException(); + } + + public void SetTransientState(in StorageCell storageCell, byte[] newValue) + { + throw new NotImplementedException(); + } + + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + throw new NotImplementedException(); + } + + public Snapshot TakeSnapshot(bool newTransactionStart = false) + { + throw new NotImplementedException(); + } + + public bool TryGetAccount(Address address, out AccountStruct account) + { + throw new NotImplementedException(); + } + + public void UpdateStorageRoot(Address address, Hash256 storageRoot) + { + throw new NotImplementedException(); + } + + public void WarmUp(AccessList? accessList) + { + throw new NotImplementedException(); + } + + public void WarmUp(Address address) + { + throw new NotImplementedException(); + } +} \ No newline at end of file From 190f2e48cbdde3d04c449d8fdfcf4143913d4f75 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 21 Aug 2025 01:03:35 +0100 Subject: [PATCH 020/215] reset changes using BAL --- .../Processing/BlockCachePreWarmer.cs | 85 +++---- ...sor.BlockValidationTransactionsExecutor.cs | 2 - .../Processing/BlockProcessor.cs | 9 +- .../Nethermind.State/BlockAccessWorldState.cs | 230 ------------------ .../Nethermind.State/StateProvider.cs | 1 - 5 files changed, 36 insertions(+), 291 deletions(-) delete mode 100644 src/Nethermind/Nethermind.State/BlockAccessWorldState.cs diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs index 0bab7208dac..951b704f84b 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockCachePreWarmer.cs @@ -21,8 +21,6 @@ using Nethermind.Core.Collections; using Nethermind.State; using Nethermind.Trie; -using Nethermind.Serialization.Rlp; -using Nethermind.Core.BlockAccessLists; namespace Nethermind.Consensus.Processing; @@ -278,7 +276,7 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block) { if (SystemTxAccessLists is not null) { - IReadOnlyTxProcessorSource env = envPool.Get(); + var env = envPool.Get(); try { using IReadOnlyTxProcessingScope scope = env.Build(parent); @@ -295,82 +293,56 @@ private void WarmupAddresses(ParallelOptions parallelOptions, Block block) } } - WarmupTransactionAddresses(envPool, block.Transactions, true); - if (block.BlockAccessList is not null) + AddressWarmingState baseState = new(envPool, block, parent); + + ParallelUnbalancedWork.For( + 0, + block.Transactions.Length, + parallelOptions, + baseState.InitThreadState, + static (i, state) => { - block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); - IReadOnlyTxProcessorSource env = envPool.Get(); + Transaction tx = state.Block.Transactions[i]; + Address? sender = tx.SenderAddress; + try { - using IReadOnlyTxProcessingScope scope = env.Build(parent); - - foreach (Address address in block.DecodedBlockAccessList.Value.AccountChanges.Keys) + if (sender is not null) { - scope.WorldState.WarmUp(address); + state.Scope.WorldState.WarmUp(sender); } - // warmup storage locations + Address to = tx.To; + if (to is not null) + { + state.Scope.WorldState.WarmUp(to); + } } - finally + catch (MissingTrieNodeException) { - envPool.Return(env); } - } + + return state; + }, + AddressWarmingState.FinallyAction); } catch (OperationCanceledException) { // Ignore, block completed cancel } } - - private void WarmupTransactionAddresses(ObjectPool envPool, Transaction[] transactions, bool warmToAddress) - { - AddressWarmingState baseState = new(envPool, transactions, parent, warmToAddress); - - ParallelUnbalancedWork.For( - 0, - transactions.Length, - parallelOptions, - baseState.InitThreadState, - static (i, state) => - { - Transaction tx = state.Transactions[i]; - Address? sender = tx.SenderAddress; - - try - { - if (sender is not null) - { - state.Scope.WorldState.WarmUp(sender); - } - - Address? to = state.WarmToAddress ? null : tx.To; - if (to is not null) - { - state.Scope.WorldState.WarmUp(to); - } - } - catch (MissingTrieNodeException) - { - } - - return state; - }, - AddressWarmingState.FinallyAction); - } } - private readonly struct AddressWarmingState(ObjectPool envPool, Transaction[] transactions, BlockHeader parent, bool warmToAddress) : IDisposable + private readonly struct AddressWarmingState(ObjectPool envPool, Block block, BlockHeader parent) : IDisposable { public static Action FinallyAction { get; } = DisposeThreadState; public readonly ObjectPool EnvPool = envPool; - public readonly Transaction[] Transactions = transactions; + public readonly Block Block = block; public readonly IReadOnlyTxProcessorSource? Env; public readonly IReadOnlyTxProcessingScope? Scope; - public readonly bool WarmToAddress = warmToAddress; - public AddressWarmingState(ObjectPool envPool, Transaction[] transactions, BlockHeader parent, IReadOnlyTxProcessorSource env, IReadOnlyTxProcessingScope scope, bool warmToAddress) : this(envPool, transactions, parent, warmToAddress) + public AddressWarmingState(ObjectPool envPool, Block block, BlockHeader parent, IReadOnlyTxProcessorSource env, IReadOnlyTxProcessingScope scope) : this(envPool, block, parent) { Env = env; Scope = scope; @@ -379,8 +351,7 @@ public AddressWarmingState(ObjectPool envPool, Trans public AddressWarmingState InitThreadState() { IReadOnlyTxProcessorSource env = EnvPool.Get(); - IReadOnlyTxProcessingScope scope = env.Build(parent); - return new(EnvPool, Transactions, parent, env, scope, WarmToAddress); + return new(EnvPool, Block, parent, env, scope: env.Build(parent)); } public void Dispose() diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index d749f666498..3a107f65749 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -11,7 +11,6 @@ using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Evm.TransactionProcessing; -using Nethermind.State; using Metrics = Nethermind.Evm.Metrics; namespace Nethermind.Consensus.Processing @@ -45,7 +44,6 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing protected virtual void ProcessTransaction(Block block, Transaction currentTx, int index, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions) { - BlockAccessWorldState blockAccessStateProvider = new(block.DecodedBlockAccessList!.Value, (ushort)(index + 1), stateProvider); TransactionResult result = transactionProcessor.ProcessTransaction(currentTx, receiptsTracer, processingOptions, stateProvider); if (!result) ThrowInvalidBlockException(result, block.Header, currentTx, index); TransactionProcessed?.Invoke(this, new TxProcessedEventArgs(index, currentTx, receiptsTracer.TxReceipts[index])); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index a5aeb87cbd3..ecdc1da7bae 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -233,7 +233,14 @@ private Block PrepareBlockForProcessing(Block suggestedBlock) headerForProcessing.StateRoot = bh.StateRoot; } - return suggestedBlock.WithReplacedHeader(headerForProcessing); + Block block = suggestedBlock.WithReplacedHeader(headerForProcessing); + + if (block.BlockAccessList is not null) + { + block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); + } + + return block; } private void ApplyMinerRewards(Block block, IBlockTracer tracer, IReleaseSpec spec) diff --git a/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs b/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs deleted file mode 100644 index bfb452692d1..00000000000 --- a/src/Nethermind/Nethermind.State/BlockAccessWorldState.cs +++ /dev/null @@ -1,230 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Core; -using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Eip2930; -using Nethermind.Core.Specs; -using Nethermind.Evm.State; -using Nethermind.Evm.Tracing.State; -using Nethermind.Int256; - -namespace Nethermind.State; - -public class BlockAccessWorldState(BlockAccessList blockAccessList, ushort blockAccessIndex, IWorldState innerWorldState) : IWorldState -{ - public Hash256 StateRoot => throw new NotImplementedException(); - - public bool AccountExists(Address address) - => innerWorldState.AccountExists(address); - - public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) - { - throw new NotImplementedException(); - } - - public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) - { - throw new NotImplementedException(); - } - - public void ClearStorage(Address address) - { - throw new NotImplementedException(); - } - - public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) - { - throw new NotImplementedException(); - } - - public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) - { - throw new NotImplementedException(); - } - - public void CommitTree(long blockNumber) - { - throw new NotImplementedException(); - } - - public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) - { - throw new NotImplementedException(); - } - - public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) - { - throw new NotImplementedException(); - } - - public void DecrementNonce(Address address, UInt256 delta) - { - throw new NotImplementedException(); - } - - public void DeleteAccount(Address address) - { - throw new NotImplementedException(); - } - - public ReadOnlySpan Get(in StorageCell storageCell) - { - throw new NotImplementedException(); - } - - public ArrayPoolList? GetAccountChanges() - { - throw new NotImplementedException(); - } - - public ref readonly UInt256 GetBalance(Address address) - { - throw new NotImplementedException(); - } - - public byte[]? GetCode(Address address) - { - if (!blockAccessList.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - return innerWorldState.GetCode(address); - } - - List codeChanges = accountChanges.CodeChanges; - CodeChange? lastChange = null; - foreach (CodeChange codeChange in codeChanges) - { - if (codeChange.BlockAccessIndex >= blockAccessIndex) - { - break; - } - lastChange = codeChange; - } - - if (lastChange is null) - { - return innerWorldState.GetCode(address); - } - - return lastChange.Value.NewCode; - } - - public byte[]? GetCode(in ValueHash256 codeHash) - { - throw new NotImplementedException(); - } - - public ref readonly ValueHash256 GetCodeHash(Address address) - { - throw new NotImplementedException(); - } - - public byte[] GetOriginal(in StorageCell storageCell) - { - throw new NotImplementedException(); - } - - public ReadOnlySpan GetTransientState(in StorageCell storageCell) - { - throw new NotImplementedException(); - } - - public bool HasStateForBlock(BlockHeader? baseBlock) - { - throw new NotImplementedException(); - } - - public void IncrementNonce(Address address, UInt256 delta) - { - throw new NotImplementedException(); - } - - public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) - { - throw new NotImplementedException(); - } - - public bool IsContract(Address address) - { - throw new NotImplementedException(); - } - - public bool IsDeadAccount(Address address) - { - throw new NotImplementedException(); - } - - public void RecalculateStateRoot() - { - throw new NotImplementedException(); - } - - public void Reset(bool resetBlockChanges = true) - { - throw new NotImplementedException(); - } - - public void ResetTransient() - { - throw new NotImplementedException(); - } - - public void Restore(Snapshot snapshot) - { - throw new NotImplementedException(); - } - - public void Set(in StorageCell storageCell, byte[] newValue) - { - throw new NotImplementedException(); - } - - public void SetBaseBlock(BlockHeader? header) - { - throw new NotImplementedException(); - } - - public void SetNonce(Address address, in UInt256 nonce) - { - throw new NotImplementedException(); - } - - public void SetTransientState(in StorageCell storageCell, byte[] newValue) - { - throw new NotImplementedException(); - } - - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) - { - throw new NotImplementedException(); - } - - public Snapshot TakeSnapshot(bool newTransactionStart = false) - { - throw new NotImplementedException(); - } - - public bool TryGetAccount(Address address, out AccountStruct account) - { - throw new NotImplementedException(); - } - - public void UpdateStorageRoot(Address address, Hash256 storageRoot) - { - throw new NotImplementedException(); - } - - public void WarmUp(AccessList? accessList) - { - throw new NotImplementedException(); - } - - public void WarmUp(Address address) - { - throw new NotImplementedException(); - } -} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 5167e75cbc4..549792456cb 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -57,7 +57,6 @@ internal class StateProvider private readonly bool _populatePreBlockCache; private bool _needsStateRootUpdate; - // get from block access list public StateProvider(IScopedTrieStore? trieStore, IKeyValueStoreWithBatching codeDb, ILogManager logManager, From f09890c59e9f5391abc38a1bc7d01073ce037aa6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:04:59 +0100 Subject: [PATCH 021/215] only trace when EIP enabled --- .../Processing/BlockProcessor.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index ecdc1da7bae..3671c6a4606 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -104,10 +104,17 @@ protected virtual TxReceipt[] ProcessBlock( BlockBody body = block.Body; BlockHeader header = block.Header; - CompositeBlockTracer compositeBlockTracer = new(); - compositeBlockTracer.Add(blockTracer); - compositeBlockTracer.Add(BlockAccessTracer); - ReceiptsTracer.SetOtherTracer(compositeBlockTracer); + if (spec.BlockLevelAccessListsEnabled) + { + CompositeBlockTracer compositeBlockTracer = new(); + compositeBlockTracer.Add(blockTracer); + compositeBlockTracer.Add(BlockAccessTracer); + ReceiptsTracer.SetOtherTracer(compositeBlockTracer); + } + else + { + ReceiptsTracer.SetOtherTracer(blockTracer); + } ReceiptsTracer.StartNewBlockTrace(block); blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); @@ -120,7 +127,7 @@ protected virtual TxReceipt[] ProcessBlock( _stateProvider.Commit(spec, commitRoots: false); - BlockAccessList bal = ReceiptsTracer.GetTracer().BlockAccessList; + BlockAccessList? bal = ReceiptsTracer.GetTracer()?.BlockAccessList; CalculateBlooms(receipts); @@ -131,6 +138,7 @@ protected virtual TxReceipt[] ProcessBlock( header.ReceiptsRoot = _receiptsRootCalculator.GetReceiptsRoot(receipts, spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); + // build BAL withdrawalProcessor.ProcessWithdrawals(block, spec); // We need to do a commit here as in _executionRequestsProcessor while executing system transactions @@ -157,7 +165,11 @@ protected virtual TxReceipt[] ProcessBlock( } header.Hash = header.CalculateHash(); - body.BlockAccessList = Rlp.Encode(bal).Bytes; + + if (spec.BlockLevelAccessListsEnabled) + { + body.BlockAccessList = Rlp.Encode(bal).Bytes; + } return receipts; } From f93a43cbed8a3d9b4cc66a690fa24c0b1addebe9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:21:44 +0100 Subject: [PATCH 022/215] handle withdrawals --- .../Tracing/BlockAccessTracer.cs | 6 ++---- .../Processing/BlockProcessor.cs | 10 +++++----- .../Withdrawals/IWithdrawalProcessor.cs | 3 ++- .../Withdrawals/WithdrawalProcessor.cs | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index ee791fee743..f448574b43c 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -90,8 +90,6 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after _bal.AccountChanges.Add(address, accountChanges); } - // show should refunds be handled? - // don't add zero balance transfers, but add empty account changes if ((before ?? 0) == after) { @@ -230,7 +228,7 @@ public void SetOperationMemory(TraceMemory memoryTrace) {} public void ReportFees(UInt256 fees, UInt256 burntFees) {} // private ITxTracer _currentTxTracer = NullTxTracer.Instance; - private ushort _blockAccessIndex = 1; + private ushort _blockAccessIndex = 0; // private readonly List _txReceipts = new(); private BlockAccessList _bal = new(); protected Transaction? CurrentTx; @@ -259,7 +257,7 @@ public void ReportReward(Address author, string rewardType, UInt256 rewardValue) public void StartNewBlockTrace(Block block) { Block = block; - _blockAccessIndex = 1; + _blockAccessIndex = 0; _bal = new(); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 3671c6a4606..0ef15672e40 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -123,12 +123,13 @@ protected virtual TxReceipt[] ProcessBlock( blockHashStore.ApplyBlockhashStateChanges(header, spec); _stateProvider.Commit(spec, commitRoots: false); + // set access index to 1 since system contracts completed + BlockAccessTracer.EndTxTrace(); + TxReceipt[] receipts = blockTransactionsExecutor.ProcessTransactions(block, options, ReceiptsTracer, token); _stateProvider.Commit(spec, commitRoots: false); - BlockAccessList? bal = ReceiptsTracer.GetTracer()?.BlockAccessList; - CalculateBlooms(receipts); if (spec.IsEip4844Enabled) @@ -138,8 +139,7 @@ protected virtual TxReceipt[] ProcessBlock( header.ReceiptsRoot = _receiptsRootCalculator.GetReceiptsRoot(receipts, spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); - // build BAL - withdrawalProcessor.ProcessWithdrawals(block, spec); + withdrawalProcessor.ProcessWithdrawals(block, spec, BlockAccessTracer); // We need to do a commit here as in _executionRequestsProcessor while executing system transactions // we do WorldState.Commit(SystemTransactionReleaseSpec.Instance). In SystemTransactionReleaseSpec @@ -168,7 +168,7 @@ protected virtual TxReceipt[] ProcessBlock( if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = Rlp.Encode(bal).Bytes; + body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; } return receipts; diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs index 5909ba1f220..c2eb33848ff 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Blockchain.Tracing; using Nethermind.Core; using Nethermind.Core.Specs; @@ -8,5 +9,5 @@ namespace Nethermind.Consensus.Withdrawals; public interface IWithdrawalProcessor { - void ProcessWithdrawals(Block block, IReleaseSpec spec); + void ProcessWithdrawals(Block block, IReleaseSpec spec, BlockAccessTracer blockAccessTracer); } diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs index 3b526813f89..2582445a359 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs @@ -2,9 +2,11 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Nethermind.Blockchain.Tracing; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.State; +using Nethermind.Int256; using Nethermind.Logging; namespace Nethermind.Consensus.Withdrawals; @@ -22,7 +24,7 @@ public WithdrawalProcessor(IWorldState stateProvider, ILogManager logManager) _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); } - public void ProcessWithdrawals(Block block, IReleaseSpec spec) + public void ProcessWithdrawals(Block block, IReleaseSpec spec, BlockAccessTracer blockAccessTracer) { if (!spec.WithdrawalsEnabled) return; @@ -35,10 +37,18 @@ public void ProcessWithdrawals(Block block, IReleaseSpec spec) for (int i = 0; i < blockWithdrawals.Length; i++) { Withdrawal withdrawal = blockWithdrawals[i]; + Address address = withdrawal.Address; + UInt256 amount = withdrawal.AmountInWei; if (_logger.IsTrace) _logger.Trace($" {withdrawal.AmountInGwei} GWei to account {withdrawal.Address}"); // Consensus clients are using Gwei for withdrawals amount. We need to convert it to Wei before applying state changes https://github.com/ethereum/execution-apis/pull/354 - _stateProvider.AddToBalanceAndCreateIfNotExists(withdrawal.Address, withdrawal.AmountInWei, spec); + _stateProvider.AddToBalanceAndCreateIfNotExists(address, amount, spec); + + if (spec.BlockLevelAccessListsEnabled && amount != 0) + { + UInt256 newBalance = _stateProvider.GetBalance(address); + blockAccessTracer.ReportBalanceChange(address, null, newBalance); + } } } From 2bf2cefcc6457ac0c22a9fa897c68954aff76f63 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:33:29 +0100 Subject: [PATCH 023/215] handle system contracts --- .../Nethermind.Blockchain/Blocks/BlockhashStore.cs | 14 ++++++++++---- .../Blocks/IBlockhashStore.cs | 5 +++-- .../Processing/BlockProcessor.cs | 9 +++++---- .../Withdrawals/IWithdrawalProcessor.cs | 4 ++-- .../Withdrawals/WithdrawalProcessor.cs | 8 +++++--- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockhashStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockhashStore.cs index 00d7a44cda8..c8dbf66b1d3 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/BlockhashStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/BlockhashStore.cs @@ -8,6 +8,7 @@ using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; using Nethermind.Int256; [assembly: InternalsVisibleTo("Nethermind.Blockchain.Test")] @@ -19,10 +20,10 @@ public class BlockhashStore(ISpecProvider specProvider, IWorldState worldState) { private static readonly byte[] EmptyBytes = [0]; - public void ApplyBlockhashStateChanges(BlockHeader blockHeader) - => ApplyBlockhashStateChanges(blockHeader, specProvider.GetSpec(blockHeader)); + public void ApplyBlockhashStateChanges(BlockHeader blockHeader, ITxTracer? tracer = null) + => ApplyBlockhashStateChanges(blockHeader, specProvider.GetSpec(blockHeader), tracer); - public void ApplyBlockhashStateChanges(BlockHeader blockHeader, IReleaseSpec spec) + public void ApplyBlockhashStateChanges(BlockHeader blockHeader, IReleaseSpec spec, ITxTracer? tracer = null) { if (!spec.IsEip2935Enabled || blockHeader.IsGenesis || blockHeader.ParentHash is null) return; @@ -32,7 +33,12 @@ public void ApplyBlockhashStateChanges(BlockHeader blockHeader, IReleaseSpec spe Hash256 parentBlockHash = blockHeader.ParentHash; var parentBlockIndex = new UInt256((ulong)((blockHeader.Number - 1) % Eip2935Constants.RingBufferSize)); StorageCell blockHashStoreCell = new(eip2935Account, parentBlockIndex); - worldState.Set(blockHashStoreCell, parentBlockHash!.Bytes.WithoutLeadingZeros().ToArray()); + byte[] newValue = parentBlockHash!.Bytes.WithoutLeadingZeros().ToArray(); + worldState.Set(blockHashStoreCell, newValue); + if (tracer is not null && tracer.IsTracingStorage) + { + tracer.ReportStorageChange(blockHashStoreCell, null, newValue); + } } public Hash256? GetBlockHashFromState(BlockHeader currentHeader, long requiredBlockNumber) diff --git a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockhashStore.cs b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockhashStore.cs index 77cc6b214f5..035f1a543f3 100644 --- a/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockhashStore.cs +++ b/src/Nethermind/Nethermind.Blockchain/Blocks/IBlockhashStore.cs @@ -4,13 +4,14 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; +using Nethermind.Evm.Tracing; namespace Nethermind.Blockchain.Blocks; public interface IBlockhashStore { - public void ApplyBlockhashStateChanges(BlockHeader blockHeader); - public void ApplyBlockhashStateChanges(BlockHeader blockHeader, IReleaseSpec spec); + public void ApplyBlockhashStateChanges(BlockHeader blockHeader, ITxTracer? tracer = null); + public void ApplyBlockhashStateChanges(BlockHeader blockHeader, IReleaseSpec spec, ITxTracer? tracer = null); public Hash256? GetBlockHashFromState(BlockHeader currentBlockHeader, long requiredBlockNumber); public Hash256? GetBlockHashFromState(BlockHeader currentBlockHeader, long requiredBlockNumber, IReleaseSpec? spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 0ef15672e40..b59400afb8f 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -119,8 +119,8 @@ protected virtual TxReceipt[] ProcessBlock( blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); - StoreBeaconRoot(block, spec); - blockHashStore.ApplyBlockhashStateChanges(header, spec); + StoreBeaconRoot(block, spec, BlockAccessTracer); + blockHashStore.ApplyBlockhashStateChanges(header, spec, BlockAccessTracer); _stateProvider.Commit(spec, commitRoots: false); // set access index to 1 since system contracts completed @@ -189,11 +189,11 @@ private static void CalculateBlooms(TxReceipt[] receipts) }); } - private void StoreBeaconRoot(Block block, IReleaseSpec spec) + private void StoreBeaconRoot(Block block, IReleaseSpec spec, ITxTracer tracer) { try { - beaconBlockRootHandler.StoreBeaconRoot(block, spec, NullTxTracer.Instance); + beaconBlockRootHandler.StoreBeaconRoot(block, spec, tracer); } catch (Exception e) { @@ -287,6 +287,7 @@ private void ApplyMinerReward(Block block, BlockReward reward, IReleaseSpec spec if (_logger.IsTrace) _logger.Trace($" {(BigInteger)reward.Value / (BigInteger)Unit.Ether:N3}{Unit.EthSymbol} for account at {reward.Address}"); _stateProvider.AddToBalanceAndCreateIfNotExists(reward.Address, reward.Value, spec); + // tracer here? } private void ApplyDaoTransition(Block block) diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs index c2eb33848ff..3a42e111666 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Blockchain.Tracing; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.Withdrawals; public interface IWithdrawalProcessor { - void ProcessWithdrawals(Block block, IReleaseSpec spec, BlockAccessTracer blockAccessTracer); + void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null); } diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs index 2582445a359..1243f3e95fd 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs @@ -2,10 +2,12 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Diagnostics.CodeAnalysis; using Nethermind.Blockchain.Tracing; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; @@ -24,7 +26,7 @@ public WithdrawalProcessor(IWorldState stateProvider, ILogManager logManager) _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); } - public void ProcessWithdrawals(Block block, IReleaseSpec spec, BlockAccessTracer blockAccessTracer) + public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null) { if (!spec.WithdrawalsEnabled) return; @@ -44,10 +46,10 @@ public void ProcessWithdrawals(Block block, IReleaseSpec spec, BlockAccessTracer // Consensus clients are using Gwei for withdrawals amount. We need to convert it to Wei before applying state changes https://github.com/ethereum/execution-apis/pull/354 _stateProvider.AddToBalanceAndCreateIfNotExists(address, amount, spec); - if (spec.BlockLevelAccessListsEnabled && amount != 0) + if (tracer is not null && amount != 0) { UInt256 newBalance = _stateProvider.GetBalance(address); - blockAccessTracer.ReportBalanceChange(address, null, newBalance); + tracer.ReportBalanceChange(address, null, newBalance); } } } From ce008923c08823a17d76792fd97e24e42f690695 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:46:27 +0100 Subject: [PATCH 024/215] fix compile --- .../BlockProductionWithdrawalProcessor.cs | 12 +++++----- .../Withdrawals/NullWithdrawalProcessor.cs | 3 ++- .../Withdrawals/AuraWithdrawalProcessor.cs | 4 +++- .../OptimismWithdrawals.cs | 22 +++++++------------ 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs index c03f0702002..d05d4104c11 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs @@ -5,20 +5,18 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; +using Nethermind.Evm.Tracing; using Nethermind.State.Proofs; namespace Nethermind.Consensus.Withdrawals; -public class BlockProductionWithdrawalProcessor : IWithdrawalProcessor +public class BlockProductionWithdrawalProcessor(IWithdrawalProcessor processor) : IWithdrawalProcessor { - private readonly IWithdrawalProcessor _processor; + private readonly IWithdrawalProcessor _processor = processor ?? throw new ArgumentNullException(nameof(processor)); - public BlockProductionWithdrawalProcessor(IWithdrawalProcessor processor) => - _processor = processor ?? throw new ArgumentNullException(nameof(processor)); - - public void ProcessWithdrawals(Block block, IReleaseSpec spec) + public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null) { - _processor.ProcessWithdrawals(block, spec); + _processor.ProcessWithdrawals(block, spec, tracer); if (spec.WithdrawalsEnabled) { diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs index 24581691fcf..a768b1826bf 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs @@ -3,12 +3,13 @@ using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.Withdrawals; public class NullWithdrawalProcessor : IWithdrawalProcessor { - public void ProcessWithdrawals(Block block, IReleaseSpec spec) { } + public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) { } public static IWithdrawalProcessor Instance { get; } = new NullWithdrawalProcessor(); } diff --git a/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs index 85776645e9a..4cdcb468e58 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs @@ -9,6 +9,7 @@ using Nethermind.Core.Collections; using Nethermind.Core.Specs; using Nethermind.Evm; +using Nethermind.Evm.Tracing; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Merge.AuRa.Contracts; @@ -29,7 +30,7 @@ public AuraWithdrawalProcessor(IWithdrawalContract contract, ILogManager logMana _logger = logManager.GetClassLogger(); } - public void ProcessWithdrawals(Block block, IReleaseSpec spec) + public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) { if (!spec.WithdrawalsEnabled || block.Withdrawals is null) // The second check seems redundant return; @@ -50,6 +51,7 @@ public void ProcessWithdrawals(Block block, IReleaseSpec spec) if (_logger.IsTrace) _logger.Trace($" {(BigInteger)withdrawal.AmountInWei / (BigInteger)Unit.Ether:N3}GNO to account {withdrawal.Address}"); } + // todo: trace state changes try { _contract.ExecuteWithdrawals(block.Header, _failedWithdrawalsMaxCount, amounts, addresses); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs b/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs index 141d3ecc0d6..47b2c52db65 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs @@ -6,6 +6,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; using Nethermind.Logging; namespace Nethermind.Optimism; @@ -17,28 +18,21 @@ namespace Nethermind.Optimism; /// Constructed over the world state so that it can construct the proper withdrawals hash just before commitment. /// https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/isthmus/exec-engine.md#l2tol1messagepasser-storage-root-in-header /// -public class OptimismWithdrawalProcessor : IWithdrawalProcessor +public class OptimismWithdrawalProcessor(IWorldState state, ILogManager logManager, IOptimismSpecHelper specHelper) : IWithdrawalProcessor { - private readonly IWorldState _state; - private readonly IOptimismSpecHelper _specHelper; - private readonly ILogger _logger; + private readonly IWorldState _state = state; + private readonly IOptimismSpecHelper _specHelper = specHelper; + private readonly ILogger _logger = logManager.GetClassLogger(); - public OptimismWithdrawalProcessor(IWorldState state, ILogManager logManager, IOptimismSpecHelper specHelper) + public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) { - _state = state; - _specHelper = specHelper; - _logger = logManager.GetClassLogger(); - } - - public void ProcessWithdrawals(Block block, IReleaseSpec spec) - { - var header = block.Header; + BlockHeader header = block.Header; if (_specHelper.IsIsthmus(header)) { _state.Commit(spec, commitRoots: true); - if (_state.TryGetAccount(PreDeploys.L2ToL1MessagePasser, out var account)) + if (_state.TryGetAccount(PreDeploys.L2ToL1MessagePasser, out AccountStruct account)) { if (_logger.IsDebug) _logger.Debug($"Setting {nameof(BlockHeader.WithdrawalsRoot)} to {account.StorageRoot}"); From 9d1d9ffa958d74cb8ee4aafa7b5f8352ae3e55d9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 14:58:09 +0100 Subject: [PATCH 025/215] add block processing test --- .../BlockAccessListTests.cs | 81 +++++++++++++++++++ .../TransactionProcessorTests.cs | 2 +- .../Tracing/BlockAccessTracer.cs | 46 +++++------ .../Processing/BlockProcessor.cs | 3 +- .../OverridableReleaseSpec.cs | 2 +- 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 5a94de1ee71..c5cc18a455d 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -2,11 +2,27 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; +using System.Threading; +using Nethermind.Blockchain.BeaconBlockRoot; +using Nethermind.Blockchain.Blocks; +using Nethermind.Blockchain.Receipts; +using Nethermind.Blockchain.Test.Validators; using Nethermind.Blockchain.Tracing; +using Nethermind.Consensus.ExecutionRequests; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Rewards; +using Nethermind.Consensus.Withdrawals; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Test.Builders; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using Nethermind.Specs; +using Nethermind.Specs.Forks; +using Nethermind.Specs.Test; +using NSubstitute; using NUnit.Framework; //move all to correct folder @@ -74,5 +90,70 @@ public void Balance_and_nonce_changes() Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } } + + [Test] + public void System_contracts_and_withdrawals() + { + BlockProcessor processor = new(HoleskySpecProvider.Instance, + TestBlockValidator.AlwaysValid, + NoBlockRewards.Instance, + new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(_transactionProcessor), _stateProvider), + _stateProvider, + NullReceiptStorage.Instance, + new BeaconBlockRootHandler(_transactionProcessor, _stateProvider), + Substitute.For(), // create dummy? + LimboLogs.Instance, + new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), + new ExecutionRequestsProcessor(_transactionProcessor)); + + ulong gasPrice = 2; + long gasLimit = 100000; + Transaction tx = Build.A.Transaction + .WithTo(TestItem.AddressB) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .TestObject; + + Block block = Build.A.Block + .WithTransactions(tx) + .WithBaseFeePerGas(1) + .WithBeneficiary(TestItem.AddressC).TestObject; + + // BlockReceiptsTracer blockReceiptsTracer = new(); + // BlockAccessTracer accessTracer = new(); + // blockReceiptsTracer.SetOtherTracer(accessTracer); + // Execute(tx, block, blockReceiptsTracer); + + OverridableReleaseSpec spec = new(Prague.Instance) + { + IsEip7928Enabled = true + }; + (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); + + SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; + Assert.That(accountChanges, Has.Count.EqualTo(3)); + + List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; + List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; + List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + + using (Assert.EnterMultipleScope()) + { + Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + + Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); + Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); + + // zero balance change should not be recorded + Assert.That(toBalanceChanges, Is.Empty); + + Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + } + } } } diff --git a/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs index 77392d405a6..b7617318245 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/TransactionProcessorTests.cs @@ -40,7 +40,7 @@ public abstract class TransactionProcessorTests private readonly ISpecProvider _specProvider; private IEthereumEcdsa _ethereumEcdsa; protected ITransactionProcessor _transactionProcessor; - private IWorldState _stateProvider; + protected IWorldState _stateProvider; public TransactionProcessorTests(bool eip155Enabled) { diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index f448574b43c..68f6a00483f 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -45,18 +45,18 @@ public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, } - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) {} + public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { } - public void ReportOperationError(EvmExceptionType error) {} + public void ReportOperationError(EvmExceptionType error) { } - public void ReportOperationRemainingGas(long gas) {} + public void ReportOperationRemainingGas(long gas) { } - public void ReportLog(LogEntry log) {} + public void ReportLog(LogEntry log) { } - public void SetOperationMemorySize(ulong newSize) {} + public void SetOperationMemorySize(ulong newSize) { } - public void ReportMemoryChange(long offset, in ReadOnlySpan data) {} + public void ReportMemoryChange(long offset, in ReadOnlySpan data) { } public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) { @@ -72,9 +72,9 @@ public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlyS } } - public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) {} + public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) { } - public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) {} + public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) { } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -194,38 +194,38 @@ public void ReportStorageRead(in StorageCell storageCell) accountChanges.StorageReads.Add(storageKey); } - public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) {} + public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) { } - public void ReportActionEnd(long gas, ReadOnlyMemory output) {} + public void ReportActionEnd(long gas, ReadOnlyMemory output) { } - public void ReportActionError(EvmExceptionType exceptionType) {} + public void ReportActionError(EvmExceptionType exceptionType) { } - public void ReportActionRevert(long gasLeft, ReadOnlyMemory output) {} + public void ReportActionRevert(long gasLeft, ReadOnlyMemory output) { } - public void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory deployedCode) {} + public void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory deployedCode) { } - public void ReportByteCode(ReadOnlyMemory byteCode) {} + public void ReportByteCode(ReadOnlyMemory byteCode) { } - public void ReportGasUpdateForVmTrace(long refund, long gasAvailable) {} + public void ReportGasUpdateForVmTrace(long refund, long gasAvailable) { } - public void ReportRefund(long refund) {} + public void ReportRefund(long refund) { } - public void ReportExtraGasPressure(long extraGasPressure) {} + public void ReportExtraGasPressure(long extraGasPressure) { } public void ReportAccess(IReadOnlyCollection
accessedAddresses, IReadOnlyCollection accessedStorageCells) { // _bal.Add(new()); } - public void SetOperationStack(TraceStack stack) {} + public void SetOperationStack(TraceStack stack) { } - public void ReportStackPush(in ReadOnlySpan stackItem) {} + public void ReportStackPush(in ReadOnlySpan stackItem) { } - public void ReportBlockHash(Hash256 blockHash) {} + public void ReportBlockHash(Hash256 blockHash) { } - public void SetOperationMemory(TraceMemory memoryTrace) {} + public void SetOperationMemory(TraceMemory memoryTrace) { } - public void ReportFees(UInt256 fees, UInt256 burntFees) {} + public void ReportFees(UInt256 fees, UInt256 burntFees) { } // private ITxTracer _currentTxTracer = NullTxTracer.Instance; private ushort _blockAccessIndex = 0; @@ -252,7 +252,7 @@ public void Restore(int snapshot) // Block.Header.GasUsed = _txReceipts.Count > 0 ? _txReceipts.Last().GasUsedTotal : 0; } - public void ReportReward(Address author, string rewardType, UInt256 rewardValue) {} + public void ReportReward(Address author, string rewardType, UInt256 rewardValue) { } public void StartNewBlockTrace(Block block) { diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index b59400afb8f..2dab108feff 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -247,7 +247,7 @@ private Block PrepareBlockForProcessing(Block suggestedBlock) Block block = suggestedBlock.WithReplacedHeader(headerForProcessing); - if (block.BlockAccessList is not null) + if (block.BlockAccessList is not null && block.BlockAccessList.Length != 0) { block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); } @@ -287,7 +287,6 @@ private void ApplyMinerReward(Block block, BlockReward reward, IReleaseSpec spec if (_logger.IsTrace) _logger.Trace($" {(BigInteger)reward.Value / (BigInteger)Unit.Ether:N3}{Unit.EthSymbol} for account at {reward.Address}"); _stateProvider.AddToBalanceAndCreateIfNotExists(reward.Address, reward.Value, spec); - // tracer here? } private void ApplyDaoTransition(Block block) diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index c354ab068a3..aafe166b3fb 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -196,6 +196,6 @@ public ulong Eip4844TransitionTimestamp public bool IsEip7939Enabled => spec.IsEip7939Enabled; public bool IsEip7907Enabled => spec.IsEip7907Enabled; public bool IsRip7728Enabled => spec.IsRip7728Enabled; - public bool IsEip7928Enabled => spec.IsEip7928Enabled; + public bool IsEip7928Enabled { get; set; } = spec.IsEip7928Enabled; } } From 2f6829d0150ef9ffd68341edeba31d65e0ac29e3 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 15:05:47 +0100 Subject: [PATCH 026/215] move bal decoding to suggested block validation --- .../Messages/BlockErrorMessages.cs | 5 ++++- .../Processing/BlockProcessor.cs | 10 +--------- .../Validators/BlockValidator.cs | 20 +++++++++++++++++++ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs index ec1db39e983..414f7cde0c4 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs @@ -153,7 +153,10 @@ public static string ExceededBlockSizeLimit(int limit) => public const string MissingBlockLevelAccessList = "MissingBlockLevelAccessList: Must be present in block body."; + public const string InvalidBlockLevelAccessList = + $"InvalidBlockLevelAccessList: Unable to decode."; + public static string InvalidBlockLevelAccessListRoot(Hash256 expected, Hash256 actual) => - $"InvalidBlockLevelAccessListRoot: expected {expected}, got {actual}"; + $"InvalidBlockLevelAccessListRoot: Expected {expected}, got {actual}"; } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 2dab108feff..26a1cb5b9b2 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -15,7 +15,6 @@ using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; using Nethermind.Core; -using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Specs; using Nethermind.Core.Threading; using Nethermind.Crypto; @@ -245,14 +244,7 @@ private Block PrepareBlockForProcessing(Block suggestedBlock) headerForProcessing.StateRoot = bh.StateRoot; } - Block block = suggestedBlock.WithReplacedHeader(headerForProcessing); - - if (block.BlockAccessList is not null && block.BlockAccessList.Length != 0) - { - block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); - } - - return block; + return suggestedBlock.WithReplacedHeader(headerForProcessing); } private void ApplyMinerRewards(Block block, IBlockTracer tracer, IReleaseSpec spec) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 35384b53474..97c76fa5ef5 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -6,6 +6,7 @@ using Nethermind.Blockchain; using Nethermind.Consensus.Messages; using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -145,6 +146,25 @@ public bool ValidateSuggestedBlock(Block block, out string? errorMessage, bool v } } + if (spec.BlockLevelAccessListsEnabled) + { + if (block.BlockAccessList is null || block.BlockAccessList.Length == 0) + { + errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; + return false; + } + + try + { + block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); + } + catch (RlpException) + { + errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; + return false; + } + } + return true; } From 23dc197af3caa98820c25a7b286193f9e70d4bf9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 15:07:48 +0100 Subject: [PATCH 027/215] decoding debug logs --- .../Nethermind.Consensus/Validators/BlockValidator.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 97c76fa5ef5..921e907afb6 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -150,6 +150,7 @@ public bool ValidateSuggestedBlock(Block block, out string? errorMessage, bool v { if (block.BlockAccessList is null || block.BlockAccessList.Length == 0) { + if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list was missing or empty"); errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; return false; } @@ -158,8 +159,9 @@ public bool ValidateSuggestedBlock(Block block, out string? errorMessage, bool v { block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); } - catch (RlpException) + catch (RlpException e) { + if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list could not be decoded: {e}"); errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; return false; } From 496b6869b4927c2cb8458442f15175512a0aac00 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 15:14:36 +0100 Subject: [PATCH 028/215] empty rlp decode for bal --- .../BlockAccessListDecoder.cs | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs new file mode 100644 index 00000000000..880c6176840 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs @@ -0,0 +1,134 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp; + +public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + // private readonly TxDecoder _txDecoder = TxDecoder.Instance; + // private readonly HeaderDecoder _headerDecoder = new(); + // private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); + + private static BlockAccessListDecoder? _instance = null; + public static BlockAccessListDecoder Instance => _instance ??= new(); + + // public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) + // { + // return Rlp.LengthOfSequence(GetBodyLength(item)); + // } + + // public int GetBodyLength(BlockBody b) + // { + // (int txs, int uncles, int? withdrawals) = GetBodyComponentLength(b); + // return Rlp.LengthOfSequence(txs) + + // Rlp.LengthOfSequence(uncles) + + // (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0); + // } + + // public (int Txs, int Uncles, int? Withdrawals) GetBodyComponentLength(BlockBody b) => + // ( + // GetTxLength(b.Transactions), + // GetUnclesLength(b.Uncles), + // b.Withdrawals is not null ? GetWithdrawalsLength(b.Withdrawals) : null + // ); + + // private int GetTxLength(Transaction[] transactions) + // { + // if (transactions.Length == 0) return 0; + + // int sum = 0; + // foreach (Transaction tx in transactions) + // { + // sum += _txDecoder.GetLength(tx, RlpBehaviors.None); + // } + + // return sum; + // } + + // public BlockBody? Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + // { + // int sequenceLength = ctx.ReadSequenceLength(); + // int startingPosition = ctx.Position; + // if (sequenceLength == 0) + // { + // return null; + // } + + // return DecodeUnwrapped(ref ctx, startingPosition + sequenceLength); + // } + + // public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition) + // { + + // // quite significant allocations (>0.5%) here based on a sample 3M blocks sync + // // (just on these delegates) + // Transaction[] transactions = ctx.DecodeArray(_txDecoder); + // BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); + // Withdrawal[]? withdrawals = null; + + // if (ctx.PeekNumberOfItemsRemaining(lastPosition, 1) > 0) + // { + // withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); + // } + + // return new BlockBody(transactions, uncles, withdrawals); + // } + + // public BlockBody Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + // { + // Span span = rlpStream.PeekNextItem(); + // Rlp.ValueDecoderContext ctx = new Rlp.ValueDecoderContext(span); + // BlockBody response = Decode(ref ctx, rlpBehaviors); + // rlpStream.SkipItem(); + + // return response; + // } + + // public void Encode(RlpStream stream, BlockBody body, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + // { + // stream.StartSequence(GetBodyLength(body)); + // stream.StartSequence(GetTxLength(body.Transactions)); + // foreach (Transaction? txn in body.Transactions) + // { + // stream.Encode(txn); + // } + + // stream.StartSequence(GetUnclesLength(body.Uncles)); + // foreach (BlockHeader? uncle in body.Uncles) + // { + // stream.Encode(uncle); + // } + + // if (body.Withdrawals is not null) + // { + // stream.StartSequence(GetWithdrawalsLength(body.Withdrawals)); + // foreach (Withdrawal? withdrawal in body.Withdrawals) + // { + // stream.Encode(withdrawal); + // } + // } + // } + + BlockAccessList IRlpValueDecoder.Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) + { + throw new NotImplementedException(); + } + + public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) + { + throw new NotImplementedException(); + } + + BlockAccessList IRlpStreamDecoder.Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + throw new NotImplementedException(); + } + + public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + throw new NotImplementedException(); + } +} From 15bbab12c60dc3137cf7be21a6d4021886573c67 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 15:20:03 +0100 Subject: [PATCH 029/215] move RLP decoding --- .../{ => Eip7928}/BlockAccessListDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/Nethermind/Nethermind.Serialization.Rlp/{ => Eip7928}/BlockAccessListDecoder.cs (98%) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs similarity index 98% rename from src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs rename to src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 880c6176840..074ff75dfcc 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -4,7 +4,7 @@ using System; using Nethermind.Core.BlockAccessLists; -namespace Nethermind.Serialization.Rlp; +namespace Nethermind.Serialization.Rlp.Eip7928; public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStreamDecoder { From 7c31e87c10e47e9acf8fb646ded8f9027646d92d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 18:24:54 +0100 Subject: [PATCH 030/215] don't encode/decode for testing --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 4 +++- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index c5cc18a455d..bc98445671a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -132,7 +132,9 @@ public void System_contracts_and_withdrawals() }; (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); - SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; + // tmp + // SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; + SortedDictionary accountChanges = processedBlock.DecodedBlockAccessList!.Value.AccountChanges; Assert.That(accountChanges, Has.Count.EqualTo(3)); List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 26a1cb5b9b2..155536b1c55 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -167,7 +167,9 @@ protected virtual TxReceipt[] ProcessBlock( if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; + //tmp + block.DecodedBlockAccessList = BlockAccessTracer.BlockAccessList; + // body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; } return receipts; From 9015f703ac425f6346ff6428387f2fc696453282 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 18:32:39 +0100 Subject: [PATCH 031/215] init beacon block roots address --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index bc98445671a..63c5033fcc9 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -14,6 +14,7 @@ using Nethermind.Consensus.Withdrawals; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; @@ -106,6 +107,8 @@ public void System_contracts_and_withdrawals() new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), new ExecutionRequestsProcessor(_transactionProcessor)); + _stateProvider.CreateAccount(Eip4788Constants.BeaconRootsAddress, 10); + ulong gasPrice = 2; long gasLimit = 100000; Transaction tx = Build.A.Transaction @@ -117,6 +120,8 @@ public void System_contracts_and_withdrawals() .TestObject; Block block = Build.A.Block + .WithParentBeaconBlockRoot(Hash256.Zero) + .WithNumber(100) .WithTransactions(tx) .WithBaseFeePerGas(1) .WithBeneficiary(TestItem.AddressC).TestObject; From 8ac89395697c159b34f1bdb543d38ebd65796b2d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 22 Aug 2025 18:41:46 +0100 Subject: [PATCH 032/215] setup execution requests contracts --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 63c5033fcc9..2aae0778869 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -15,6 +15,7 @@ using Nethermind.Core; using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; +using Nethermind.Core.Test; using Nethermind.Core.Test.Builders; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; @@ -107,7 +108,12 @@ public void System_contracts_and_withdrawals() new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), new ExecutionRequestsProcessor(_transactionProcessor)); + // todo: just use test blockchain? _stateProvider.CreateAccount(Eip4788Constants.BeaconRootsAddress, 10); + _stateProvider.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); + _stateProvider.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, Prague.Instance); + _stateProvider.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); + _stateProvider.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, Prague.Instance); ulong gasPrice = 2; long gasLimit = 100000; From 4a507b6a5e7bd6e1facec75083bc7d4f7135e053 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:07:02 +0100 Subject: [PATCH 033/215] start implementing rlp --- .../BlockAccessListTests.cs | 20 +++++++++ .../Nethermind.Core/BlockAccessList.cs | 16 +++---- .../Eip7928/AccountChangesDecoder.cs | 39 +++++++++++++++++ .../Eip7928/BalanceChangeDecoder.cs | 42 ++++++++++++++++++ .../Eip7928/CodeChangeDecoder.cs | 42 ++++++++++++++++++ .../Eip7928/NonceChangeDecoder.cs | 42 ++++++++++++++++++ .../Eip7928/SlotChangesDecoder.cs | 43 +++++++++++++++++++ .../Eip7928/StorageChangeDecoder.cs | 42 ++++++++++++++++++ .../Nethermind.Serialization.Rlp/Rlp.cs | 39 +++++++++++++++++ 9 files changed, 317 insertions(+), 8 deletions(-) create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 2aae0778869..247559e0c45 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -168,5 +168,25 @@ public void System_contracts_and_withdrawals() Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } } + + [Test] + public void Fun_test() + { + StorageChange s = new() + { + BlockAccessIndex = 5, + NewValue = [] + }; + byte[] x = Rlp.Encode(s).Bytes; + // Assert.That(x, Has.Length.EqualTo(10)); + + BalanceChange b = new() + { + BlockAccessIndex = 5, + PostBalance = 69 + }; + byte[] y = Rlp.Encode(b).Bytes; + Assert.That(x, Has.Length.EqualTo(10)); + } } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index 8d3e53442bd..160a3a0e79f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -43,17 +43,17 @@ public struct CodeChange public struct SlotChanges() { // [SszVector(32)] - // public byte[] Slot { get; set; } + public byte[] Slot { get; set; } = []; // [SszList(Eip7928Constants.MaxTxs)] public List Changes { get; set; } = []; } -public struct StorageKey(ReadOnlySpan key) -{ - // [SszVector(32)] - public byte[] Key { get; set; } = key.ToArray(); -} +// public struct StorageKey(ReadOnlySpan key) +// { +// // [SszVector(32)] +// public byte[] Key { get; set; } = key.ToArray(); +// } public struct AccountChanges(Address address) { @@ -62,11 +62,11 @@ public struct AccountChanges(Address address) // Storage changes (slot -> [tx_index -> new_value]) // [SszList(Eip7928Constants.MaxSlots)] - public SortedDictionary StorageChanges { get; set; } = []; + public SortedDictionary StorageChanges { get; set; } = []; // Read-only storage keys // [SszList(Eip7928Constants.MaxSlots)] - public List StorageReads { get; set; } = []; + public List StorageReads { get; set; } = []; // Balance changes ([tx_index -> post_balance]) // [SszList(Eip7928Constants.MaxTxs)] diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs new file mode 100644 index 00000000000..9d1e63a401c --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class AccountChangesDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + private static AccountChangesDecoder? _instance = null; + public static AccountChangesDecoder Instance => _instance ??= new(); + + public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + BlockAccessIndex = ctx.DecodeUShort(), + PostBalance = ctx.DecodeUInt256() + }; + + public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) => Length; + + public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + AccountChanges response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(Length); + stream.Encode(item.BlockAccessIndex); + stream.Encode(item.PostBalance); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs new file mode 100644 index 00000000000..a58f309db1c --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class BalanceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + // ushort + UInt256 + private const int Length = 2 + 32; + + private static BalanceChangeDecoder? _instance = null; + public static BalanceChangeDecoder Instance => _instance ??= new(); + + public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + BlockAccessIndex = ctx.DecodeUShort(), + PostBalance = ctx.DecodeUInt256() + }; + + public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) => Length; + + public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + BalanceChange response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(Length); + stream.Encode(item.BlockAccessIndex); + stream.Encode(item.PostBalance); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs new file mode 100644 index 00000000000..63b66698739 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class CodeChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + // ushort + UInt256 + private const int Length = 2 + 32; + + private static CodeChangeDecoder? _instance = null; + public static CodeChangeDecoder Instance => _instance ??= new(); + + public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + BlockAccessIndex = ctx.DecodeUShort(), + NewCode = ctx.DecodeByteArray() + }; + + public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) => Length; + + public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + CodeChange response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, CodeChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(Length); + stream.Encode(item.BlockAccessIndex); + stream.Encode(item.NewCode); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs new file mode 100644 index 00000000000..6703dbf6616 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + // ushort + UInt256 + private const int Length = 2 + 32; + + private static BalanceChangeDecoder? _instance = null; + public static BalanceChangeDecoder Instance => _instance ??= new(); + + public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + BlockAccessIndex = ctx.DecodeUShort(), + PostBalance = ctx.DecodeUInt256() + }; + + public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) => Length; + + public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + BalanceChange response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(Length); + stream.Encode(item.BlockAccessIndex); + stream.Encode(item.PostBalance); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs new file mode 100644 index 00000000000..467621714eb --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Linq; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class SlotChangesDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + private static SlotChangesDecoder? _instance = null; + public static SlotChangesDecoder Instance => _instance ??= new(); + + public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + Slot = ctx.DecodeByteArray(), + Changes = [.. ctx.DecodeArray(StorageChangeDecoder.Instance)] + }; + + public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) => 0; + + public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + SlotChanges response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(0); + stream.Encode(item.Slot); + foreach (StorageChange change in item.Changes) + { + StorageChangeDecoder.Instance.Encode(stream, change); + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs new file mode 100644 index 00000000000..993d4986823 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class StorageChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + // ushort + 32 byte vector + private const int Length = 2 + 32; + + private static StorageChangeDecoder? _instance = null; + public static StorageChangeDecoder Instance => _instance ??= new(); + + public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + => new() + { + BlockAccessIndex = ctx.DecodeUShort(), + NewValue = ctx.DecodeByteArray() + }; + + public int GetLength(StorageChange item, RlpBehaviors rlpBehaviors) => Length; + + public StorageChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + StorageChange response = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return response; + } + + public void Encode(RlpStream stream, StorageChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(Length); + stream.Encode(item.BlockAccessIndex); + stream.Encode(item.NewValue); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index 582df031495..642ba81bae2 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -1573,6 +1573,45 @@ internal byte[][] DecodeByteArrays() return result; } + public ushort DecodeUShort() + { + int prefix = ReadByte(); + + switch (prefix) + { + case 0: + throw new RlpException($"Non-canonical ushort (leading zero bytes) at position {Position}"); + case < 128: + return (ushort)prefix; + case 128: + return 0; + } + + int length = prefix - 128; + if (length > 8) + { + throw new RlpException($"Unexpected length of ushort value: {length}"); + } + + ushort result = 0; + for (int i = 2; i > 0; i--) + { + result <<= 8; + if (i <= length) + { + result |= PeekByte(length - i); + if (result == 0) + { + throw new RlpException($"Non-canonical ushort (leading zero bytes) at position {Position}"); + } + } + } + + SkipBytes(length); + + return result; + } + public byte DecodeByte() { byte byteValue = PeekByte(); From 87dc02c45175b74ce02621b1a132d7085ebf7754 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:10:30 +0100 Subject: [PATCH 034/215] account changes rlp+ --- .../Eip7928/AccountChangesDecoder.cs | 80 +++++++++++++++++-- .../Eip7928/CodeChangeDecoder.cs | 7 +- .../Eip7928/NonceChangeDecoder.cs | 24 +++--- .../Eip7928/SlotChangesDecoder.cs | 12 ++- 4 files changed, 98 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 9d1e63a401c..7574830ba64 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; +using System.Linq; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -12,13 +14,53 @@ public class AccountChangesDecoder : IRlpValueDecoder, IRlpStrea public static AccountChangesDecoder Instance => _instance ??= new(); public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + byte[] address = ctx.DecodeAddress().Bytes; + SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); + SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s)); + byte[][] storageReads = ctx.DecodeByteArrays(); + BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); + NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); + CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); + return new() { - BlockAccessIndex = ctx.DecodeUShort(), - PostBalance = ctx.DecodeUInt256() + Address = address, + StorageChanges = slotChangesMap, + StorageReads = [.. storageReads], + BalanceChanges = [.. balanceChanges], + NonceChanges = [.. nonceChanges], + CodeChanges = [.. codeChanges] }; + } + + public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) + { + int len = 20; // address + + foreach (SlotChanges slotChanges in item.StorageChanges.Values) + { + len += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); + } + + len += item.StorageReads.Count * 32; + + foreach (BalanceChange balanceChange in item.BalanceChanges) + { + len += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); + } + + foreach (NonceChange nonceChange in item.NonceChanges) + { + len += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); + } + + foreach (CodeChange codeChange in item.CodeChanges) + { + len += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); + } - public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) => Length; + return len; + } public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -32,8 +74,32 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(Length); - stream.Encode(item.BlockAccessIndex); - stream.Encode(item.PostBalance); + stream.StartSequence(GetLength(item, rlpBehaviors)); + stream.Encode(item.Address); + + foreach (SlotChanges slotChanges in item.StorageChanges.Values) + { + SlotChangesDecoder.Instance.Encode(stream, slotChanges); + } + + foreach (byte[] storageKey in item.StorageReads) + { + stream.Encode(storageKey); + } + + foreach (BalanceChange balanceChange in item.BalanceChanges) + { + BalanceChangeDecoder.Instance.Encode(stream, balanceChange); + } + + foreach (NonceChange nonceChange in item.NonceChanges) + { + NonceChangeDecoder.Instance.Encode(stream, nonceChange); + } + + foreach (CodeChange codeChange in item.CodeChanges) + { + CodeChangeDecoder.Instance.Encode(stream, codeChange); + } } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 63b66698739..00e22fce156 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -8,9 +8,6 @@ namespace Nethermind.Serialization.Rlp.Eip7928; public class CodeChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // ushort + UInt256 - private const int Length = 2 + 32; - private static CodeChangeDecoder? _instance = null; public static CodeChangeDecoder Instance => _instance ??= new(); @@ -21,7 +18,7 @@ public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehavi NewCode = ctx.DecodeByteArray() }; - public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) => Length; + public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) => 2 + item.NewCode.Length; public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -35,7 +32,7 @@ public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, CodeChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(Length); + stream.StartSequence(GetLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); stream.Encode(item.NewCode); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index 6703dbf6616..ff72867bfcd 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -6,37 +6,37 @@ namespace Nethermind.Serialization.Rlp.Eip7928; -public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder +public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // ushort + UInt256 - private const int Length = 2 + 32; + // ushort + ulong + private const int Length = 2 + 8; - private static BalanceChangeDecoder? _instance = null; - public static BalanceChangeDecoder Instance => _instance ??= new(); + private static NonceChangeDecoder? _instance = null; + public static NonceChangeDecoder Instance => _instance ??= new(); - public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + public NonceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) => new() { BlockAccessIndex = ctx.DecodeUShort(), - PostBalance = ctx.DecodeUInt256() + NewNonce = ctx.DecodeULong() }; - public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) => Length; + public int GetLength(NonceChange item, RlpBehaviors rlpBehaviors) => Length; - public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + public NonceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - BalanceChange response = Decode(ref ctx, rlpBehaviors); + NonceChange response = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); return response; } - public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + public void Encode(RlpStream stream, NonceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(Length); stream.Encode(item.BlockAccessIndex); - stream.Encode(item.PostBalance); + stream.Encode(item.NewNonce); } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 467621714eb..12c3ea10a36 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -19,7 +19,17 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav Changes = [.. ctx.DecodeArray(StorageChangeDecoder.Instance)] }; - public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) => 0; + public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) + { + int len = 32; // slot + + foreach (StorageChange slotChange in item.Changes) + { + len += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); + } + + return len; + } public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { From a9f0e93c04de88ec3541c7185c92d7bb2c2bed32 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:58:39 +0100 Subject: [PATCH 035/215] bal encoding / decoding --- .../BlockAccessListTests.cs | 20 --- .../Eip7928/AccountChangesDecoder.cs | 4 +- .../Eip7928/BalanceChangeDecoder.cs | 4 +- .../Eip7928/BlockAccessListDecoder.cs | 138 ++++-------------- .../Eip7928/CodeChangeDecoder.cs | 4 +- .../Eip7928/NonceChangeDecoder.cs | 4 +- .../Eip7928/SlotChangesDecoder.cs | 4 +- .../Eip7928/StorageChangeDecoder.cs | 4 +- 8 files changed, 43 insertions(+), 139 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 247559e0c45..2aae0778869 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -168,25 +168,5 @@ public void System_contracts_and_withdrawals() Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } } - - [Test] - public void Fun_test() - { - StorageChange s = new() - { - BlockAccessIndex = 5, - NewValue = [] - }; - byte[] x = Rlp.Encode(s).Bytes; - // Assert.That(x, Has.Length.EqualTo(10)); - - BalanceChange b = new() - { - BlockAccessIndex = 5, - PostBalance = 69 - }; - byte[] y = Rlp.Encode(b).Bytes; - Assert.That(x, Has.Length.EqualTo(10)); - } } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 7574830ba64..8aca07f706f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -66,10 +66,10 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - AccountChanges response = Decode(ref ctx, rlpBehaviors); + AccountChanges res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index a58f309db1c..c526b31b3e1 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -27,10 +27,10 @@ public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - BalanceChange response = Decode(ref ctx, rlpBehaviors); + BalanceChange res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 074ff75dfcc..a88428c04ee 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -2,133 +2,57 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // private readonly TxDecoder _txDecoder = TxDecoder.Instance; - // private readonly HeaderDecoder _headerDecoder = new(); - // private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); - private static BlockAccessListDecoder? _instance = null; public static BlockAccessListDecoder Instance => _instance ??= new(); - // public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) - // { - // return Rlp.LengthOfSequence(GetBodyLength(item)); - // } - - // public int GetBodyLength(BlockBody b) - // { - // (int txs, int uncles, int? withdrawals) = GetBodyComponentLength(b); - // return Rlp.LengthOfSequence(txs) + - // Rlp.LengthOfSequence(uncles) + - // (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0); - // } - - // public (int Txs, int Uncles, int? Withdrawals) GetBodyComponentLength(BlockBody b) => - // ( - // GetTxLength(b.Transactions), - // GetUnclesLength(b.Uncles), - // b.Withdrawals is not null ? GetWithdrawalsLength(b.Withdrawals) : null - // ); - - // private int GetTxLength(Transaction[] transactions) - // { - // if (transactions.Length == 0) return 0; - - // int sum = 0; - // foreach (Transaction tx in transactions) - // { - // sum += _txDecoder.GetLength(tx, RlpBehaviors.None); - // } - - // return sum; - // } - - // public BlockBody? Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - // { - // int sequenceLength = ctx.ReadSequenceLength(); - // int startingPosition = ctx.Position; - // if (sequenceLength == 0) - // { - // return null; - // } - - // return DecodeUnwrapped(ref ctx, startingPosition + sequenceLength); - // } - - // public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition) - // { - - // // quite significant allocations (>0.5%) here based on a sample 3M blocks sync - // // (just on these delegates) - // Transaction[] transactions = ctx.DecodeArray(_txDecoder); - // BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); - // Withdrawal[]? withdrawals = null; - - // if (ctx.PeekNumberOfItemsRemaining(lastPosition, 1) > 0) - // { - // withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); - // } - - // return new BlockBody(transactions, uncles, withdrawals); - // } - - // public BlockBody Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - // { - // Span span = rlpStream.PeekNextItem(); - // Rlp.ValueDecoderContext ctx = new Rlp.ValueDecoderContext(span); - // BlockBody response = Decode(ref ctx, rlpBehaviors); - // rlpStream.SkipItem(); - - // return response; - // } - - // public void Encode(RlpStream stream, BlockBody body, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - // { - // stream.StartSequence(GetBodyLength(body)); - // stream.StartSequence(GetTxLength(body.Transactions)); - // foreach (Transaction? txn in body.Transactions) - // { - // stream.Encode(txn); - // } - - // stream.StartSequence(GetUnclesLength(body.Uncles)); - // foreach (BlockHeader? uncle in body.Uncles) - // { - // stream.Encode(uncle); - // } + public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) + { + int len = 0; - // if (body.Withdrawals is not null) - // { - // stream.StartSequence(GetWithdrawalsLength(body.Withdrawals)); - // foreach (Withdrawal? withdrawal in body.Withdrawals) - // { - // stream.Encode(withdrawal); - // } - // } - // } + foreach (AccountChanges accountChange in item.AccountChanges.Values) + { + len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); + } - BlockAccessList IRlpValueDecoder.Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - throw new NotImplementedException(); + return len; } - public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) + public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - throw new NotImplementedException(); + AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); + SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => new Address(a.Address), a => a)); + return new() + { + AccountChanges = accountChangesMap + }; } - BlockAccessList IRlpStreamDecoder.Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { - throw new NotImplementedException(); + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + BlockAccessList res = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return res; } public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - throw new NotImplementedException(); + stream.StartSequence(GetLength(item, rlpBehaviors)); + + foreach (AccountChanges accountChanges in item.AccountChanges.Values) + { + AccountChangesDecoder.Instance.Encode(stream, accountChanges); + } } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 00e22fce156..64199c527f1 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -24,10 +24,10 @@ public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - CodeChange response = Decode(ref ctx, rlpBehaviors); + CodeChange res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, CodeChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index ff72867bfcd..3d95b07ddbc 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -27,10 +27,10 @@ public NonceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - NonceChange response = Decode(ref ctx, rlpBehaviors); + NonceChange res= Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, NonceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 12c3ea10a36..bd0606ee86f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -35,10 +35,10 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - SlotChanges response = Decode(ref ctx, rlpBehaviors); + SlotChanges res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 993d4986823..6d928b1fc64 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -27,10 +27,10 @@ public StorageChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - StorageChange response = Decode(ref ctx, rlpBehaviors); + StorageChange res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); - return response; + return res; } public void Encode(RlpStream stream, StorageChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) From d12d0b5d8267dcc3763a58bb9fa974a3cebbc254 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:05:12 +0100 Subject: [PATCH 036/215] change storagekey to bytes, use rlp in test --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 4 ++-- .../Nethermind.Blockchain/Tracing/BlockAccessTracer.cs | 9 +++------ .../Nethermind.Consensus/Processing/BlockProcessor.cs | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 2aae0778869..3963ba98cc8 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -144,8 +144,8 @@ public void System_contracts_and_withdrawals() (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); // tmp - // SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; - SortedDictionary accountChanges = processedBlock.DecodedBlockAccessList!.Value.AccountChanges; + SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; + // SortedDictionary accountChanges = processedBlock.DecodedBlockAccessList!.Value.AccountChanges; Assert.That(accountChanges, Has.Count.EqualTo(3)); List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 68f6a00483f..a3894486f6a 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -179,11 +179,8 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ public void ReportStorageRead(in StorageCell storageCell) { - StorageKey storageKey = new() - { - Key = storageCell.Hash.ToByteArray() - }; - Address address = Address.Zero; + byte[] storageKey = storageCell.Hash.ToByteArray(); + Address address = storageCell.Address; if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) { @@ -304,7 +301,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan NewValue = value.ToArray() }; - StorageKey storageKey = new(key); + byte[] storageKey = [.. key]; if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) { storageChanges = new(); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 155536b1c55..bf1d2b595c4 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -168,8 +168,8 @@ protected virtual TxReceipt[] ProcessBlock( if (spec.BlockLevelAccessListsEnabled) { //tmp - block.DecodedBlockAccessList = BlockAccessTracer.BlockAccessList; - // body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; + // block.DecodedBlockAccessList = BlockAccessTracer.BlockAccessList; + body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; } return receipts; From 3223680e8241e3aa03e41189c4fe48326cced66e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 27 Aug 2025 13:15:16 +0100 Subject: [PATCH 037/215] always start new sequence rlp --- .../Eip7928/AccountChangesDecoder.cs | 85 +++++++++++++------ .../Eip7928/SlotChangesDecoder.cs | 10 ++- 2 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 8aca07f706f..192a4a2561b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -34,33 +35,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe } public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) - { - int len = 20; // address - - foreach (SlotChanges slotChanges in item.StorageChanges.Values) - { - len += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); - } - - len += item.StorageReads.Count * 32; - - foreach (BalanceChange balanceChange in item.BalanceChanges) - { - len += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); - } - - foreach (NonceChange nonceChange in item.NonceChanges) - { - len += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); - } - - foreach (CodeChange codeChange in item.CodeChanges) - { - len += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); - } - - return len; - } + => GetContentLength(item, rlpBehaviors).Total; public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -74,32 +49,86 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(GetLength(item, rlpBehaviors)); + ContentLengths contentLengths = GetContentLength(item, rlpBehaviors); + stream.StartSequence(contentLengths.Total); stream.Encode(item.Address); + stream.StartSequence(contentLengths.SlotChanges); foreach (SlotChanges slotChanges in item.StorageChanges.Values) { SlotChangesDecoder.Instance.Encode(stream, slotChanges); } + stream.StartSequence(contentLengths.StorageReads); foreach (byte[] storageKey in item.StorageReads) { stream.Encode(storageKey); } + stream.StartSequence(contentLengths.BalanceChanges); foreach (BalanceChange balanceChange in item.BalanceChanges) { BalanceChangeDecoder.Instance.Encode(stream, balanceChange); } + stream.StartSequence(contentLengths.NonceChanges); foreach (NonceChange nonceChange in item.NonceChanges) { NonceChangeDecoder.Instance.Encode(stream, nonceChange); } + stream.StartSequence(contentLengths.CodeChanges); foreach (CodeChange codeChange in item.CodeChanges) { CodeChangeDecoder.Instance.Encode(stream, codeChange); } } + + private static ContentLengths GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) + { + ContentLengths res = new() + { + Total = Address.Size, + SlotChanges = 0, + StorageReads = 0, + BalanceChanges = 0, + NonceChanges = 0, + CodeChanges = 0 + }; + + foreach (SlotChanges slotChanges in item.StorageChanges.Values) + { + res.SlotChanges += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); + } + + res.StorageReads = item.StorageReads.Count * 32; + + foreach (BalanceChange balanceChange in item.BalanceChanges) + { + res.BalanceChanges += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); + } + + foreach (NonceChange nonceChange in item.NonceChanges) + { + res.NonceChanges += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); + } + + foreach (CodeChange codeChange in item.CodeChanges) + { + res.CodeChanges += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); + } + + res.Total += res.SlotChanges + res.StorageReads + res.BalanceChanges + res.NonceChanges + res.CodeChanges; + return res; + } + + private struct ContentLengths + { + public int Total; + public int SlotChanges; + public int StorageReads; + public int BalanceChanges; + public int NonceChanges; + public int CodeChanges; + } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index bd0606ee86f..c27ff75d315 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Linq; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -12,6 +11,8 @@ public class SlotChangesDecoder : IRlpValueDecoder, IRlpStreamDecod private static SlotChangesDecoder? _instance = null; public static SlotChangesDecoder Instance => _instance ??= new(); + private const int SlotSize = 32; + public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) => new() { @@ -21,7 +22,7 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) { - int len = 32; // slot + int len = SlotSize; foreach (StorageChange slotChange in item.Changes) { @@ -43,8 +44,11 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(0); + int len = GetLength(item, rlpBehaviors); + stream.StartSequence(len); stream.Encode(item.Slot); + + stream.StartSequence(len - SlotSize); foreach (StorageChange change in item.Changes) { StorageChangeDecoder.Instance.Encode(stream, change); From 7009deed72cdb6d58bf19329e5c1c1ac0c0357e3 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:13:54 +0100 Subject: [PATCH 038/215] fix bal encoding length --- .../BlockAccessListTests.cs | 9 +++++++ .../Eip7928/BlockAccessListDecoder.cs | 25 +++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 3963ba98cc8..d14f838c765 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -93,6 +93,15 @@ public void Balance_and_nonce_changes() } } + [Test] + public void Encoding_decoding_test() + { + BlockAccessList b = new(); + byte[] bytes = Rlp.Encode(b).Bytes; + BlockAccessList b2 = Rlp.Decode(bytes); + Assert.That(b, Is.EqualTo(b2)); + } + [Test] public void System_contracts_and_withdrawals() { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index a88428c04ee..ec385d2011a 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -15,16 +15,7 @@ public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStr public static BlockAccessListDecoder Instance => _instance ??= new(); public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) - { - int len = 0; - - foreach (AccountChanges accountChange in item.AccountChanges.Values) - { - len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); - } - - return len; - } + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { @@ -48,11 +39,23 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(GetLength(item, rlpBehaviors)); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); foreach (AccountChanges accountChanges in item.AccountChanges.Values) { AccountChangesDecoder.Instance.Encode(stream, accountChanges); } } + + private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) + { + int len = 0; + + foreach (AccountChanges accountChange in item.AccountChanges.Values) + { + len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); + } + + return len; + } } From f30d1b5c2189bd6c2195b3d1f9146a1f43d2f4b0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:42:12 +0100 Subject: [PATCH 039/215] fix all rlp lengths --- .../BlockAccessListTests.cs | 2 +- .../Eip7928/AccountChangesDecoder.cs | 11 ++++--- .../Eip7928/BalanceChangeDecoder.cs | 11 +++---- .../Eip7928/CodeChangeDecoder.cs | 6 +++- .../Eip7928/NonceChangeDecoder.cs | 11 +++---- .../Eip7928/SlotChangesDecoder.cs | 30 +++++++++++-------- .../Eip7928/StorageChangeDecoder.cs | 11 +++---- .../Nethermind.Serialization.Rlp/Rlp.cs | 13 ++++++++ 8 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index d14f838c765..e3ac6f75a0a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -93,13 +93,13 @@ public void Balance_and_nonce_changes() } } + // tmp [Test] public void Encoding_decoding_test() { BlockAccessList b = new(); byte[] bytes = Rlp.Encode(b).Bytes; BlockAccessList b2 = Rlp.Decode(bytes); - Assert.That(b, Is.EqualTo(b2)); } [Test] diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 192a4a2561b..403b1ba8f76 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -35,7 +34,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe } public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) - => GetContentLength(item, rlpBehaviors).Total; + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors).Total); public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -88,7 +87,7 @@ private static ContentLengths GetContentLength(AccountChanges item, RlpBehaviors { ContentLengths res = new() { - Total = Address.Size, + Total = Rlp.LengthOfAddressRlp, SlotChanges = 0, StorageReads = 0, BalanceChanges = 0, @@ -100,23 +99,27 @@ private static ContentLengths GetContentLength(AccountChanges item, RlpBehaviors { res.SlotChanges += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); } + res.SlotChanges = Rlp.LengthOfSequence(res.SlotChanges); - res.StorageReads = item.StorageReads.Count * 32; + res.StorageReads = Rlp.LengthOfSequence(item.StorageReads.Count * 32); foreach (BalanceChange balanceChange in item.BalanceChanges) { res.BalanceChanges += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); } + res.BalanceChanges = Rlp.LengthOfSequence(res.BalanceChanges); foreach (NonceChange nonceChange in item.NonceChanges) { res.NonceChanges += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); } + res.NonceChanges = Rlp.LengthOfSequence(res.NonceChanges); foreach (CodeChange codeChange in item.CodeChanges) { res.CodeChanges += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); } + res.CodeChanges = Rlp.LengthOfSequence(res.CodeChanges); res.Total += res.SlotChanges + res.StorageReads + res.BalanceChanges + res.NonceChanges + res.CodeChanges; return res; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index c526b31b3e1..d1a717819da 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -8,9 +8,6 @@ namespace Nethermind.Serialization.Rlp.Eip7928; public class BalanceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // ushort + UInt256 - private const int Length = 2 + 32; - private static BalanceChangeDecoder? _instance = null; public static BalanceChangeDecoder Instance => _instance ??= new(); @@ -21,7 +18,8 @@ public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh PostBalance = ctx.DecodeUInt256() }; - public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) => Length; + public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -35,8 +33,11 @@ public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(Length); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); stream.Encode(item.PostBalance); } + + public static int GetContentLength(BalanceChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.PostBalance); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 64199c527f1..61519946373 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -18,7 +18,8 @@ public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehavi NewCode = ctx.DecodeByteArray() }; - public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) => 2 + item.NewCode.Length; + public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -36,4 +37,7 @@ public void Encode(RlpStream stream, CodeChange item, RlpBehaviors rlpBehaviors stream.Encode(item.BlockAccessIndex); stream.Encode(item.NewCode); } + + public static int GetContentLength(CodeChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewCode); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index 3d95b07ddbc..c5e06bc473d 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -8,9 +8,6 @@ namespace Nethermind.Serialization.Rlp.Eip7928; public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // ushort + ulong - private const int Length = 2 + 8; - private static NonceChangeDecoder? _instance = null; public static NonceChangeDecoder Instance => _instance ??= new(); @@ -21,7 +18,8 @@ public NonceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav NewNonce = ctx.DecodeULong() }; - public int GetLength(NonceChange item, RlpBehaviors rlpBehaviors) => Length; + public int GetLength(NonceChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public NonceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -35,8 +33,11 @@ public NonceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, NonceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(Length); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); stream.Encode(item.NewNonce); } + + public static int GetContentLength(NonceChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewNonce); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index c27ff75d315..8507ec41f74 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -21,16 +22,7 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav }; public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) - { - int len = SlotSize; - - foreach (StorageChange slotChange in item.Changes) - { - len += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); - } - - return len; - } + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors).Total); public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -44,14 +36,26 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - int len = GetLength(item, rlpBehaviors); - stream.StartSequence(len); + (int Total, int SlotChanges) len = GetContentLength(item, rlpBehaviors); + stream.StartSequence(len.Total); stream.Encode(item.Slot); - stream.StartSequence(len - SlotSize); + stream.StartSequence(len.SlotChanges); foreach (StorageChange change in item.Changes) { StorageChangeDecoder.Instance.Encode(stream, change); } } + + public static (int Total, int SlotChanges) GetContentLength(SlotChanges item, RlpBehaviors rlpBehaviors) + { + int slotChangeLen = 0; + + foreach (StorageChange slotChange in item.Changes) + { + slotChangeLen += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); + } + + return (slotChangeLen + Rlp.LengthOf(item.Slot), slotChangeLen); + } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 6d928b1fc64..6cdd2b76076 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -8,9 +8,6 @@ namespace Nethermind.Serialization.Rlp.Eip7928; public class StorageChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { - // ushort + 32 byte vector - private const int Length = 2 + 32; - private static StorageChangeDecoder? _instance = null; public static StorageChangeDecoder Instance => _instance ??= new(); @@ -21,7 +18,8 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh NewValue = ctx.DecodeByteArray() }; - public int GetLength(StorageChange item, RlpBehaviors rlpBehaviors) => Length; + public int GetLength(StorageChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public StorageChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -35,8 +33,11 @@ public StorageChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, StorageChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(Length); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); stream.Encode(item.NewValue); } + + public static int GetContentLength(StorageChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewValue); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index 642ba81bae2..542d50f821e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -1767,6 +1767,19 @@ public static int LengthOf(long value) public static int LengthOf(int value) => LengthOf((long)value); + public static int LengthOf(ushort value) + { + if (value < 128) + { + return 1; + } + else + { + // everything has a length prefix + return 1 + sizeof(ushort) - (BitOperations.LeadingZeroCount(value) / 2); + } + } + public static int LengthOf(Hash256? item) { return item is null ? 1 : 33; From c49c33cc0f8a4e27f62ddf25e9cdd527c3726cd7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 09:51:46 +0100 Subject: [PATCH 040/215] fix storagechange and slotchanges encodings --- .../BlockAccessListTests.cs | 23 ++++++++-- .../Eip7928/AccountChangesDecoder.cs | 9 ++++ .../Eip7928/BalanceChangeDecoder.cs | 6 +-- .../Eip7928/SlotChangesDecoder.cs | 46 +++++++++++++------ .../Eip7928/StorageChangeDecoder.cs | 14 +++++- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index e3ac6f75a0a..a5ff14ee5d2 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; +using System.Linq; using System.Threading; using Nethermind.Blockchain.BeaconBlockRoot; using Nethermind.Blockchain.Blocks; @@ -97,9 +98,25 @@ public void Balance_and_nonce_changes() [Test] public void Encoding_decoding_test() { - BlockAccessList b = new(); - byte[] bytes = Rlp.Encode(b).Bytes; - BlockAccessList b2 = Rlp.Decode(bytes); + StorageChange s = new() + { + BlockAccessIndex = 10, + NewValue = [.. Enumerable.Repeat(50, 32)] + }; + byte[] b = Rlp.Encode(s, RlpBehaviors.None).Bytes; + StorageChange s2 = Rlp.Decode(b, RlpBehaviors.None); + + SlotChanges sc = new() + { + Slot = [.. Enumerable.Repeat(100, 32)], + Changes = [s, s] + }; + byte[] b2 = Rlp.Encode(sc, RlpBehaviors.None).Bytes; + SlotChanges sc2 = Rlp.Decode(b2, RlpBehaviors.None); + Assert.That(true); + // BlockAccessList b = new(); + // byte[] bytes = Rlp.Encode(b).Bytes; + // BlockAccessList b2 = Rlp.Decode(bytes); } [Test] diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 403b1ba8f76..47da196ce41 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -15,6 +15,9 @@ public class AccountChangesDecoder : IRlpValueDecoder, IRlpStrea public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + byte[] address = ctx.DecodeAddress().Bytes; SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s)); @@ -22,6 +25,12 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); + + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + return new() { Address = address, diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index d1a717819da..c1699f77988 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -11,6 +11,9 @@ public class BalanceChangeDecoder : IRlpValueDecoder, IRlpStreamD private static BalanceChangeDecoder? _instance = null; public static BalanceChangeDecoder Instance => _instance ??= new(); + public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) => new() { @@ -18,9 +21,6 @@ public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh PostBalance = ctx.DecodeUInt256() }; - public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); - public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 8507ec41f74..dca45d3642b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -12,15 +11,31 @@ public class SlotChangesDecoder : IRlpValueDecoder, IRlpStreamDecod private static SlotChangesDecoder? _instance = null; public static SlotChangesDecoder Instance => _instance ??= new(); - private const int SlotSize = 32; public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + byte[] slot = ctx.DecodeByteArray(); + + // int changesLength = ctx.ReadSequenceLength(); + // int changesCheck = length + ctx.Position; + + SlotChanges slotChanges = new() { - Slot = ctx.DecodeByteArray(), + Slot = slot, Changes = [.. ctx.DecodeArray(StorageChangeDecoder.Instance)] }; + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return slotChanges; + } + public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors).Total); @@ -36,18 +51,21 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - (int Total, int SlotChanges) len = GetContentLength(item, rlpBehaviors); - stream.StartSequence(len.Total); + (int Total, int Slot, int SlotChanges) = GetContentLength(item, rlpBehaviors); + stream.StartSequence(Total); + + // stream.StartSequence(Slot); stream.Encode(item.Slot); - stream.StartSequence(len.SlotChanges); - foreach (StorageChange change in item.Changes) - { - StorageChangeDecoder.Instance.Encode(stream, change); - } + // stream.StartSequence(SlotChanges); + // foreach (StorageChange change in item.Changes) + // { + // StorageChangeDecoder.Instance.Encode(stream, change); + // } + stream.EncodeArray([.. item.Changes], rlpBehaviors); } - public static (int Total, int SlotChanges) GetContentLength(SlotChanges item, RlpBehaviors rlpBehaviors) + public static (int Total, int Slot, int SlotChanges) GetContentLength(SlotChanges item, RlpBehaviors rlpBehaviors) { int slotChangeLen = 0; @@ -56,6 +74,8 @@ public static (int Total, int SlotChanges) GetContentLength(SlotChanges item, Rl slotChangeLen += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); } - return (slotChangeLen + Rlp.LengthOf(item.Slot), slotChangeLen); + int slot = Rlp.LengthOf(item.Slot); + slotChangeLen = Rlp.LengthOfSequence(slotChangeLen); + return (slotChangeLen + slot, slot, slotChangeLen); } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 6cdd2b76076..72a8c77dacd 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -12,12 +12,24 @@ public class StorageChangeDecoder : IRlpValueDecoder, IRlpStreamD public static StorageChangeDecoder Instance => _instance ??= new(); public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + StorageChange storageChange = new() { BlockAccessIndex = ctx.DecodeUShort(), NewValue = ctx.DecodeByteArray() }; + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return storageChange; + } + public int GetLength(StorageChange item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); From 19bafac725a9d88983221cbb2382687fe91394ea Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:07:49 +0100 Subject: [PATCH 041/215] use encodearray --- .../Eip7928/AccountChangesDecoder.cs | 134 +++++++++--------- .../Eip7928/BalanceChangeDecoder.cs | 14 +- .../Eip7928/BlockAccessListDecoder.cs | 14 +- .../Eip7928/CodeChangeDecoder.cs | 14 +- .../Eip7928/NonceChangeDecoder.cs | 14 +- .../Eip7928/SlotChangesDecoder.cs | 18 ++- 6 files changed, 124 insertions(+), 84 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 47da196ce41..779480a8a51 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -43,7 +43,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe } public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors).Total); + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -57,90 +57,96 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - ContentLengths contentLengths = GetContentLength(item, rlpBehaviors); - stream.StartSequence(contentLengths.Total); + int len = GetContentLength(item, rlpBehaviors); + stream.StartSequence(len); stream.Encode(item.Address); - - stream.StartSequence(contentLengths.SlotChanges); - foreach (SlotChanges slotChanges in item.StorageChanges.Values) - { - SlotChangesDecoder.Instance.Encode(stream, slotChanges); - } - - stream.StartSequence(contentLengths.StorageReads); - foreach (byte[] storageKey in item.StorageReads) - { - stream.Encode(storageKey); - } - - stream.StartSequence(contentLengths.BalanceChanges); - foreach (BalanceChange balanceChange in item.BalanceChanges) - { - BalanceChangeDecoder.Instance.Encode(stream, balanceChange); - } - - stream.StartSequence(contentLengths.NonceChanges); - foreach (NonceChange nonceChange in item.NonceChanges) - { - NonceChangeDecoder.Instance.Encode(stream, nonceChange); - } - - stream.StartSequence(contentLengths.CodeChanges); - foreach (CodeChange codeChange in item.CodeChanges) - { - CodeChangeDecoder.Instance.Encode(stream, codeChange); - } + stream.EncodeArray([.. item.StorageChanges], rlpBehaviors); + stream.EncodeArray([.. item.StorageReads], rlpBehaviors); + stream.EncodeArray([.. item.BalanceChanges], rlpBehaviors); + stream.EncodeArray([.. item.NonceChanges], rlpBehaviors); + stream.EncodeArray([.. item.CodeChanges], rlpBehaviors); + // stream.StartSequence(contentLengths.SlotChanges); + // foreach (SlotChanges slotChanges in item.StorageChanges.Values) + // { + // SlotChangesDecoder.Instance.Encode(stream, slotChanges); + // } + + // stream.StartSequence(contentLengths.StorageReads); + // foreach (byte[] storageKey in item.StorageReads) + // { + // stream.Encode(storageKey); + // } + + // stream.StartSequence(contentLengths.BalanceChanges); + // foreach (BalanceChange balanceChange in item.BalanceChanges) + // { + // BalanceChangeDecoder.Instance.Encode(stream, balanceChange); + // } + + // stream.StartSequence(contentLengths.NonceChanges); + // foreach (NonceChange nonceChange in item.NonceChanges) + // { + // NonceChangeDecoder.Instance.Encode(stream, nonceChange); + // } + + // stream.StartSequence(contentLengths.CodeChanges); + // foreach (CodeChange codeChange in item.CodeChanges) + // { + // CodeChangeDecoder.Instance.Encode(stream, codeChange); + // } } - private static ContentLengths GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) + private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) { - ContentLengths res = new() - { - Total = Rlp.LengthOfAddressRlp, - SlotChanges = 0, - StorageReads = 0, - BalanceChanges = 0, - NonceChanges = 0, - CodeChanges = 0 - }; - + // ContentLengths res = new() + // { + // Total = Rlp.LengthOfAddressRlp, + // SlotChanges = 0, + // StorageReads = 0, + // BalanceChanges = 0, + // NonceChanges = 0, + // CodeChanges = 0 + // }; + int slotChangesLen = 0; foreach (SlotChanges slotChanges in item.StorageChanges.Values) { - res.SlotChanges += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); + slotChangesLen += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); } - res.SlotChanges = Rlp.LengthOfSequence(res.SlotChanges); + slotChangesLen = Rlp.LengthOfSequence(slotChangesLen); - res.StorageReads = Rlp.LengthOfSequence(item.StorageReads.Count * 32); + int storageReadsLen = Rlp.LengthOfSequence(item.StorageReads.Count * 32); + int balanceChangesLen = 0; foreach (BalanceChange balanceChange in item.BalanceChanges) { - res.BalanceChanges += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); + balanceChangesLen += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); } - res.BalanceChanges = Rlp.LengthOfSequence(res.BalanceChanges); + balanceChangesLen = Rlp.LengthOfSequence(balanceChangesLen); + int nonceChangesLen = 0; foreach (NonceChange nonceChange in item.NonceChanges) { - res.NonceChanges += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); + nonceChangesLen += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); } - res.NonceChanges = Rlp.LengthOfSequence(res.NonceChanges); + nonceChangesLen = Rlp.LengthOfSequence(nonceChangesLen); + int codeChangesLen = 0; foreach (CodeChange codeChange in item.CodeChanges) { - res.CodeChanges += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); + codeChangesLen += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); } - res.CodeChanges = Rlp.LengthOfSequence(res.CodeChanges); + codeChangesLen = Rlp.LengthOfSequence(codeChangesLen); - res.Total += res.SlotChanges + res.StorageReads + res.BalanceChanges + res.NonceChanges + res.CodeChanges; - return res; + return slotChangesLen + storageReadsLen + balanceChangesLen + nonceChangesLen + codeChangesLen; } - private struct ContentLengths - { - public int Total; - public int SlotChanges; - public int StorageReads; - public int BalanceChanges; - public int NonceChanges; - public int CodeChanges; - } + // private struct ContentLengths + // { + // public int Total; + // public int SlotChanges; + // public int StorageReads; + // public int BalanceChanges; + // public int NonceChanges; + // public int CodeChanges; + // } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index c1699f77988..791e70c2dd1 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -15,12 +15,24 @@ public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + BalanceChange balanceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), PostBalance = ctx.DecodeUInt256() }; + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return balanceChange; + } + public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index ec385d2011a..5f9f785abd0 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -15,7 +15,7 @@ public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStr public static BlockAccessListDecoder Instance => _instance ??= new(); public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + => GetContentLength(item, rlpBehaviors); public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { @@ -39,12 +39,12 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(GetContentLength(item, rlpBehaviors)); - - foreach (AccountChanges accountChanges in item.AccountChanges.Values) - { - AccountChangesDecoder.Instance.Encode(stream, accountChanges); - } + // stream.StartSequence(GetContentLength(item, rlpBehaviors)); + stream.EncodeArray([.. item.AccountChanges.Values]); + // foreach (AccountChanges accountChanges in item.AccountChanges.Values) + // { + // AccountChangesDecoder.Instance.Encode(stream, accountChanges); + // } } private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 61519946373..ae1d518372a 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -12,12 +12,24 @@ public class CodeChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder public static CodeChangeDecoder Instance => _instance ??= new(); public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + CodeChange codeChange = new() { BlockAccessIndex = ctx.DecodeUShort(), NewCode = ctx.DecodeByteArray() }; + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return codeChange; + } + public int GetLength(CodeChange item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index c5e06bc473d..4b98c9545db 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -12,12 +12,24 @@ public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecod public static NonceChangeDecoder Instance => _instance ??= new(); public NonceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) - => new() + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + NonceChange nonceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), NewNonce = ctx.DecodeULong() }; + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return nonceChange; + } + public int GetLength(NonceChange item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index dca45d3642b..40d94e36d01 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -37,7 +37,7 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav } public int GetLength(SlotChanges item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors).Total); + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { @@ -51,8 +51,7 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - (int Total, int Slot, int SlotChanges) = GetContentLength(item, rlpBehaviors); - stream.StartSequence(Total); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); // stream.StartSequence(Slot); stream.Encode(item.Slot); @@ -62,20 +61,19 @@ public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors // { // StorageChangeDecoder.Instance.Encode(stream, change); // } - stream.EncodeArray([.. item.Changes], rlpBehaviors); + stream.EncodeArray([.. item.Changes], rlpBehaviors); } - public static (int Total, int Slot, int SlotChanges) GetContentLength(SlotChanges item, RlpBehaviors rlpBehaviors) + public static int GetContentLength(SlotChanges item, RlpBehaviors rlpBehaviors) { - int slotChangeLen = 0; + int storageChangesLen = 0; foreach (StorageChange slotChange in item.Changes) { - slotChangeLen += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); + storageChangesLen += StorageChangeDecoder.Instance.GetLength(slotChange, rlpBehaviors); } + storageChangesLen = Rlp.LengthOfSequence(storageChangesLen); - int slot = Rlp.LengthOf(item.Slot); - slotChangeLen = Rlp.LengthOfSequence(slotChangeLen); - return (slotChangeLen + slot, slot, slotChangeLen); + return storageChangesLen + Rlp.LengthOf(item.Slot); } } From 143ff10ba09e037445b26337c93c42b2b0c3bd7a Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 13:18:47 +0100 Subject: [PATCH 042/215] encoding tests and more fixes --- .../BlockAccessListTests.cs | 79 +++++++++++++++++-- .../Eip7928/AccountChangesDecoder.cs | 15 ++-- .../Eip7928/CodeChangeDecoder.cs | 2 +- 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index a5ff14ee5d2..8fa47d1042a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -98,22 +98,85 @@ public void Balance_and_nonce_changes() [Test] public void Encoding_decoding_test() { - StorageChange s = new() + StorageChange storageChange = new() { BlockAccessIndex = 10, NewValue = [.. Enumerable.Repeat(50, 32)] }; - byte[] b = Rlp.Encode(s, RlpBehaviors.None).Bytes; - StorageChange s2 = Rlp.Decode(b, RlpBehaviors.None); + byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; + StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); + using (Assert.EnterMultipleScope()) + { + Assert.That(storageChange.BlockAccessIndex, Is.EqualTo(storageChangeDecoded.BlockAccessIndex)); + Assert.That(storageChange.NewValue, Is.EquivalentTo(storageChangeDecoded.NewValue)); + } - SlotChanges sc = new() + SlotChanges slotChanges = new() { Slot = [.. Enumerable.Repeat(100, 32)], - Changes = [s, s] + Changes = [storageChange, storageChange] + }; + byte[] slotChangesBytes = Rlp.Encode(slotChanges, RlpBehaviors.None).Bytes; + SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); + Assert.That(slotChanges.Slot, Is.EquivalentTo(slotChangesDecoded.Slot)); + + BalanceChange balanceChange = new() + { + BlockAccessIndex = 10, + PostBalance = 0 + }; + byte[] balanceChangeBytes = Rlp.Encode(balanceChange, RlpBehaviors.None).Bytes; + BalanceChange balanceChangeDecoded = Rlp.Decode(balanceChangeBytes, RlpBehaviors.None); + using (Assert.EnterMultipleScope()) + { + Assert.That(balanceChange.BlockAccessIndex, Is.EqualTo(balanceChangeDecoded.BlockAccessIndex)); + Assert.That(balanceChange.PostBalance, Is.EqualTo(balanceChangeDecoded.PostBalance)); + } + + NonceChange nonceChange = new() + { + BlockAccessIndex = 10, + NewNonce = 0 }; - byte[] b2 = Rlp.Encode(sc, RlpBehaviors.None).Bytes; - SlotChanges sc2 = Rlp.Decode(b2, RlpBehaviors.None); - Assert.That(true); + byte[] nonceChangeBytes = Rlp.Encode(nonceChange, RlpBehaviors.None).Bytes; + NonceChange nonceChangeDecoded = Rlp.Decode(nonceChangeBytes, RlpBehaviors.None); + using (Assert.EnterMultipleScope()) + { + Assert.That(nonceChange.BlockAccessIndex, Is.EqualTo(nonceChangeDecoded.BlockAccessIndex)); + Assert.That(nonceChange.NewNonce, Is.EqualTo(nonceChangeDecoded.NewNonce)); + } + + CodeChange codeChange = new() + { + BlockAccessIndex = 10, + NewCode = [0, 50] + }; + byte[] codeChangeBytes = Rlp.Encode(codeChange, RlpBehaviors.None).Bytes; + CodeChange codeChangeDecoded = Rlp.Decode(codeChangeBytes, RlpBehaviors.None); + using (Assert.EnterMultipleScope()) + { + Assert.That(codeChange.BlockAccessIndex, Is.EqualTo(codeChangeDecoded.BlockAccessIndex)); + Assert.That(codeChange.NewCode, Is.EqualTo(codeChangeDecoded.NewCode)); + } + + SortedDictionary storageChanges = new() + { + { slotChanges.Slot, slotChanges } + }; + + AccountChanges accountChanges = new() + { + Address = TestItem.AddressA.Bytes, + StorageChanges = storageChanges, + StorageReads = [[.. Enumerable.Repeat(50, 32)]], + BalanceChanges = [balanceChange, balanceChange], + NonceChanges = [nonceChange, nonceChange], + CodeChanges = [codeChange, codeChange] + }; + byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; + AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); + Assert.That(accountChanges.Address, Is.EquivalentTo(accountChangesDecoded.Address)); + // BlockAccessList b = new(); // byte[] bytes = Rlp.Encode(b).Bytes; // BlockAccessList b2 = Rlp.Decode(bytes); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 779480a8a51..397fcac2bb3 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -57,10 +57,10 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - int len = GetContentLength(item, rlpBehaviors); - stream.StartSequence(len); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); + // stream.StartSequence(Rlp.LengthOfAddressRlp); stream.Encode(item.Address); - stream.EncodeArray([.. item.StorageChanges], rlpBehaviors); + stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); stream.EncodeArray([.. item.BalanceChanges], rlpBehaviors); stream.EncodeArray([.. item.NonceChanges], rlpBehaviors); @@ -114,7 +114,12 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio } slotChangesLen = Rlp.LengthOfSequence(slotChangesLen); - int storageReadsLen = Rlp.LengthOfSequence(item.StorageReads.Count * 32); + int storageReadsLen = 0; + foreach (byte[] storageRead in item.StorageReads) + { + storageReadsLen += Rlp.LengthOf(storageRead); + } + storageReadsLen = Rlp.LengthOfSequence(storageReadsLen); int balanceChangesLen = 0; foreach (BalanceChange balanceChange in item.BalanceChanges) @@ -137,7 +142,7 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio } codeChangesLen = Rlp.LengthOfSequence(codeChangesLen); - return slotChangesLen + storageReadsLen + balanceChangesLen + nonceChangesLen + codeChangesLen; + return Rlp.LengthOfAddressRlp + slotChangesLen + storageReadsLen + balanceChangesLen + nonceChangesLen + codeChangesLen; } // private struct ContentLengths diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index ae1d518372a..eb1b2f74258 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -45,7 +45,7 @@ public CodeChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, CodeChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(GetLength(item, rlpBehaviors)); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); stream.Encode(item.NewCode); } From 795c35dedb2cf4552491b6edd211487edcad9bd2 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 14:31:35 +0100 Subject: [PATCH 043/215] storage read encoding, complete bal encoding --- .../BlockAccessListTests.cs | 32 ++++++++--- .../Tracing/BlockAccessTracer.cs | 7 ++- .../Nethermind.Core/BlockAccessList.cs | 7 ++- .../Eip7928/AccountChangesDecoder.cs | 6 +-- .../Eip7928/BlockAccessListDecoder.cs | 18 +++++-- .../Eip7928/StorageReadDecoder.cs | 53 +++++++++++++++++++ 6 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 8fa47d1042a..5e578fb9bf0 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -94,9 +94,8 @@ public void Balance_and_nonce_changes() } } - // tmp [Test] - public void Encoding_decoding_test() + public void Can_encode_and_decode() { StorageChange storageChange = new() { @@ -120,6 +119,14 @@ public void Encoding_decoding_test() SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); Assert.That(slotChanges.Slot, Is.EquivalentTo(slotChangesDecoded.Slot)); + StorageRead storageRead = new() + { + Key = [.. Enumerable.Repeat(50, 32)] + }; + byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; + StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); + Assert.That(storageRead.Key, Is.EquivalentTo(storageReadDecoded.Key)); + BalanceChange balanceChange = new() { BlockAccessIndex = 10, @@ -159,7 +166,7 @@ public void Encoding_decoding_test() Assert.That(codeChange.NewCode, Is.EqualTo(codeChangeDecoded.NewCode)); } - SortedDictionary storageChanges = new() + SortedDictionary storageChangesDict = new() { { slotChanges.Slot, slotChanges } }; @@ -167,8 +174,8 @@ public void Encoding_decoding_test() AccountChanges accountChanges = new() { Address = TestItem.AddressA.Bytes, - StorageChanges = storageChanges, - StorageReads = [[.. Enumerable.Repeat(50, 32)]], + StorageChanges = storageChangesDict, + StorageReads = [storageRead, storageRead], BalanceChanges = [balanceChange, balanceChange], NonceChanges = [nonceChange, nonceChange], CodeChanges = [codeChange, codeChange] @@ -177,9 +184,18 @@ public void Encoding_decoding_test() AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); Assert.That(accountChanges.Address, Is.EquivalentTo(accountChangesDecoded.Address)); - // BlockAccessList b = new(); - // byte[] bytes = Rlp.Encode(b).Bytes; - // BlockAccessList b2 = Rlp.Decode(bytes); + SortedDictionary accountChangesDict = new() + { + { new(accountChanges.Address), accountChanges } + }; + + BlockAccessList blockAccessList = new() + { + AccountChanges = accountChangesDict + }; + byte[] blockAccessListBytes = Rlp.Encode(blockAccessList, RlpBehaviors.None).Bytes; + BlockAccessList blockAccessListDecoded = Rlp.Decode(blockAccessListBytes, RlpBehaviors.None); + Assert.That(blockAccessList.AccountChanges, Has.Count.EqualTo(blockAccessListDecoded.AccountChanges.Count)); } [Test] diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index a3894486f6a..0f973ed0986 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -179,7 +179,10 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ public void ReportStorageRead(in StorageCell storageCell) { - byte[] storageKey = storageCell.Hash.ToByteArray(); + StorageRead storageRead = new() + { + Key = storageCell.Hash.ToByteArray() + }; Address address = storageCell.Address; if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) @@ -188,7 +191,7 @@ public void ReportStorageRead(in StorageCell storageCell) _bal.AccountChanges.Add(address, accountChanges); } - accountChanges.StorageReads.Add(storageKey); + accountChanges.StorageReads.Add(storageRead); } public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) { } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList.cs index 160a3a0e79f..c5f43711755 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList.cs @@ -15,6 +15,11 @@ public struct StorageChange public byte[] NewValue { get; set; } } +public struct StorageRead +{ + public byte[] Key { get; set; } +} + // BalanceChange: [block_access_index, post_balance] public struct BalanceChange { @@ -66,7 +71,7 @@ public struct AccountChanges(Address address) // Read-only storage keys // [SszList(Eip7928Constants.MaxSlots)] - public List StorageReads { get; set; } = []; + public List StorageReads { get; set; } = []; // Balance changes ([tx_index -> post_balance]) // [SszList(Eip7928Constants.MaxTxs)] diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 397fcac2bb3..ee6ecac9c56 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -21,7 +21,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe byte[] address = ctx.DecodeAddress().Bytes; SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s)); - byte[][] storageReads = ctx.DecodeByteArrays(); + StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); @@ -115,9 +115,9 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio slotChangesLen = Rlp.LengthOfSequence(slotChangesLen); int storageReadsLen = 0; - foreach (byte[] storageRead in item.StorageReads) + foreach (StorageRead storageRead in item.StorageReads) { - storageReadsLen += Rlp.LengthOf(storageRead); + storageReadsLen += StorageReadDecoder.Instance.GetLength(storageRead, rlpBehaviors); } storageReadsLen = Rlp.LengthOfSequence(storageReadsLen); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 5f9f785abd0..a80447e59d4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -15,16 +15,26 @@ public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStr public static BlockAccessListDecoder Instance => _instance ??= new(); public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) - => GetContentLength(item, rlpBehaviors); + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => new Address(a.Address), a => a)); - return new() + BlockAccessList blockAccessList = new() { AccountChanges = accountChangesMap }; + + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return blockAccessList; } public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) @@ -39,7 +49,7 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - // stream.StartSequence(GetContentLength(item, rlpBehaviors)); + stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.EncodeArray([.. item.AccountChanges.Values]); // foreach (AccountChanges accountChanges in item.AccountChanges.Values) // { @@ -56,6 +66,6 @@ private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehavi len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); } - return len; + return Rlp.LengthOfSequence(len); } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs new file mode 100644 index 00000000000..f9e9b68bc6a --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.BlockAccessLists; + +namespace Nethermind.Serialization.Rlp.Eip7928; + +public class StorageReadDecoder : IRlpValueDecoder, IRlpStreamDecoder +{ + private static StorageReadDecoder? _instance = null; + public static StorageReadDecoder Instance => _instance ??= new(); + + public int GetLength(StorageRead item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + + public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) + { + int length = ctx.ReadSequenceLength(); + int check = length + ctx.Position; + + StorageRead storageRead = new() + { + Key = ctx.DecodeByteArray(), + }; + + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + ctx.Check(check); + } + + return storageRead; + } + + public StorageRead Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) + { + Span span = rlpStream.PeekNextItem(); + Rlp.ValueDecoderContext ctx = new(span); + StorageRead res = Decode(ref ctx, rlpBehaviors); + rlpStream.SkipItem(); + + return res; + } + + public void Encode(RlpStream stream, StorageRead item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.StartSequence(GetContentLength(item, rlpBehaviors)); + stream.Encode(item.Key); + } + + public static int GetContentLength(StorageRead item, RlpBehaviors rlpBehaviors) + => Rlp.LengthOf(item.Key); +} From 8ac9f4635045eb3b72db081bdf5e776e8d92ff4e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:48:04 +0100 Subject: [PATCH 044/215] tidy --- .../BlockAccessListTests.cs | 5 +- .../{ => BlockAccessList}/BlockAccessList.cs | 22 +-------- .../Eip7928/AccountChangesDecoder.cs | 49 ------------------- .../Eip7928/BlockAccessListDecoder.cs | 4 -- .../Eip7928/SlotChangesDecoder.cs | 11 ----- 5 files changed, 3 insertions(+), 88 deletions(-) rename src/Nethermind/Nethermind.Core/{ => BlockAccessList}/BlockAccessList.cs (75%) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 5e578fb9bf0..487d9a28e59 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -248,9 +248,8 @@ public void System_contracts_and_withdrawals() }; (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); - // tmp - SortedDictionary accountChanges = Rlp.Decode(processedBlock.BlockAccessList).AccountChanges; - // SortedDictionary accountChanges = processedBlock.DecodedBlockAccessList!.Value.AccountChanges; + BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); + SortedDictionary accountChanges = blockAccessList.AccountChanges; Assert.That(accountChanges, Has.Count.EqualTo(3)); List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs similarity index 75% rename from src/Nethermind/Nethermind.Core/BlockAccessList.cs rename to src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs index c5f43711755..ea6313938c8 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs @@ -1,17 +1,15 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Collections.Generic; using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; -// # StorageChange: [block_access_index, new_value] +// StorageChange: [block_access_index, new_value] public struct StorageChange { public ushort BlockAccessIndex { get; set; } - // [SszVector(32)] public byte[] NewValue { get; set; } } @@ -38,8 +36,6 @@ public struct NonceChange public struct CodeChange { public ushort BlockAccessIndex { get; set; } - - // [SszList(Eip7928Constants.MaxCodeSize)] public byte[] NewCode { get; set; } } @@ -47,47 +43,31 @@ public struct CodeChange // All changes to a single storage slot public struct SlotChanges() { - // [SszVector(32)] public byte[] Slot { get; set; } = []; - - // [SszList(Eip7928Constants.MaxTxs)] public List Changes { get; set; } = []; } -// public struct StorageKey(ReadOnlySpan key) -// { -// // [SszVector(32)] -// public byte[] Key { get; set; } = key.ToArray(); -// } - public struct AccountChanges(Address address) { - // [SszVector(20)] public byte[] Address { get; set; } = address.Bytes; // Storage changes (slot -> [tx_index -> new_value]) - // [SszList(Eip7928Constants.MaxSlots)] public SortedDictionary StorageChanges { get; set; } = []; // Read-only storage keys - // [SszList(Eip7928Constants.MaxSlots)] public List StorageReads { get; set; } = []; // Balance changes ([tx_index -> post_balance]) - // [SszList(Eip7928Constants.MaxTxs)] public List BalanceChanges { get; set; } = []; // Nonce changes ([tx_index -> new_nonce]) - // [SszList(Eip7928Constants.MaxTxs)] public List NonceChanges { get; set; } = []; // Code changes ([tx_index -> new_code]) - // [SszList(Eip7928Constants.MaxCodeChanges)] public List CodeChanges { get; set; } = []; } public struct BlockAccessList() { - // [SszList(Eip7928Constants.MaxAccounts)] public SortedDictionary AccountChanges { get; set; } = []; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index ee6ecac9c56..94ae3f1df5c 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -58,55 +58,16 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - // stream.StartSequence(Rlp.LengthOfAddressRlp); stream.Encode(item.Address); stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); stream.EncodeArray([.. item.BalanceChanges], rlpBehaviors); stream.EncodeArray([.. item.NonceChanges], rlpBehaviors); stream.EncodeArray([.. item.CodeChanges], rlpBehaviors); - // stream.StartSequence(contentLengths.SlotChanges); - // foreach (SlotChanges slotChanges in item.StorageChanges.Values) - // { - // SlotChangesDecoder.Instance.Encode(stream, slotChanges); - // } - - // stream.StartSequence(contentLengths.StorageReads); - // foreach (byte[] storageKey in item.StorageReads) - // { - // stream.Encode(storageKey); - // } - - // stream.StartSequence(contentLengths.BalanceChanges); - // foreach (BalanceChange balanceChange in item.BalanceChanges) - // { - // BalanceChangeDecoder.Instance.Encode(stream, balanceChange); - // } - - // stream.StartSequence(contentLengths.NonceChanges); - // foreach (NonceChange nonceChange in item.NonceChanges) - // { - // NonceChangeDecoder.Instance.Encode(stream, nonceChange); - // } - - // stream.StartSequence(contentLengths.CodeChanges); - // foreach (CodeChange codeChange in item.CodeChanges) - // { - // CodeChangeDecoder.Instance.Encode(stream, codeChange); - // } } private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) { - // ContentLengths res = new() - // { - // Total = Rlp.LengthOfAddressRlp, - // SlotChanges = 0, - // StorageReads = 0, - // BalanceChanges = 0, - // NonceChanges = 0, - // CodeChanges = 0 - // }; int slotChangesLen = 0; foreach (SlotChanges slotChanges in item.StorageChanges.Values) { @@ -144,14 +105,4 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio return Rlp.LengthOfAddressRlp + slotChangesLen + storageReadsLen + balanceChangesLen + nonceChangesLen + codeChangesLen; } - - // private struct ContentLengths - // { - // public int Total; - // public int SlotChanges; - // public int StorageReads; - // public int BalanceChanges; - // public int NonceChanges; - // public int CodeChanges; - // } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index a80447e59d4..02b9228b1d2 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -51,10 +51,6 @@ public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehav { stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.EncodeArray([.. item.AccountChanges.Values]); - // foreach (AccountChanges accountChanges in item.AccountChanges.Values) - // { - // AccountChangesDecoder.Instance.Encode(stream, accountChanges); - // } } private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 40d94e36d01..719d80cc060 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -19,9 +19,6 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav byte[] slot = ctx.DecodeByteArray(); - // int changesLength = ctx.ReadSequenceLength(); - // int changesCheck = length + ctx.Position; - SlotChanges slotChanges = new() { Slot = slot, @@ -52,15 +49,7 @@ public SlotChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, SlotChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - - // stream.StartSequence(Slot); stream.Encode(item.Slot); - - // stream.StartSequence(SlotChanges); - // foreach (StorageChange change in item.Changes) - // { - // StorageChangeDecoder.Instance.Encode(stream, change); - // } stream.EncodeArray([.. item.Changes], rlpBehaviors); } From 5f9618448baec85b599006c37d6d9ef243ec59a6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:50:30 +0100 Subject: [PATCH 045/215] more tidying --- .../BlockAccessList/BlockAccessList.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs index ea6313938c8..cd0931934ea 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs @@ -6,7 +6,6 @@ namespace Nethermind.Core.BlockAccessLists; -// StorageChange: [block_access_index, new_value] public struct StorageChange { public ushort BlockAccessIndex { get; set; } @@ -18,29 +17,24 @@ public struct StorageRead public byte[] Key { get; set; } } -// BalanceChange: [block_access_index, post_balance] public struct BalanceChange { public ushort BlockAccessIndex { get; set; } public UInt256 PostBalance { get; set; } } -// NonceChange: [block_access_index, new_nonce] public struct NonceChange { public ushort BlockAccessIndex { get; set; } public ulong NewNonce { get; set; } } -// CodeChange: [block_access_index, new_code] public struct CodeChange { public ushort BlockAccessIndex { get; set; } public byte[] NewCode { get; set; } } -// SlotChanges: [slot, [changes]] -// All changes to a single storage slot public struct SlotChanges() { public byte[] Slot { get; set; } = []; @@ -50,20 +44,10 @@ public struct SlotChanges() public struct AccountChanges(Address address) { public byte[] Address { get; set; } = address.Bytes; - - // Storage changes (slot -> [tx_index -> new_value]) public SortedDictionary StorageChanges { get; set; } = []; - - // Read-only storage keys public List StorageReads { get; set; } = []; - - // Balance changes ([tx_index -> post_balance]) public List BalanceChanges { get; set; } = []; - - // Nonce changes ([tx_index -> new_nonce]) public List NonceChanges { get; set; } = []; - - // Code changes ([tx_index -> new_code]) public List CodeChanges { get; set; } = []; } From 3cd7a1a0bc8def99ee7e43c1222deb16e5c3d1a0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 17:57:41 +0100 Subject: [PATCH 046/215] use specific types --- .../BlockAccessListTests.cs | 14 +++++++------- .../Tracing/BlockAccessTracer.cs | 4 ++-- .../BlockAccessList/BlockAccessList.cs | 6 +++--- .../Eip7928/AccountChangesDecoder.cs | 3 ++- .../Eip7928/BlockAccessListDecoder.cs | 2 +- .../Eip7928/StorageChangeDecoder.cs | 6 +++--- .../Eip7928/StorageReadDecoder.cs | 6 +++--- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 487d9a28e59..9df63a3b74a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -100,14 +100,14 @@ public void Can_encode_and_decode() StorageChange storageChange = new() { BlockAccessIndex = 10, - NewValue = [.. Enumerable.Repeat(50, 32)] + NewValue = new([.. Enumerable.Repeat(50, 32)]) }; byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); using (Assert.EnterMultipleScope()) { Assert.That(storageChange.BlockAccessIndex, Is.EqualTo(storageChangeDecoded.BlockAccessIndex)); - Assert.That(storageChange.NewValue, Is.EquivalentTo(storageChangeDecoded.NewValue)); + Assert.That(storageChange.NewValue, Is.EqualTo(storageChangeDecoded.NewValue)); } SlotChanges slotChanges = new() @@ -121,11 +121,11 @@ public void Can_encode_and_decode() StorageRead storageRead = new() { - Key = [.. Enumerable.Repeat(50, 32)] + Key = new([.. Enumerable.Repeat(50, 32)]) }; byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); - Assert.That(storageRead.Key, Is.EquivalentTo(storageReadDecoded.Key)); + Assert.That(storageRead.Key, Is.EqualTo(storageReadDecoded.Key)); BalanceChange balanceChange = new() { @@ -173,7 +173,7 @@ public void Can_encode_and_decode() AccountChanges accountChanges = new() { - Address = TestItem.AddressA.Bytes, + Address = TestItem.AddressA, StorageChanges = storageChangesDict, StorageReads = [storageRead, storageRead], BalanceChanges = [balanceChange, balanceChange], @@ -182,11 +182,11 @@ public void Can_encode_and_decode() }; byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); - Assert.That(accountChanges.Address, Is.EquivalentTo(accountChangesDecoded.Address)); + Assert.That(accountChanges.Address, Is.EqualTo(accountChangesDecoded.Address)); SortedDictionary accountChangesDict = new() { - { new(accountChanges.Address), accountChanges } + { accountChanges.Address, accountChanges } }; BlockAccessList blockAccessList = new() diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 0f973ed0986..8c573b63bd0 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -181,7 +181,7 @@ public void ReportStorageRead(in StorageCell storageCell) { StorageRead storageRead = new() { - Key = storageCell.Hash.ToByteArray() + Key = new(storageCell.Hash.ToByteArray()) }; Address address = storageCell.Address; @@ -301,7 +301,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan StorageChange storageChange = new() { BlockAccessIndex = _blockAccessIndex, - NewValue = value.ToArray() + NewValue = new(value.ToArray()) }; byte[] storageKey = [.. key]; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs index cd0931934ea..506bc275522 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs @@ -9,12 +9,12 @@ namespace Nethermind.Core.BlockAccessLists; public struct StorageChange { public ushort BlockAccessIndex { get; set; } - public byte[] NewValue { get; set; } + public Bytes32 NewValue { get; set; } } public struct StorageRead { - public byte[] Key { get; set; } + public Bytes32 Key { get; set; } } public struct BalanceChange @@ -43,7 +43,7 @@ public struct SlotChanges() public struct AccountChanges(Address address) { - public byte[] Address { get; set; } = address.Bytes; + public Address Address { get; set; } = address; public SortedDictionary StorageChanges { get; set; } = []; public List StorageReads { get; set; } = []; public List BalanceChanges { get; set; } = []; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 94ae3f1df5c..9a215e41d45 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -18,7 +19,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - byte[] address = ctx.DecodeAddress().Bytes; + Address address = ctx.DecodeAddress(); SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s)); StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 02b9228b1d2..5d1da0a7c80 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -23,7 +23,7 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB int check = length + ctx.Position; AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); - SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => new Address(a.Address), a => a)); + SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => a.Address, a => a)); BlockAccessList blockAccessList = new() { AccountChanges = accountChangesMap diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 72a8c77dacd..361ba60efb7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -19,7 +19,7 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh StorageChange storageChange = new() { BlockAccessIndex = ctx.DecodeUShort(), - NewValue = ctx.DecodeByteArray() + NewValue = new(ctx.DecodeByteArray()) }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) @@ -47,9 +47,9 @@ public void Encode(RlpStream stream, StorageChange item, RlpBehaviors rlpBehavio { stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); - stream.Encode(item.NewValue); + stream.Encode(item.NewValue.Unwrap()); } public static int GetContentLength(StorageChange item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewValue); + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewValue.Unwrap()); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index f9e9b68bc6a..ca3bbe267b2 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -21,7 +21,7 @@ public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav StorageRead storageRead = new() { - Key = ctx.DecodeByteArray(), + Key = new(ctx.DecodeByteArray()), }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) @@ -45,9 +45,9 @@ public StorageRead Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, StorageRead item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - stream.Encode(item.Key); + stream.Encode(item.Key.Unwrap()); } public static int GetContentLength(StorageRead item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOf(item.Key); + => Rlp.LengthOf(item.Key.Unwrap()); } From 9a7429b3032be9fd31422735147af431d681bf56 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:20:32 +0100 Subject: [PATCH 047/215] check length is 32 bytes --- .../Eip7928/SlotChangesDecoder.cs | 4 ++++ .../Eip7928/StorageChangeDecoder.cs | 8 +++++++- .../Eip7928/StorageReadDecoder.cs | 8 +++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 719d80cc060..28be224fd5a 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -18,6 +18,10 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav int check = length + ctx.Position; byte[] slot = ctx.DecodeByteArray(); + if (slot.Length != 32) + { + throw new RlpException("Invalid storage key, should be 32 bytes."); + } SlotChanges slotChanges = new() { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 361ba60efb7..78a2f45047e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -16,10 +16,16 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + byte[] newValue = ctx.DecodeByteArray(); + if (newValue.Length != 32) + { + throw new RlpException("Invalid storage value, should be 32 bytes."); + } + StorageChange storageChange = new() { BlockAccessIndex = ctx.DecodeUShort(), - NewValue = new(ctx.DecodeByteArray()) + NewValue = new(newValue) }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index ca3bbe267b2..0d2864aef5f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -19,9 +19,15 @@ public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + byte[] key = ctx.DecodeByteArray(); + if (key.Length != 32) + { + throw new RlpException("Invalid storage key, should be 32 bytes."); + } + StorageRead storageRead = new() { - Key = new(ctx.DecodeByteArray()), + Key = new(key), }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) From 4b1aec5061e8aa62340ef5e67923b1c555b08b72 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 29 Aug 2025 18:34:01 +0100 Subject: [PATCH 048/215] check against maximums --- .../Eip7928/AccountChangesDecoder.cs | 5 +++++ .../Eip7928/BlockAccessListDecoder.cs | 5 +++++ .../Eip7928/CodeChangeDecoder.cs | 9 ++++++++- .../Eip7928/SlotChangesDecoder.cs | 9 ++++++++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 9a215e41d45..12ff0137830 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -27,6 +27,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); + if (codeChanges.Length > Eip7928Constants.MaxCodeChanges) + { + throw new RlpException("Number of code changes exceeded maximum."); + } + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) { ctx.Check(check); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 5d1da0a7c80..1fd7ff401cc 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -23,6 +23,11 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB int check = length + ctx.Position; AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); + if (accountChanges.Length > Eip7928Constants.MaxAccounts) + { + throw new RlpException("Number of accounts exceeded maximum."); + } + SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => a.Address, a => a)); BlockAccessList blockAccessList = new() { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index eb1b2f74258..f06ad126627 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -16,10 +17,16 @@ public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehavi int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + byte[] newCode = ctx.DecodeByteArray(); + if (newCode.Length > Eip7928Constants.MaxCodeSize) + { + throw new RlpException("New code exceeded maxium length."); + } + CodeChange codeChange = new() { BlockAccessIndex = ctx.DecodeUShort(), - NewCode = ctx.DecodeByteArray() + NewCode = newCode }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 28be224fd5a..11a270e9936 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Nethermind.Core; using Nethermind.Core.BlockAccessLists; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -23,10 +24,16 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav throw new RlpException("Invalid storage key, should be 32 bytes."); } + StorageChange[] changes = ctx.DecodeArray(StorageChangeDecoder.Instance); + if (changes.Length > Eip7928Constants.MaxSlots) + { + throw new RlpException("Number of slot changes exceeded maximum."); + } + SlotChanges slotChanges = new() { Slot = slot, - Changes = [.. ctx.DecodeArray(StorageChangeDecoder.Instance)] + Changes = [.. changes] }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) From 723eacaff58424ba4e83dbcb09bb3241531207df Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:44:11 +0100 Subject: [PATCH 049/215] fix decoding & test --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 2 +- .../Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs | 3 ++- .../Eip7928/StorageChangeDecoder.cs | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 9df63a3b74a..2be65d7ae63 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -178,7 +178,7 @@ public void Can_encode_and_decode() StorageReads = [storageRead, storageRead], BalanceChanges = [balanceChange, balanceChange], NonceChanges = [nonceChange, nonceChange], - CodeChanges = [codeChange, codeChange] + CodeChanges = [codeChange] }; byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index f06ad126627..4eedac27e1b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -17,6 +17,7 @@ public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehavi int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + ushort blockAccessIndex = ctx.DecodeUShort(); byte[] newCode = ctx.DecodeByteArray(); if (newCode.Length > Eip7928Constants.MaxCodeSize) { @@ -25,7 +26,7 @@ public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehavi CodeChange codeChange = new() { - BlockAccessIndex = ctx.DecodeUShort(), + BlockAccessIndex = blockAccessIndex, NewCode = newCode }; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 78a2f45047e..18c894012da 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -16,6 +16,7 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + ushort blockAccessIndex = ctx.DecodeUShort(); byte[] newValue = ctx.DecodeByteArray(); if (newValue.Length != 32) { @@ -24,7 +25,7 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh StorageChange storageChange = new() { - BlockAccessIndex = ctx.DecodeUShort(), + BlockAccessIndex = blockAccessIndex, NewValue = new(newValue) }; From 511bd95b386ec234034ad4156c765022d7831563 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 12:35:56 +0100 Subject: [PATCH 050/215] equatable in tests, tidy up structs --- .../BlockAccessListTests.cs | 37 +++--------- .../BlockAccessList/BlockAccessList.cs | 57 ------------------- .../BlockAccessLists/AccountChanges.cs | 53 +++++++++++++++++ .../BlockAccessLists/BalanceChange.cs | 29 ++++++++++ .../BlockAccessLists/BlockAccessList.cs | 37 ++++++++++++ .../BlockAccessLists/CodeChange.cs | 39 +++++++++++++ .../BlockAccessLists/NonceChange.cs | 28 +++++++++ .../BlockAccessLists/SlotChanges.cs | 44 ++++++++++++++ .../BlockAccessLists/StorageChange.cs | 30 ++++++++++ .../BlockAccessLists/StorageRead.cs | 27 +++++++++ 10 files changed, 296 insertions(+), 85 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 2be65d7ae63..5150158bb9c 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -104,11 +104,7 @@ public void Can_encode_and_decode() }; byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); - using (Assert.EnterMultipleScope()) - { - Assert.That(storageChange.BlockAccessIndex, Is.EqualTo(storageChangeDecoded.BlockAccessIndex)); - Assert.That(storageChange.NewValue, Is.EqualTo(storageChangeDecoded.NewValue)); - } + Assert.That(storageChange, Is.EqualTo(storageChangeDecoded)); SlotChanges slotChanges = new() { @@ -117,15 +113,12 @@ public void Can_encode_and_decode() }; byte[] slotChangesBytes = Rlp.Encode(slotChanges, RlpBehaviors.None).Bytes; SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); - Assert.That(slotChanges.Slot, Is.EquivalentTo(slotChangesDecoded.Slot)); + Assert.That(slotChanges, Is.EqualTo(slotChangesDecoded)); - StorageRead storageRead = new() - { - Key = new([.. Enumerable.Repeat(50, 32)]) - }; + StorageRead storageRead = new(new Bytes32([.. Enumerable.Repeat(50, 32)])); byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); - Assert.That(storageRead.Key, Is.EqualTo(storageReadDecoded.Key)); + Assert.That(storageRead, Is.EqualTo(storageReadDecoded)); BalanceChange balanceChange = new() { @@ -134,11 +127,7 @@ public void Can_encode_and_decode() }; byte[] balanceChangeBytes = Rlp.Encode(balanceChange, RlpBehaviors.None).Bytes; BalanceChange balanceChangeDecoded = Rlp.Decode(balanceChangeBytes, RlpBehaviors.None); - using (Assert.EnterMultipleScope()) - { - Assert.That(balanceChange.BlockAccessIndex, Is.EqualTo(balanceChangeDecoded.BlockAccessIndex)); - Assert.That(balanceChange.PostBalance, Is.EqualTo(balanceChangeDecoded.PostBalance)); - } + Assert.That(balanceChange, Is.EqualTo(balanceChangeDecoded)); NonceChange nonceChange = new() { @@ -147,11 +136,7 @@ public void Can_encode_and_decode() }; byte[] nonceChangeBytes = Rlp.Encode(nonceChange, RlpBehaviors.None).Bytes; NonceChange nonceChangeDecoded = Rlp.Decode(nonceChangeBytes, RlpBehaviors.None); - using (Assert.EnterMultipleScope()) - { - Assert.That(nonceChange.BlockAccessIndex, Is.EqualTo(nonceChangeDecoded.BlockAccessIndex)); - Assert.That(nonceChange.NewNonce, Is.EqualTo(nonceChangeDecoded.NewNonce)); - } + Assert.That(nonceChange, Is.EqualTo(nonceChangeDecoded)); CodeChange codeChange = new() { @@ -160,11 +145,7 @@ public void Can_encode_and_decode() }; byte[] codeChangeBytes = Rlp.Encode(codeChange, RlpBehaviors.None).Bytes; CodeChange codeChangeDecoded = Rlp.Decode(codeChangeBytes, RlpBehaviors.None); - using (Assert.EnterMultipleScope()) - { - Assert.That(codeChange.BlockAccessIndex, Is.EqualTo(codeChangeDecoded.BlockAccessIndex)); - Assert.That(codeChange.NewCode, Is.EqualTo(codeChangeDecoded.NewCode)); - } + Assert.That(codeChange, Is.EqualTo(codeChangeDecoded)); SortedDictionary storageChangesDict = new() { @@ -182,7 +163,7 @@ public void Can_encode_and_decode() }; byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); - Assert.That(accountChanges.Address, Is.EqualTo(accountChangesDecoded.Address)); + Assert.That(accountChanges, Is.EqualTo(accountChangesDecoded)); SortedDictionary accountChangesDict = new() { @@ -195,7 +176,7 @@ public void Can_encode_and_decode() }; byte[] blockAccessListBytes = Rlp.Encode(blockAccessList, RlpBehaviors.None).Bytes; BlockAccessList blockAccessListDecoded = Rlp.Decode(blockAccessListBytes, RlpBehaviors.None); - Assert.That(blockAccessList.AccountChanges, Has.Count.EqualTo(blockAccessListDecoded.AccountChanges.Count)); + Assert.That(blockAccessList, Is.EqualTo(blockAccessListDecoded)); } [Test] diff --git a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs deleted file mode 100644 index 506bc275522..00000000000 --- a/src/Nethermind/Nethermind.Core/BlockAccessList/BlockAccessList.cs +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System.Collections.Generic; -using Nethermind.Int256; - -namespace Nethermind.Core.BlockAccessLists; - -public struct StorageChange -{ - public ushort BlockAccessIndex { get; set; } - public Bytes32 NewValue { get; set; } -} - -public struct StorageRead -{ - public Bytes32 Key { get; set; } -} - -public struct BalanceChange -{ - public ushort BlockAccessIndex { get; set; } - public UInt256 PostBalance { get; set; } -} - -public struct NonceChange -{ - public ushort BlockAccessIndex { get; set; } - public ulong NewNonce { get; set; } -} - -public struct CodeChange -{ - public ushort BlockAccessIndex { get; set; } - public byte[] NewCode { get; set; } -} - -public struct SlotChanges() -{ - public byte[] Slot { get; set; } = []; - public List Changes { get; set; } = []; -} - -public struct AccountChanges(Address address) -{ - public Address Address { get; set; } = address; - public SortedDictionary StorageChanges { get; set; } = []; - public List StorageReads { get; set; } = []; - public List BalanceChanges { get; set; } = []; - public List NonceChanges { get; set; } = []; - public List CodeChanges { get; set; } = []; -} - -public struct BlockAccessList() -{ - public SortedDictionary AccountChanges { get; set; } = []; -} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs new file mode 100644 index 00000000000..e883502c8a7 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -0,0 +1,53 @@ + +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct AccountChanges : IEquatable +{ + public Address Address { get; init; } + public SortedDictionary StorageChanges { get; init; } + public List StorageReads { get; init; } + public List BalanceChanges { get; init; } + public List NonceChanges { get; init; } + public List CodeChanges { get; init; } + + public AccountChanges(Address address) + { + Address = address; + StorageChanges = []; + StorageReads = []; + BalanceChanges = []; + NonceChanges = []; + CodeChanges = []; + } + + public AccountChanges(Address address, SortedDictionary storageChanges, List storageReads, List balanceChanges, List nonceChanges, List codeChanges) + { + Address = address; + StorageChanges = storageChanges; + StorageReads = storageReads; + BalanceChanges = balanceChanges; + NonceChanges = nonceChanges; + CodeChanges = codeChanges; + } + + public readonly bool Equals(AccountChanges other) => + Address == other.Address; + + public override readonly bool Equals(object? obj) => + obj is AccountChanges other && Equals(other); + + public override readonly int GetHashCode() => + Address.GetHashCode(); + + public static bool operator ==(AccountChanges left, AccountChanges right) => + left.Equals(right); + + public static bool operator !=(AccountChanges left, AccountChanges right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs new file mode 100644 index 00000000000..d2eb6fb32c3 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Int256; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct BalanceChange(ushort blockAccessIndex, UInt256 postBalance) : IEquatable +{ + public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + public UInt256 PostBalance { get; init; } = postBalance; + + public readonly bool Equals(BalanceChange other) => + BlockAccessIndex == other.BlockAccessIndex && + PostBalance == other.PostBalance; + + public override readonly bool Equals(object? obj) => + obj is BalanceChange other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(BlockAccessIndex, PostBalance); + + public static bool operator ==(BalanceChange left, BalanceChange right) => + left.Equals(right); + + public static bool operator !=(BalanceChange left, BalanceChange right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs new file mode 100644 index 00000000000..929f3a1b33a --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct BlockAccessList : IEquatable +{ + public SortedDictionary AccountChanges { get; init; } + + public BlockAccessList() + { + AccountChanges = []; + } + + public BlockAccessList(SortedDictionary accountChanges) + { + AccountChanges = accountChanges; + } + + public readonly bool Equals(BlockAccessList other) => + AccountChanges.Count == other.AccountChanges.Count; + + public override readonly bool Equals(object? obj) => + obj is BlockAccessList other && Equals(other); + + public override readonly int GetHashCode() => + AccountChanges.Count.GetHashCode(); + + public static bool operator ==(BlockAccessList left, BlockAccessList right) => + left.Equals(right); + + public static bool operator !=(BlockAccessList left, BlockAccessList right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs new file mode 100644 index 00000000000..f1b916c6866 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -0,0 +1,39 @@ + +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Linq; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct CodeChange(ushort blockAccessIndex, byte[] newCode) : IEquatable +{ + public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + public byte[] NewCode { get; init; } = newCode; + + public readonly bool Equals(CodeChange other) => + BlockAccessIndex == other.BlockAccessIndex && + CompareByteArrays(NewCode, other.NewCode); + + public override readonly bool Equals(object? obj) => + obj is CodeChange other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(BlockAccessIndex, NewCode); + + private static bool CompareByteArrays(byte[]? left, byte[]? right) => + left switch + { + null when right == null => true, + null => false, + _ when right == null => false, + _ => left.SequenceEqual(right) + }; + + public static bool operator ==(CodeChange left, CodeChange right) => + left.Equals(right); + + public static bool operator !=(CodeChange left, CodeChange right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs new file mode 100644 index 00000000000..eca1250fda5 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct NonceChange(ushort blockAccessIndex, ulong newNonce) : IEquatable +{ + public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + public ulong NewNonce { get; init; } = newNonce; + + public readonly bool Equals(NonceChange other) => + BlockAccessIndex == other.BlockAccessIndex && + NewNonce == other.NewNonce; + + public override readonly bool Equals(object? obj) => + obj is NonceChange other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(BlockAccessIndex, NewNonce); + + public static bool operator ==(NonceChange left, NonceChange right) => + left.Equals(right); + + public static bool operator !=(NonceChange left, NonceChange right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs new file mode 100644 index 00000000000..5394ae588ad --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -0,0 +1,44 @@ + +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct SlotChanges(byte[] slot, List changes) : IEquatable +{ + public byte[] Slot { get; init; } = slot; + public List Changes { get; init; } = changes; + + public SlotChanges(byte[] slot) : this(slot, []) + { + } + + public readonly bool Equals(SlotChanges other) => + CompareByteArrays(Slot, other.Slot) && + Changes.SequenceEqual(other.Changes); + + public override readonly bool Equals(object? obj) => + obj is SlotChanges other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(Slot, Changes); + + private static bool CompareByteArrays(byte[]? left, byte[]? right) => + left switch + { + null when right == null => true, + null => false, + _ when right == null => false, + _ => left.SequenceEqual(right) + }; + + public static bool operator ==(SlotChanges left, SlotChanges right) => + left.Equals(right); + + public static bool operator !=(SlotChanges left, SlotChanges right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs new file mode 100644 index 00000000000..2cb8acbda44 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -0,0 +1,30 @@ + +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Linq; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct StorageChange(ushort blockAccessIndex, Bytes32 newValue) : IEquatable +{ + public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + public Bytes32 NewValue { get; init; } = newValue; + + public readonly bool Equals(StorageChange other) => + BlockAccessIndex == other.BlockAccessIndex && + NewValue.Unwrap().SequenceEqual(other.NewValue.Unwrap()); + + public override readonly bool Equals(object? obj) => + obj is StorageChange other && Equals(other); + + public override readonly int GetHashCode() => + HashCode.Combine(BlockAccessIndex, NewValue); + + public static bool operator ==(StorageChange left, StorageChange right) => + left.Equals(right); + + public static bool operator !=(StorageChange left, StorageChange right) => + !(left == right); +} diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs new file mode 100644 index 00000000000..3cae4357868 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Linq; + +namespace Nethermind.Core.BlockAccessLists; + +public readonly struct StorageRead(Bytes32 key) : IEquatable +{ + public Bytes32 Key { get; init; } = key; + + public readonly bool Equals(StorageRead other) => + Key.Unwrap().SequenceEqual(other.Key.Unwrap()); + + public override readonly bool Equals(object? obj) => + obj is StorageRead other && Equals(other); + + public override readonly int GetHashCode() => + Key.GetHashCode(); + + public static bool operator ==(StorageRead left, StorageRead right) => + left.Equals(right); + + public static bool operator !=(StorageRead left, StorageRead right) => + !(left == right); +} From 26be91420c0f5e0490d56ef4bc60c67cb68c37bd Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 13:35:16 +0100 Subject: [PATCH 051/215] formatting --- .../BlockAccessListTests.cs | 421 +++++++++--------- .../BlockAccessLists/BalanceChange.cs | 2 +- .../BlockAccessLists/CodeChange.cs | 2 +- .../BlockAccessLists/NonceChange.cs | 2 +- .../BlockAccessLists/SlotChanges.cs | 2 +- .../BlockAccessLists/StorageChange.cs | 2 +- .../Eip7928/NonceChangeDecoder.cs | 2 +- 7 files changed, 216 insertions(+), 217 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 5150158bb9c..67ca30f9a1d 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -29,229 +29,228 @@ using NUnit.Framework; //move all to correct folder -namespace Nethermind.Evm.Test +namespace Nethermind.Evm.Test; + +[TestFixture] +public class BlockAccessListTests() : TransactionProcessorTests(true) { - [TestFixture] - public class BlockAccessListTests() : TransactionProcessorTests(true) + [Test] + public void Empty_account_changes() { - [Test] - public void Empty_account_changes() + Block block = Build.A.Block.WithTransactions(Build.A.Transaction.TestObject).TestObject; + + BlockAccessTracer tracer = new(); + tracer.StartNewBlockTrace(block); + tracer.StartNewTxTrace(block.Transactions[0]); + tracer.MarkAsSuccess(TestItem.AddressA, 100, [], [], TestItem.KeccakF); + + Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); + } + + [Test] + public void Balance_and_nonce_changes() + { + ulong gasPrice = 2; + long gasLimit = 100000; + Transaction tx = Build.A.Transaction + .WithTo(TestItem.AddressB) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .TestObject; + + Block block = Build.A.Block + .WithTransactions(tx) + .WithBaseFeePerGas(1) + .WithBeneficiary(TestItem.AddressC).TestObject; + + BlockReceiptsTracer blockReceiptsTracer = new(); + BlockAccessTracer accessTracer = new(); + blockReceiptsTracer.SetOtherTracer(accessTracer); + Execute(tx, block, blockReceiptsTracer); + + SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; + Assert.That(accountChanges, Has.Count.EqualTo(3)); + + List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; + List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; + List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + + using (Assert.EnterMultipleScope()) { - Block block = Build.A.Block.WithTransactions(Build.A.Transaction.TestObject).TestObject; + Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + + Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); + Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - BlockAccessTracer tracer = new(); - tracer.StartNewBlockTrace(block); - tracer.StartNewTxTrace(block.Transactions[0]); - tracer.MarkAsSuccess(TestItem.AddressA, 100, [], [], TestItem.KeccakF); + // zero balance change should not be recorded + Assert.That(toBalanceChanges, Is.Empty); - Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); + Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } + } - [Test] - public void Balance_and_nonce_changes() + [Test] + public void Can_encode_and_decode() + { + StorageChange storageChange = new() { - ulong gasPrice = 2; - long gasLimit = 100000; - Transaction tx = Build.A.Transaction - .WithTo(TestItem.AddressB) - .WithSenderAddress(TestItem.AddressA) - .WithValue(0) - .WithGasPrice(gasPrice) - .WithGasLimit(gasLimit) - .TestObject; - - Block block = Build.A.Block - .WithTransactions(tx) - .WithBaseFeePerGas(1) - .WithBeneficiary(TestItem.AddressC).TestObject; - - BlockReceiptsTracer blockReceiptsTracer = new(); - BlockAccessTracer accessTracer = new(); - blockReceiptsTracer.SetOtherTracer(accessTracer); - Execute(tx, block, blockReceiptsTracer); - - SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(3)); - - List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; - List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - - using (Assert.EnterMultipleScope()) - { - Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); - - Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); - Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - - // zero balance change should not be recorded - Assert.That(toBalanceChanges, Is.Empty); - - Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); - } - } + BlockAccessIndex = 10, + NewValue = new([.. Enumerable.Repeat(50, 32)]) + }; + byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; + StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); + Assert.That(storageChange, Is.EqualTo(storageChangeDecoded)); + + SlotChanges slotChanges = new() + { + Slot = [.. Enumerable.Repeat(100, 32)], + Changes = [storageChange, storageChange] + }; + byte[] slotChangesBytes = Rlp.Encode(slotChanges, RlpBehaviors.None).Bytes; + SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); + Assert.That(slotChanges, Is.EqualTo(slotChangesDecoded)); + + StorageRead storageRead = new(new Bytes32([.. Enumerable.Repeat(50, 32)])); + byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; + StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); + Assert.That(storageRead, Is.EqualTo(storageReadDecoded)); + + BalanceChange balanceChange = new() + { + BlockAccessIndex = 10, + PostBalance = 0 + }; + byte[] balanceChangeBytes = Rlp.Encode(balanceChange, RlpBehaviors.None).Bytes; + BalanceChange balanceChangeDecoded = Rlp.Decode(balanceChangeBytes, RlpBehaviors.None); + Assert.That(balanceChange, Is.EqualTo(balanceChangeDecoded)); + + NonceChange nonceChange = new() + { + BlockAccessIndex = 10, + NewNonce = 0 + }; + byte[] nonceChangeBytes = Rlp.Encode(nonceChange, RlpBehaviors.None).Bytes; + NonceChange nonceChangeDecoded = Rlp.Decode(nonceChangeBytes, RlpBehaviors.None); + Assert.That(nonceChange, Is.EqualTo(nonceChangeDecoded)); + + CodeChange codeChange = new() + { + BlockAccessIndex = 10, + NewCode = [0, 50] + }; + byte[] codeChangeBytes = Rlp.Encode(codeChange, RlpBehaviors.None).Bytes; + CodeChange codeChangeDecoded = Rlp.Decode(codeChangeBytes, RlpBehaviors.None); + Assert.That(codeChange, Is.EqualTo(codeChangeDecoded)); + + SortedDictionary storageChangesDict = new() + { + { slotChanges.Slot, slotChanges } + }; - [Test] - public void Can_encode_and_decode() + AccountChanges accountChanges = new() { - StorageChange storageChange = new() - { - BlockAccessIndex = 10, - NewValue = new([.. Enumerable.Repeat(50, 32)]) - }; - byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; - StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); - Assert.That(storageChange, Is.EqualTo(storageChangeDecoded)); - - SlotChanges slotChanges = new() - { - Slot = [.. Enumerable.Repeat(100, 32)], - Changes = [storageChange, storageChange] - }; - byte[] slotChangesBytes = Rlp.Encode(slotChanges, RlpBehaviors.None).Bytes; - SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); - Assert.That(slotChanges, Is.EqualTo(slotChangesDecoded)); - - StorageRead storageRead = new(new Bytes32([.. Enumerable.Repeat(50, 32)])); - byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; - StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); - Assert.That(storageRead, Is.EqualTo(storageReadDecoded)); - - BalanceChange balanceChange = new() - { - BlockAccessIndex = 10, - PostBalance = 0 - }; - byte[] balanceChangeBytes = Rlp.Encode(balanceChange, RlpBehaviors.None).Bytes; - BalanceChange balanceChangeDecoded = Rlp.Decode(balanceChangeBytes, RlpBehaviors.None); - Assert.That(balanceChange, Is.EqualTo(balanceChangeDecoded)); - - NonceChange nonceChange = new() - { - BlockAccessIndex = 10, - NewNonce = 0 - }; - byte[] nonceChangeBytes = Rlp.Encode(nonceChange, RlpBehaviors.None).Bytes; - NonceChange nonceChangeDecoded = Rlp.Decode(nonceChangeBytes, RlpBehaviors.None); - Assert.That(nonceChange, Is.EqualTo(nonceChangeDecoded)); - - CodeChange codeChange = new() - { - BlockAccessIndex = 10, - NewCode = [0, 50] - }; - byte[] codeChangeBytes = Rlp.Encode(codeChange, RlpBehaviors.None).Bytes; - CodeChange codeChangeDecoded = Rlp.Decode(codeChangeBytes, RlpBehaviors.None); - Assert.That(codeChange, Is.EqualTo(codeChangeDecoded)); - - SortedDictionary storageChangesDict = new() - { - { slotChanges.Slot, slotChanges } - }; - - AccountChanges accountChanges = new() - { - Address = TestItem.AddressA, - StorageChanges = storageChangesDict, - StorageReads = [storageRead, storageRead], - BalanceChanges = [balanceChange, balanceChange], - NonceChanges = [nonceChange, nonceChange], - CodeChanges = [codeChange] - }; - byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; - AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); - Assert.That(accountChanges, Is.EqualTo(accountChangesDecoded)); - - SortedDictionary accountChangesDict = new() - { - { accountChanges.Address, accountChanges } - }; - - BlockAccessList blockAccessList = new() - { - AccountChanges = accountChangesDict - }; - byte[] blockAccessListBytes = Rlp.Encode(blockAccessList, RlpBehaviors.None).Bytes; - BlockAccessList blockAccessListDecoded = Rlp.Decode(blockAccessListBytes, RlpBehaviors.None); - Assert.That(blockAccessList, Is.EqualTo(blockAccessListDecoded)); - } + Address = TestItem.AddressA, + StorageChanges = storageChangesDict, + StorageReads = [storageRead, storageRead], + BalanceChanges = [balanceChange, balanceChange], + NonceChanges = [nonceChange, nonceChange], + CodeChanges = [codeChange] + }; + byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; + AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); + Assert.That(accountChanges, Is.EqualTo(accountChangesDecoded)); + + SortedDictionary accountChangesDict = new() + { + { accountChanges.Address, accountChanges } + }; + + BlockAccessList blockAccessList = new() + { + AccountChanges = accountChangesDict + }; + byte[] blockAccessListBytes = Rlp.Encode(blockAccessList, RlpBehaviors.None).Bytes; + BlockAccessList blockAccessListDecoded = Rlp.Decode(blockAccessListBytes, RlpBehaviors.None); + Assert.That(blockAccessList, Is.EqualTo(blockAccessListDecoded)); + } + + [Test] + public void System_contracts_and_withdrawals() + { + BlockProcessor processor = new(HoleskySpecProvider.Instance, + TestBlockValidator.AlwaysValid, + NoBlockRewards.Instance, + new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(_transactionProcessor), _stateProvider), + _stateProvider, + NullReceiptStorage.Instance, + new BeaconBlockRootHandler(_transactionProcessor, _stateProvider), + Substitute.For(), // create dummy? + LimboLogs.Instance, + new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), + new ExecutionRequestsProcessor(_transactionProcessor)); + + // todo: just use test blockchain? + _stateProvider.CreateAccount(Eip4788Constants.BeaconRootsAddress, 10); + _stateProvider.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); + _stateProvider.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, Prague.Instance); + _stateProvider.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); + _stateProvider.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, Prague.Instance); + + ulong gasPrice = 2; + long gasLimit = 100000; + Transaction tx = Build.A.Transaction + .WithTo(TestItem.AddressB) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .TestObject; + + Block block = Build.A.Block + .WithParentBeaconBlockRoot(Hash256.Zero) + .WithNumber(100) + .WithTransactions(tx) + .WithBaseFeePerGas(1) + .WithBeneficiary(TestItem.AddressC).TestObject; + + // BlockReceiptsTracer blockReceiptsTracer = new(); + // BlockAccessTracer accessTracer = new(); + // blockReceiptsTracer.SetOtherTracer(accessTracer); + // Execute(tx, block, blockReceiptsTracer); + + OverridableReleaseSpec spec = new(Prague.Instance) + { + IsEip7928Enabled = true + }; + (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); + + BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); + SortedDictionary accountChanges = blockAccessList.AccountChanges; + Assert.That(accountChanges, Has.Count.EqualTo(3)); + + List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; + List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; + List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - [Test] - public void System_contracts_and_withdrawals() + using (Assert.EnterMultipleScope()) { - BlockProcessor processor = new(HoleskySpecProvider.Instance, - TestBlockValidator.AlwaysValid, - NoBlockRewards.Instance, - new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(_transactionProcessor), _stateProvider), - _stateProvider, - NullReceiptStorage.Instance, - new BeaconBlockRootHandler(_transactionProcessor, _stateProvider), - Substitute.For(), // create dummy? - LimboLogs.Instance, - new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), - new ExecutionRequestsProcessor(_transactionProcessor)); - - // todo: just use test blockchain? - _stateProvider.CreateAccount(Eip4788Constants.BeaconRootsAddress, 10); - _stateProvider.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); - _stateProvider.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, Prague.Instance); - _stateProvider.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); - _stateProvider.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, Prague.Instance); - - ulong gasPrice = 2; - long gasLimit = 100000; - Transaction tx = Build.A.Transaction - .WithTo(TestItem.AddressB) - .WithSenderAddress(TestItem.AddressA) - .WithValue(0) - .WithGasPrice(gasPrice) - .WithGasLimit(gasLimit) - .TestObject; - - Block block = Build.A.Block - .WithParentBeaconBlockRoot(Hash256.Zero) - .WithNumber(100) - .WithTransactions(tx) - .WithBaseFeePerGas(1) - .WithBeneficiary(TestItem.AddressC).TestObject; - - // BlockReceiptsTracer blockReceiptsTracer = new(); - // BlockAccessTracer accessTracer = new(); - // blockReceiptsTracer.SetOtherTracer(accessTracer); - // Execute(tx, block, blockReceiptsTracer); - - OverridableReleaseSpec spec = new(Prague.Instance) - { - IsEip7928Enabled = true - }; - (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); - - BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); - SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(3)); - - List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; - List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - - using (Assert.EnterMultipleScope()) - { - Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); - - Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); - Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - - // zero balance change should not be recorded - Assert.That(toBalanceChanges, Is.Empty); - - Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); - } + Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + + Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); + Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); + + // zero balance change should not be recorded + Assert.That(toBalanceChanges, Is.Empty); + + Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index d2eb6fb32c3..5625667417c 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -12,7 +12,7 @@ public readonly struct BalanceChange(ushort blockAccessIndex, UInt256 postBalanc public UInt256 PostBalance { get; init; } = postBalance; public readonly bool Equals(BalanceChange other) => - BlockAccessIndex == other.BlockAccessIndex && + BlockAccessIndex == other.BlockAccessIndex && PostBalance == other.PostBalance; public override readonly bool Equals(object? obj) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index f1b916c6866..dcc1782dd4f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -13,7 +13,7 @@ public readonly struct CodeChange(ushort blockAccessIndex, byte[] newCode) : IEq public byte[] NewCode { get; init; } = newCode; public readonly bool Equals(CodeChange other) => - BlockAccessIndex == other.BlockAccessIndex && + BlockAccessIndex == other.BlockAccessIndex && CompareByteArrays(NewCode, other.NewCode); public override readonly bool Equals(object? obj) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs index eca1250fda5..5b163343341 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -11,7 +11,7 @@ public readonly struct NonceChange(ushort blockAccessIndex, ulong newNonce) : IE public ulong NewNonce { get; init; } = newNonce; public readonly bool Equals(NonceChange other) => - BlockAccessIndex == other.BlockAccessIndex && + BlockAccessIndex == other.BlockAccessIndex && NewNonce == other.NewNonce; public override readonly bool Equals(object? obj) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs index 5394ae588ad..c647a2f1ef2 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -18,7 +18,7 @@ public SlotChanges(byte[] slot) : this(slot, []) } public readonly bool Equals(SlotChanges other) => - CompareByteArrays(Slot, other.Slot) && + CompareByteArrays(Slot, other.Slot) && Changes.SequenceEqual(other.Changes); public override readonly bool Equals(object? obj) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index 2cb8acbda44..6efe2af7fff 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -13,7 +13,7 @@ public readonly struct StorageChange(ushort blockAccessIndex, Bytes32 newValue) public Bytes32 NewValue { get; init; } = newValue; public readonly bool Equals(StorageChange other) => - BlockAccessIndex == other.BlockAccessIndex && + BlockAccessIndex == other.BlockAccessIndex && NewValue.Unwrap().SequenceEqual(other.NewValue.Unwrap()); public override readonly bool Equals(object? obj) => diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index 4b98c9545db..352bd7ccedd 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -37,7 +37,7 @@ public NonceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { Span span = rlpStream.PeekNextItem(); Rlp.ValueDecoderContext ctx = new(span); - NonceChange res= Decode(ref ctx, rlpBehaviors); + NonceChange res = Decode(ref ctx, rlpBehaviors); rlpStream.SkipItem(); return res; From 7d40f7eaf3a613285a69e6b9b35aec949c2c6777 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:20:14 +0100 Subject: [PATCH 052/215] refactor test to use testblockchain --- .../BlockAccessListTests.cs | 88 ++++++++++--------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 67ca30f9a1d..ff04e66b68b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -1,31 +1,28 @@ // SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using Nethermind.Blockchain.BeaconBlockRoot; -using Nethermind.Blockchain.Blocks; -using Nethermind.Blockchain.Receipts; -using Nethermind.Blockchain.Test.Validators; +using System.Threading.Tasks; +using Autofac; using Nethermind.Blockchain.Tracing; -using Nethermind.Consensus.ExecutionRequests; using Nethermind.Consensus.Processing; -using Nethermind.Consensus.Rewards; -using Nethermind.Consensus.Withdrawals; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; using Nethermind.Core.Test; +using Nethermind.Core.Test.Blockchain; using Nethermind.Core.Test.Builders; -using Nethermind.Evm.TransactionProcessing; +using Nethermind.Evm.State; using Nethermind.Int256; -using Nethermind.Logging; using Nethermind.Serialization.Rlp; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; -using NSubstitute; using NUnit.Framework; //move all to correct folder @@ -34,6 +31,13 @@ namespace Nethermind.Evm.Test; [TestFixture] public class BlockAccessListTests() : TransactionProcessorTests(true) { + private static readonly OverridableReleaseSpec _spec = new(Prague.Instance) + { + IsEip7928Enabled = true + }; + + private static readonly ISpecProvider _specProvider = new TestSpecProvider(_spec); + [Test] public void Empty_account_changes() { @@ -180,26 +184,23 @@ public void Can_encode_and_decode() } [Test] - public void System_contracts_and_withdrawals() + public async Task System_contracts_and_withdrawals() { - BlockProcessor processor = new(HoleskySpecProvider.Instance, - TestBlockValidator.AlwaysValid, - NoBlockRewards.Instance, - new BlockProcessor.BlockValidationTransactionsExecutor(new ExecuteTransactionProcessorAdapter(_transactionProcessor), _stateProvider), - _stateProvider, - NullReceiptStorage.Instance, - new BeaconBlockRootHandler(_transactionProcessor, _stateProvider), - Substitute.For(), // create dummy? - LimboLogs.Instance, - new WithdrawalProcessor(_stateProvider, LimboLogs.Instance), - new ExecutionRequestsProcessor(_transactionProcessor)); - - // todo: just use test blockchain? - _stateProvider.CreateAccount(Eip4788Constants.BeaconRootsAddress, 10); - _stateProvider.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); - _stateProvider.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, Prague.Instance); - _stateProvider.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); - _stateProvider.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, Prague.Instance); + using BasicTestBlockchain testBlockchain = await BasicTestBlockchain.Create(BuildContainer()); + + IWorldState worldState = testBlockchain.WorldStateManager.GlobalWorldState; + using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis); + worldState.CreateAccount(TestItem.AddressA, 10.Ether()); + worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 1); + worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); + worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); + worldState.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, _specProvider.GenesisSpec); + + worldState.Commit(_specProvider.GenesisSpec); + worldState.CommitTree(0); + worldState.RecalculateStateRoot(); + Hash256 stateRoot = worldState.StateRoot; ulong gasPrice = 2; long gasLimit = 100000; @@ -211,23 +212,25 @@ public void System_contracts_and_withdrawals() .WithGasLimit(gasLimit) .TestObject; + BlockHeader header = Build.A.BlockHeader + .WithBaseFee(1) + .WithNumber(1) + .WithGasUsed(21000) + .WithReceiptsRoot(new("0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2")) + .WithStateRoot(new("0x7c14eebf21367805cab32e286e87f18191ce9286ff344b665fd7d278e2ee2b87")) + .WithBlobGasUsed(0) + .WithBeneficiary(TestItem.AddressC) + .WithParentBeaconBlockRoot(Hash256.Zero) + .WithRequestsHash(new("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")) + .TestObject; + Block block = Build.A.Block .WithParentBeaconBlockRoot(Hash256.Zero) - .WithNumber(100) .WithTransactions(tx) .WithBaseFeePerGas(1) - .WithBeneficiary(TestItem.AddressC).TestObject; + .WithHeader(header).TestObject; - // BlockReceiptsTracer blockReceiptsTracer = new(); - // BlockAccessTracer accessTracer = new(); - // blockReceiptsTracer.SetOtherTracer(accessTracer); - // Execute(tx, block, blockReceiptsTracer); - - OverridableReleaseSpec spec = new(Prague.Instance) - { - IsEip7928Enabled = true - }; - (Block processedBlock, TxReceipt[] _) = processor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, spec, CancellationToken.None); + (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); SortedDictionary accountChanges = blockAccessList.AccountChanges; @@ -253,4 +256,7 @@ public void System_contracts_and_withdrawals() Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); } } + + private static Action BuildContainer() + => containerBuilder => containerBuilder.AddSingleton(_specProvider); } From b4237c1bb75fad2776703680d38d3b4e7b620944 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:24:41 +0100 Subject: [PATCH 053/215] tidy tests --- .../BlockAccessListTests.cs | 123 +++++++++--------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index ff04e66b68b..b7187a27219 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -29,7 +29,7 @@ namespace Nethermind.Evm.Test; [TestFixture] -public class BlockAccessListTests() : TransactionProcessorTests(true) +public class BlockAccessListTests() { private static readonly OverridableReleaseSpec _spec = new(Prague.Instance) { @@ -51,52 +51,52 @@ public void Empty_account_changes() Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); } - [Test] - public void Balance_and_nonce_changes() - { - ulong gasPrice = 2; - long gasLimit = 100000; - Transaction tx = Build.A.Transaction - .WithTo(TestItem.AddressB) - .WithSenderAddress(TestItem.AddressA) - .WithValue(0) - .WithGasPrice(gasPrice) - .WithGasLimit(gasLimit) - .TestObject; - - Block block = Build.A.Block - .WithTransactions(tx) - .WithBaseFeePerGas(1) - .WithBeneficiary(TestItem.AddressC).TestObject; - - BlockReceiptsTracer blockReceiptsTracer = new(); - BlockAccessTracer accessTracer = new(); - blockReceiptsTracer.SetOtherTracer(accessTracer); - Execute(tx, block, blockReceiptsTracer); - - SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(3)); - - List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; - List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - - using (Assert.EnterMultipleScope()) - { - Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); - - Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); - Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - - // zero balance change should not be recorded - Assert.That(toBalanceChanges, Is.Empty); - - Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); - } - } + // [Test] + // public void Balance_and_nonce_changes() + // { + // ulong gasPrice = 2; + // long gasLimit = 100000; + // Transaction tx = Build.A.Transaction + // .WithTo(TestItem.AddressB) + // .WithSenderAddress(TestItem.AddressA) + // .WithValue(0) + // .WithGasPrice(gasPrice) + // .WithGasLimit(gasLimit) + // .TestObject; + + // Block block = Build.A.Block + // .WithTransactions(tx) + // .WithBaseFeePerGas(1) + // .WithBeneficiary(TestItem.AddressC).TestObject; + + // // BlockReceiptsTracer blockReceiptsTracer = new(); + // // BlockAccessTracer accessTracer = new(); + // // blockReceiptsTracer.SetOtherTracer(accessTracer); + // // Execute(tx, block, blockReceiptsTracer); + + // SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; + // Assert.That(accountChanges, Has.Count.EqualTo(3)); + + // List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; + // List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; + // List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; + // List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + + // using (Assert.EnterMultipleScope()) + // { + // Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); + // // Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + + // Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); + // Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); + + // // zero balance change should not be recorded + // Assert.That(toBalanceChanges, Is.Empty); + + // Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); + // Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + // } + // } [Test] public void Can_encode_and_decode() @@ -190,17 +190,7 @@ public async Task System_contracts_and_withdrawals() IWorldState worldState = testBlockchain.WorldStateManager.GlobalWorldState; using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis); - worldState.CreateAccount(TestItem.AddressA, 10.Ether()); - worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 1); - worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); - worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); - worldState.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); - worldState.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, _specProvider.GenesisSpec); - - worldState.Commit(_specProvider.GenesisSpec); - worldState.CommitTree(0); - worldState.RecalculateStateRoot(); - Hash256 stateRoot = worldState.StateRoot; + InitWorldState(worldState); ulong gasPrice = 2; long gasLimit = 100000; @@ -244,7 +234,7 @@ public async Task System_contracts_and_withdrawals() using (Assert.EnterMultipleScope()) { Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + // Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); @@ -259,4 +249,19 @@ public async Task System_contracts_and_withdrawals() private static Action BuildContainer() => containerBuilder => containerBuilder.AddSingleton(_specProvider); + + private static void InitWorldState(IWorldState worldState) + { + worldState.CreateAccount(TestItem.AddressA, 10.Ether()); + worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 1); + worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); + worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); + worldState.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, _specProvider.GenesisSpec); + + worldState.Commit(_specProvider.GenesisSpec); + worldState.CommitTree(0); + worldState.RecalculateStateRoot(); + // Hash256 stateRoot = worldState.StateRoot; + } } From e9bc3239459a221d7d87ffd7623055bfa381ef78 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:33:41 +0100 Subject: [PATCH 054/215] fix test --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index b7187a27219..339a1a17ff1 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -37,6 +37,7 @@ public class BlockAccessListTests() }; private static readonly ISpecProvider _specProvider = new TestSpecProvider(_spec); + private static readonly UInt256 _accountBalance = 10.Ether(); [Test] public void Empty_account_changes() @@ -215,7 +216,6 @@ public async Task System_contracts_and_withdrawals() .TestObject; Block block = Build.A.Block - .WithParentBeaconBlockRoot(Hash256.Zero) .WithTransactions(tx) .WithBaseFeePerGas(1) .WithHeader(header).TestObject; @@ -224,7 +224,7 @@ public async Task System_contracts_and_withdrawals() BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(3)); + Assert.That(accountChanges, Has.Count.EqualTo(5)); List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; @@ -234,7 +234,7 @@ public async Task System_contracts_and_withdrawals() using (Assert.EnterMultipleScope()) { Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - // Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); + Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(_accountBalance - gasPrice * GasCostOf.Transaction)); Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); @@ -252,7 +252,7 @@ private static Action BuildContainer() private static void InitWorldState(IWorldState worldState) { - worldState.CreateAccount(TestItem.AddressA, 10.Ether()); + worldState.CreateAccount(TestItem.AddressA, _accountBalance); worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 1); worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); From 158359f36d5c45d5d8170270f9205944f66d3dd8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:38:04 +0100 Subject: [PATCH 055/215] improve test --- .../BlockAccessListTests.cs | 71 ++++++++++++++----- .../BlockAccessLists/AccountChanges.cs | 8 ++- .../BlockAccessLists/BlockAccessList.cs | 3 +- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 339a1a17ff1..9c0dc3ee641 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -226,26 +226,65 @@ public async Task System_contracts_and_withdrawals() SortedDictionary accountChanges = blockAccessList.AccountChanges; Assert.That(accountChanges, Has.Count.EqualTo(5)); - List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; - List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; + AccountChanges addressAChanges = accountChanges[TestItem.AddressA]; + AccountChanges addressBChanges = accountChanges[TestItem.AddressB]; + AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; + AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; + AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; using (Assert.EnterMultipleScope()) { - Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(_accountBalance - gasPrice * GasCostOf.Transaction)); - - Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); - Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - - // zero balance change should not be recorded - Assert.That(toBalanceChanges, Is.Empty); - - Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); + Assert.That(addressAChanges, Is.EqualTo(new AccountChanges() + { + Address = TestItem.AddressA, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [new(1, _accountBalance - gasPrice * GasCostOf.Transaction)], + NonceChanges = [new(1, 1)], + CodeChanges = [] + })); + + Assert.That(addressBChanges, Is.EqualTo(new AccountChanges() + { + Address = TestItem.AddressB, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [], + NonceChanges = [], + CodeChanges = [] + })); + + Assert.That(addressCChanges, Is.EqualTo(new AccountChanges() + { + Address = TestItem.AddressC, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [new(1, new UInt256(GasCostOf.Transaction))], + NonceChanges = [new(1, 0)], + CodeChanges = [] + })); + + Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() + { + Address = Eip4788Constants.BeaconRootsAddress, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [], + NonceChanges = [], + CodeChanges = [] + })); + + Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [], + NonceChanges = [], + CodeChanges = [] + })); + } } - } private static Action BuildContainer() => containerBuilder => containerBuilder.AddSingleton(_specProvider); diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index e883502c8a7..a9a8e6ba6fd 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Nethermind.Core.BlockAccessLists; @@ -37,7 +38,12 @@ public AccountChanges(Address address, SortedDictionary sto } public readonly bool Equals(AccountChanges other) => - Address == other.Address; + Address == other.Address && + StorageChanges.Values.SequenceEqual(other.StorageChanges.Values) && + StorageReads.SequenceEqual(other.StorageReads) && + BalanceChanges.SequenceEqual(other.BalanceChanges) && + NonceChanges.SequenceEqual(other.NonceChanges) && + CodeChanges.SequenceEqual(other.CodeChanges); public override readonly bool Equals(object? obj) => obj is AccountChanges other && Equals(other); diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 929f3a1b33a..c2dc8ddf1ac 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Nethermind.Core.BlockAccessLists; @@ -21,7 +22,7 @@ public BlockAccessList(SortedDictionary accountChanges) } public readonly bool Equals(BlockAccessList other) => - AccountChanges.Count == other.AccountChanges.Count; + AccountChanges.SequenceEqual(other.AccountChanges); public override readonly bool Equals(object? obj) => obj is BlockAccessList other && Equals(other); From 034391699eedb1cd1587157cb2191c56e9c068d2 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:10:50 +0100 Subject: [PATCH 056/215] add withdrawal to the test --- .../BlockAccessListTests.cs | 75 ++++++------------- 1 file changed, 24 insertions(+), 51 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 9c0dc3ee641..aeee72767e4 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -52,53 +52,6 @@ public void Empty_account_changes() Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); } - // [Test] - // public void Balance_and_nonce_changes() - // { - // ulong gasPrice = 2; - // long gasLimit = 100000; - // Transaction tx = Build.A.Transaction - // .WithTo(TestItem.AddressB) - // .WithSenderAddress(TestItem.AddressA) - // .WithValue(0) - // .WithGasPrice(gasPrice) - // .WithGasLimit(gasLimit) - // .TestObject; - - // Block block = Build.A.Block - // .WithTransactions(tx) - // .WithBaseFeePerGas(1) - // .WithBeneficiary(TestItem.AddressC).TestObject; - - // // BlockReceiptsTracer blockReceiptsTracer = new(); - // // BlockAccessTracer accessTracer = new(); - // // blockReceiptsTracer.SetOtherTracer(accessTracer); - // // Execute(tx, block, blockReceiptsTracer); - - // SortedDictionary accountChanges = accessTracer.BlockAccessList.AccountChanges; - // Assert.That(accountChanges, Has.Count.EqualTo(3)); - - // List senderBalanceChanges = accountChanges[TestItem.AddressA].BalanceChanges; - // List senderNonceChanges = accountChanges[TestItem.AddressA].NonceChanges; - // List toBalanceChanges = accountChanges[TestItem.AddressB].BalanceChanges; - // List beneficiaryBalanceChanges = accountChanges[TestItem.AddressC].BalanceChanges; - - // using (Assert.EnterMultipleScope()) - // { - // Assert.That(senderBalanceChanges, Has.Count.EqualTo(1)); - // // Assert.That(senderBalanceChanges[0].PostBalance, Is.EqualTo(AccountBalance - gasPrice * GasCostOf.Transaction)); - - // Assert.That(senderNonceChanges, Has.Count.EqualTo(1)); - // Assert.That(senderNonceChanges[0].NewNonce, Is.EqualTo(1)); - - // // zero balance change should not be recorded - // Assert.That(toBalanceChanges, Is.Empty); - - // Assert.That(beneficiaryBalanceChanges, Has.Count.EqualTo(1)); - // Assert.That(beneficiaryBalanceChanges[0].PostBalance, Is.EqualTo(new UInt256(GasCostOf.Transaction))); - // } - // } - [Test] public void Can_encode_and_decode() { @@ -185,7 +138,7 @@ public void Can_encode_and_decode() } [Test] - public async Task System_contracts_and_withdrawals() + public async Task Can_construct_BAL() { using BasicTestBlockchain testBlockchain = await BasicTestBlockchain.Create(BuildContainer()); @@ -208,27 +161,37 @@ public async Task System_contracts_and_withdrawals() .WithNumber(1) .WithGasUsed(21000) .WithReceiptsRoot(new("0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2")) - .WithStateRoot(new("0x7c14eebf21367805cab32e286e87f18191ce9286ff344b665fd7d278e2ee2b87")) + .WithStateRoot(new("0x791d07603a3449bd9d70180d3f00af1fb493033171c7c2b056bf07779c45cfe8")) .WithBlobGasUsed(0) .WithBeneficiary(TestItem.AddressC) .WithParentBeaconBlockRoot(Hash256.Zero) .WithRequestsHash(new("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")) .TestObject; + Withdrawal withdrawal = new() + { + Index = 0, + ValidatorIndex = 0, + Address = TestItem.AddressD, + AmountInGwei = 1 + }; + Block block = Build.A.Block .WithTransactions(tx) .WithBaseFeePerGas(1) + .WithWithdrawals([withdrawal]) .WithHeader(header).TestObject; (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(5)); + Assert.That(accountChanges, Has.Count.EqualTo(6)); AccountChanges addressAChanges = accountChanges[TestItem.AddressA]; AccountChanges addressBChanges = accountChanges[TestItem.AddressB]; AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; + AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; @@ -264,6 +227,16 @@ public async Task System_contracts_and_withdrawals() CodeChanges = [] })); + Assert.That(addressDChanges, Is.EqualTo(new AccountChanges() + { + Address = TestItem.AddressD, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [new(2, 1.GWei())], + NonceChanges = [], + CodeChanges = [] + })); + Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, @@ -284,7 +257,7 @@ public async Task System_contracts_and_withdrawals() CodeChanges = [] })); } - } + } private static Action BuildContainer() => containerBuilder => containerBuilder.AddSingleton(_specProvider); From 45923a0eb60ef45a141efa2ce9ce3bea72b20e9d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:10:43 +0100 Subject: [PATCH 057/215] eip 7002 & 7251 --- .../BlockAccessListTests.cs | 42 ++++++++++++++++--- .../ExecutionRequestProcessor.cs | 11 ++--- .../IExecutionRequestProcessor.cs | 3 +- .../Processing/BlockProcessor.cs | 2 +- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index aeee72767e4..e4e6e036a73 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -186,14 +186,16 @@ public async Task Can_construct_BAL() BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(6)); + Assert.That(accountChanges, Has.Count.EqualTo(8)); AccountChanges addressAChanges = accountChanges[TestItem.AddressA]; AccountChanges addressBChanges = accountChanges[TestItem.AddressB]; AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; - AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; + AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; + AccountChanges eip7002Changes = accountChanges[Eip7002Constants.WithdrawalRequestPredeployAddress]; + AccountChanges eip7251Changes = accountChanges[Eip7251Constants.ConsolidationRequestPredeployAddress]; using (Assert.EnterMultipleScope()) { @@ -237,6 +239,16 @@ public async Task Can_construct_BAL() CodeChanges = [] })); + Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [], + NonceChanges = [], + CodeChanges = [] + })); + Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, @@ -247,11 +259,31 @@ public async Task Can_construct_BAL() CodeChanges = [] })); - Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() + Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges() { - Address = Eip2935Constants.BlockHashHistoryAddress, + Address = Eip7002Constants.WithdrawalRequestPredeployAddress, StorageChanges = [], - StorageReads = [], + StorageReads = [ + new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), + new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), + new(Bytes32.Wrap(Convert.FromHexString("405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), + new(Bytes32.Wrap(Convert.FromHexString("c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + ], + BalanceChanges = [], + NonceChanges = [], + CodeChanges = [] + })); + + Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges() + { + Address = Eip7251Constants.ConsolidationRequestPredeployAddress, + StorageChanges = [], + StorageReads = [ + new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), + new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), + new(Bytes32.Wrap(Convert.FromHexString("405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), + new(Bytes32.Wrap(Convert.FromHexString("c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + ], BalanceChanges = [], NonceChanges = [], CodeChanges = [] diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs index e504b3748cc..5413cd58692 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs @@ -13,6 +13,7 @@ using Nethermind.Crypto; using Nethermind.Evm; using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; using System; @@ -56,7 +57,7 @@ public ExecutionRequestsProcessor(ITransactionProcessor transactionProcessor) _consolidationTransaction.Hash = _consolidationTransaction.CalculateHash(); } - public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) + public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, ITxTracer tracer) { if (!spec.RequestsEnabled || block.IsGenesis) return; @@ -68,13 +69,13 @@ public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] if (spec.WithdrawalRequestsEnabled) { ReadRequests(block, state, spec.Eip7002ContractAddress, requests, _withdrawalTransaction, ExecutionRequestType.WithdrawalRequest, - BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed); + BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed, tracer); } if (spec.ConsolidationRequestsEnabled) { ReadRequests(block, state, spec.Eip7251ContractAddress, requests, _consolidationTransaction, ExecutionRequestType.ConsolidationRequest, - BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed); + BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed, tracer); } block.ExecutionRequests = [.. requests]; @@ -161,7 +162,7 @@ static void Validate(Block block, object obj, string name, int expectedSize) } private void ReadRequests(Block block, IWorldState state, Address contractAddress, ArrayPoolList requests, - Transaction systemTx, ExecutionRequestType type, string contractEmptyError, string contractFailedError) + Transaction systemTx, ExecutionRequestType type, string contractEmptyError, string contractFailedError, ITxTracer? additionalTracer = null) { if (!state.HasCode(contractAddress)) { @@ -170,7 +171,7 @@ private void ReadRequests(Block block, IWorldState state, Address contractAddres CallOutputTracer tracer = new(); - _transactionProcessor.Execute(systemTx, tracer); + _transactionProcessor.Execute(systemTx, additionalTracer is null ? tracer : new CompositeTxTracer(tracer, additionalTracer)); if (tracer.StatusCode == StatusCode.Failure) { diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs index 533ecffc794..1a8888c8286 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs @@ -4,10 +4,11 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.State; +using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.ExecutionRequests; public interface IExecutionRequestsProcessor { - public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec); + public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, ITxTracer? additionalTracer = null); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index bf1d2b595c4..da5bca46342 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -145,7 +145,7 @@ protected virtual TxReceipt[] ProcessBlock( // Eip158Enabled=false, so we end up persisting empty accounts created while processing withdrawals. _stateProvider.Commit(spec, commitRoots: false); - executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec); + executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec, BlockAccessTracer); ReceiptsTracer.EndBlockTrace(); From e00506cd10e43b06f7ccb4451416953412154335 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:26:29 +0100 Subject: [PATCH 058/215] system contracts, storage changes --- .../BlockAccessListTests.cs | 80 +++++++++++++++++-- .../Tracing/BlockAccessTracer.cs | 14 +++- .../BlockAccessLists/AccountChanges.cs | 5 +- .../TransactionProcessor.cs | 2 +- .../Eip7928/AccountChangesDecoder.cs | 3 +- 5 files changed, 89 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index e4e6e036a73..8e20aa05f02 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -161,7 +161,7 @@ public async Task Can_construct_BAL() .WithNumber(1) .WithGasUsed(21000) .WithReceiptsRoot(new("0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2")) - .WithStateRoot(new("0x791d07603a3449bd9d70180d3f00af1fb493033171c7c2b056bf07779c45cfe8")) + .WithStateRoot(new("0x869b0dea3e9d18f71753c2b64142901e11b6be272ddbb8975f32851528d30c36")) .WithBlobGasUsed(0) .WithBeneficiary(TestItem.AddressC) .WithParentBeaconBlockRoot(Hash256.Zero) @@ -193,9 +193,66 @@ public async Task Can_construct_BAL() AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; + // string k1 = Convert.ToHexString(eip2935Changes.StorageChanges.First().Key); + // string v1 = Convert.ToHexString(eip2935Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; + // string k2 = Convert.ToHexString(eip4788Changes.StorageChanges.First().Key); + // string v2 = Convert.ToHexString(eip4788Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); + // string k3 = Convert.ToHexString(eip4788Changes.StorageChanges.ElementAt(1).Key); + // string v3 = Convert.ToHexString(eip4788Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); AccountChanges eip7002Changes = accountChanges[Eip7002Constants.WithdrawalRequestPredeployAddress]; + // string k4 = Convert.ToHexString(eip7002Changes.StorageChanges.First().Key); + // string v4 = Convert.ToHexString(eip7002Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); + // string k5 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(1).Key); + // string v5 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); + // string k6 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(2).Key); + // string v6 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(2).Value.Changes.First().NewValue.Unwrap()); + // string k7 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(3).Key); + // string v7 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); AccountChanges eip7251Changes = accountChanges[Eip7251Constants.ConsolidationRequestPredeployAddress]; + // string k8 = Convert.ToHexString(eip7251Changes.StorageChanges.First().Key); + // string v8 = Convert.ToHexString(eip7251Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); + // string k9 = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(1).Key); + // string v9 = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); + // string ka = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(2).Key); + // string va = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(2).Value.Changes.First().NewValue.Unwrap()); + // string kb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Key); + // string vb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); + + byte[] slot1 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + StorageChange value1 = new(0, Bytes32.Wrap(Convert.FromHexString("FF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); + + byte[] slot2 = Convert.FromHexString("0E59911BBD9B80FD816896F0425C7A25DC9EB9092F5AC6264B432F6697F877C8"); + StorageChange value2 = new(0, Bytes32.Zero); + + byte[] slot3 = Convert.FromHexString("2CFFC05BC4230E308FCB837385A814EED1B4C90FB58BA2A0B8407649B9629B28"); + StorageChange value3 = new(0, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + + byte[] slot4 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + StorageChange value4 = new(2, Bytes32.Zero); + + byte[] slot5 = Convert.FromHexString("405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + StorageChange value5 = new(2, Bytes32.Zero); + + byte[] slot6 = Convert.FromHexString("B10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + StorageChange value6 = new(2, Bytes32.Zero); + // StorageChange value6 = new(0, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + + byte[] slot7 = Convert.FromHexString("C2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + StorageChange value7 = new(2, Bytes32.Zero); + + byte[] slot8 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + StorageChange value8 = new(2, Bytes32.Zero); + + byte[] slot9 = Convert.FromHexString("405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + StorageChange value9 = new(2, Bytes32.Zero); + + byte[] slota = Convert.FromHexString("B10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + StorageChange valuea = new(2, Bytes32.Zero); + // StorageChange valuea = new(2, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + + byte[] slotb = Convert.FromHexString("C2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + StorageChange valueb = new(2, Bytes32.Zero); using (Assert.EnterMultipleScope()) { @@ -242,7 +299,7 @@ public async Task Can_construct_BAL() Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() { Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = [], + StorageChanges = new(Bytes.Comparer) { { slot1, new SlotChanges(slot1, [value1]) } }, StorageReads = [], BalanceChanges = [], NonceChanges = [], @@ -252,8 +309,8 @@ public async Task Can_construct_BAL() Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = [], - StorageReads = [], + StorageChanges = new(Bytes.Comparer) { { slot2, new SlotChanges(slot2, [value2]) }, { slot3, new SlotChanges(slot3, [value3]) }}, + StorageReads = [new(Bytes32.Wrap(Convert.FromHexString("0e59911bbd9b80fd816896f0425c7a25dc9eb9092f5ac6264b432f6697f877c8")))], BalanceChanges = [], NonceChanges = [], CodeChanges = [] @@ -262,7 +319,7 @@ public async Task Can_construct_BAL() Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges() { Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageChanges = [], + StorageChanges = new(Bytes.Comparer) { { slot4, new SlotChanges(slot4, [value4]) }, { slot5, new SlotChanges(slot5, [value5]) }, { slot6, new SlotChanges(slot6, [value6]) }, { slot7, new SlotChanges(slot7, [value7]) }}, StorageReads = [ new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), @@ -277,7 +334,7 @@ public async Task Can_construct_BAL() Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges() { Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageChanges = [], + StorageChanges = new(Bytes.Comparer) { { slot8, new SlotChanges(slot8, [value8]) }, { slot9, new SlotChanges(slot9, [value9]) }, { slota, new SlotChanges(slota, [valuea]) }, { slotb, new SlotChanges(slotb, [valueb]) }}, StorageReads = [ new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), @@ -297,9 +354,18 @@ private static Action BuildContainer() private static void InitWorldState(IWorldState worldState) { worldState.CreateAccount(TestItem.AddressA, _accountBalance); - worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 1); + + worldState.CreateAccount(Eip2935Constants.BlockHashHistoryAddress, 0, 1); + byte[] eip2935Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500"); + worldState.InsertCode(Eip2935Constants.BlockHashHistoryAddress, ValueKeccak.Compute(eip2935Code), eip2935Code, _specProvider.GenesisSpec); + + worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 0, 1); + byte[] eip4788Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500"); + worldState.InsertCode(Eip4788Constants.BeaconRootsAddress, ValueKeccak.Compute(eip4788Code), eip4788Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip7251Constants.ConsolidationRequestPredeployAddress, 0, Eip7251TestConstants.Nonce); worldState.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, _specProvider.GenesisSpec); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 8c573b63bd0..d22cbc63b0d 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -20,7 +20,8 @@ public class BlockAccessTracer : IBlockTracer, ITxTracer, IJournal protected Block Block = null!; public bool IsTracingReceipt => false; public bool IsTracingActions => false; - public bool IsTracingOpLevelStorage => false; + // public bool IsTracingOpLevelStorage => false; + public bool IsTracingOpLevelStorage => true; public bool IsTracingMemory => false; public bool IsTracingInstructions => false; public bool IsTracingRefunds => false; @@ -171,7 +172,7 @@ public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[ _bal.AccountChanges.Add(address, accountChanges); } - if (!Enumerable.SequenceEqual(before, after)) + if (before is null || !Enumerable.SequenceEqual(before, after)) { StorageChange(accountChanges, storageCell.Hash.BytesAsSpan, after.AsSpan()); } @@ -298,22 +299,27 @@ public void Dispose() private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) { + Span newValue = stackalloc byte[32]; + newValue.Clear(); + value.CopyTo(newValue[(32 - value.Length)..]); StorageChange storageChange = new() { BlockAccessIndex = _blockAccessIndex, - NewValue = new(value.ToArray()) + NewValue = new(newValue.ToArray()) }; byte[] storageKey = [.. key]; + if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) { - storageChanges = new(); + storageChanges = new(storageKey); } else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) { storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } storageChanges.Changes.Add(storageChange); + accountChanges.StorageChanges[storageKey] = storageChanges; } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index a9a8e6ba6fd..8573d45fcbf 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; @@ -20,7 +21,7 @@ namespace Nethermind.Core.BlockAccessLists; public AccountChanges(Address address) { Address = address; - StorageChanges = []; + StorageChanges = new(Bytes.Comparer); StorageReads = []; BalanceChanges = []; NonceChanges = []; @@ -30,7 +31,7 @@ public AccountChanges(Address address) public AccountChanges(Address address, SortedDictionary storageChanges, List storageReads, List balanceChanges, List nonceChanges, List codeChanges) { Address = address; - StorageChanges = storageChanges; + StorageChanges = new(Bytes.Comparer); StorageReads = storageReads; BalanceChanges = balanceChanges; NonceChanges = nonceChanges; diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 9ccbbbde209..8a9b46c26f2 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -142,7 +142,7 @@ protected virtual TransactionResult Execute(Transaction tx, ITxTracer tracer, Ex // restore is CallAndRestore - previous call, we will restore state after the execution bool restore = opts.HasFlag(ExecutionOptions.Restore); - // commit - is for standard execute, we will commit thee state after execution + // commit - is for standard execute, we will commit the state after execution // !commit - is for build up during block production, we won't commit state after each transaction to support rollbacks // we commit only after all block is constructed bool commit = opts.HasFlag(ExecutionOptions.Commit) || (!opts.HasFlag(ExecutionOptions.SkipValidation) && !spec.IsEip658Enabled); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 12ff0137830..db4d4c00ab0 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -6,6 +6,7 @@ using System.Linq; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -21,7 +22,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe Address address = ctx.DecodeAddress(); SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); - SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s)); + SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s), Bytes.Comparer); StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); From 749593282a624af0fbaee8774c7d95080c6017fd Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:09:32 +0100 Subject: [PATCH 059/215] work out hashes --- .../BlockAccessListTests.cs | 73 ++++++++++++------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 8e20aa05f02..7b189f2098b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Threading; using System.Threading.Tasks; using Autofac; @@ -146,8 +147,11 @@ public async Task Can_construct_BAL() using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis); InitWorldState(worldState); - ulong gasPrice = 2; - long gasLimit = 100000; + const ulong gasPrice = 2; + const long gasLimit = 100000; + const ulong timestamp = 1000000; + Hash256 parentHash = new("0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c"); + Transaction tx = Build.A.Transaction .WithTo(TestItem.AddressB) .WithSenderAddress(TestItem.AddressA) @@ -156,6 +160,8 @@ public async Task Can_construct_BAL() .WithGasLimit(gasLimit) .TestObject; + // add code change + BlockHeader header = Build.A.BlockHeader .WithBaseFee(1) .WithNumber(1) @@ -166,6 +172,8 @@ public async Task Can_construct_BAL() .WithBeneficiary(TestItem.AddressC) .WithParentBeaconBlockRoot(Hash256.Zero) .WithRequestsHash(new("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")) + .WithTimestamp(timestamp) + .WithParentHash(parentHash) .TestObject; Withdrawal withdrawal = new() @@ -219,39 +227,52 @@ public async Task Can_construct_BAL() // string kb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Key); // string vb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); - byte[] slot1 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - StorageChange value1 = new(0, Bytes32.Wrap(Convert.FromHexString("FF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); + // byte[] slot1 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + byte[] slot1 = ValueKeccak.Compute([.. Enumerable.Repeat(0, 32)]).ToByteArray(); + // StorageChange value1 = new(0, Bytes32.Wrap(Bytes.FromHexString("0xFF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); + StorageChange value1 = new(0, Bytes32.Wrap(parentHash.BytesToArray())); - byte[] slot2 = Convert.FromHexString("0E59911BBD9B80FD816896F0425C7A25DC9EB9092F5AC6264B432F6697F877C8"); + byte[] slot2 = ValueKeccak.Compute(new BigInteger((timestamp % 8191) + 8191).ToBytes32(true)).ToByteArray(); + byte[] slot2Old = Bytes.FromHexString("0x0E59911BBD9B80FD816896F0425C7A25DC9EB9092F5AC6264B432F6697F877C8"); StorageChange value2 = new(0, Bytes32.Zero); - byte[] slot3 = Convert.FromHexString("2CFFC05BC4230E308FCB837385A814EED1B4C90FB58BA2A0B8407649B9629B28"); - StorageChange value3 = new(0, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + byte[] slot3 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); + byte[] slot3Old = Bytes.FromHexString("0x2CFFC05BC4230E308FCB837385A814EED1B4C90FB58BA2A0B8407649B9629B28"); + // hash(timestamp) + StorageChange value3 = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); - byte[] slot4 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + // hash(0) + byte[] slot4 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); StorageChange value4 = new(2, Bytes32.Zero); - byte[] slot5 = Convert.FromHexString("405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + // hash(2) + byte[] slot5 = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); StorageChange value5 = new(2, Bytes32.Zero); - byte[] slot6 = Convert.FromHexString("B10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + // hash(1) + byte[] slot6 = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); StorageChange value6 = new(2, Bytes32.Zero); - // StorageChange value6 = new(0, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + // StorageChange value6 = new(0, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); - byte[] slot7 = Convert.FromHexString("C2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + // hash(3) + byte[] slot7 = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); StorageChange value7 = new(2, Bytes32.Zero); - byte[] slot8 = Convert.FromHexString("290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + // hash(0) + byte[] slot8 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); StorageChange value8 = new(2, Bytes32.Zero); - byte[] slot9 = Convert.FromHexString("405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + // hash(2) + byte[] slot9 = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); StorageChange value9 = new(2, Bytes32.Zero); - byte[] slota = Convert.FromHexString("B10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + // hash(1) + byte[] slota = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); StorageChange valuea = new(2, Bytes32.Zero); - // StorageChange valuea = new(2, Bytes32.Wrap(Convert.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); + // StorageChange valuea = new(2, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); - byte[] slotb = Convert.FromHexString("C2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + // hash(3) + byte[] slotb = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); StorageChange valueb = new(2, Bytes32.Zero); using (Assert.EnterMultipleScope()) @@ -310,7 +331,7 @@ public async Task Can_construct_BAL() { Address = Eip4788Constants.BeaconRootsAddress, StorageChanges = new(Bytes.Comparer) { { slot2, new SlotChanges(slot2, [value2]) }, { slot3, new SlotChanges(slot3, [value3]) }}, - StorageReads = [new(Bytes32.Wrap(Convert.FromHexString("0e59911bbd9b80fd816896f0425c7a25dc9eb9092f5ac6264b432f6697f877c8")))], + StorageReads = [new(Bytes32.Wrap(Bytes.FromHexString("0x0e59911bbd9b80fd816896f0425c7a25dc9eb9092f5ac6264b432f6697f877c8")))], BalanceChanges = [], NonceChanges = [], CodeChanges = [] @@ -321,10 +342,10 @@ public async Task Can_construct_BAL() Address = Eip7002Constants.WithdrawalRequestPredeployAddress, StorageChanges = new(Bytes.Comparer) { { slot4, new SlotChanges(slot4, [value4]) }, { slot5, new SlotChanges(slot5, [value5]) }, { slot6, new SlotChanges(slot6, [value6]) }, { slot7, new SlotChanges(slot7, [value7]) }}, StorageReads = [ - new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), - new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), - new(Bytes32.Wrap(Convert.FromHexString("405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), - new(Bytes32.Wrap(Convert.FromHexString("c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), + new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), + new(Bytes32.Wrap(Bytes.FromHexString("0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), + new(Bytes32.Wrap(Bytes.FromHexString("0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), ], BalanceChanges = [], NonceChanges = [], @@ -336,10 +357,10 @@ public async Task Can_construct_BAL() Address = Eip7251Constants.ConsolidationRequestPredeployAddress, StorageChanges = new(Bytes.Comparer) { { slot8, new SlotChanges(slot8, [value8]) }, { slot9, new SlotChanges(slot9, [value9]) }, { slota, new SlotChanges(slota, [valuea]) }, { slotb, new SlotChanges(slotb, [valueb]) }}, StorageReads = [ - new(Bytes32.Wrap(Convert.FromHexString("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), - new(Bytes32.Wrap(Convert.FromHexString("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), - new(Bytes32.Wrap(Convert.FromHexString("405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), - new(Bytes32.Wrap(Convert.FromHexString("c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), + new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), + new(Bytes32.Wrap(Bytes.FromHexString("0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), + new(Bytes32.Wrap(Bytes.FromHexString("0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), ], BalanceChanges = [], NonceChanges = [], From 11547d45698ab5827fe1708fc1a04e39c729ae13 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:07:10 +0100 Subject: [PATCH 060/215] simplify storage changes --- .../BlockAccessListTests.cs | 68 +++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 7b189f2098b..83a3c5710f0 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -227,53 +227,62 @@ public async Task Can_construct_BAL() // string kb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Key); // string vb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); - // byte[] slot1 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - byte[] slot1 = ValueKeccak.Compute([.. Enumerable.Repeat(0, 32)]).ToByteArray(); - // StorageChange value1 = new(0, Bytes32.Wrap(Bytes.FromHexString("0xFF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); - StorageChange value1 = new(0, Bytes32.Wrap(parentHash.BytesToArray())); + // byte[] slot0 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + byte[] slot0 = ToStorageSlot(0); + // StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xFF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); + StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); - byte[] slot2 = ValueKeccak.Compute(new BigInteger((timestamp % 8191) + 8191).ToBytes32(true)).ToByteArray(); + byte[] eip4788Slot2 = ValueKeccak.Compute(new BigInteger((timestamp % 8191) + 8191).ToBytes32(true)).ToByteArray(); byte[] slot2Old = Bytes.FromHexString("0x0E59911BBD9B80FD816896F0425C7A25DC9EB9092F5AC6264B432F6697F877C8"); - StorageChange value2 = new(0, Bytes32.Zero); + StorageChange calldataStorageChange = new(0, Bytes32.Zero); - byte[] slot3 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); + byte[] eip4788Slot1 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); byte[] slot3Old = Bytes.FromHexString("0x2CFFC05BC4230E308FCB837385A814EED1B4C90FB58BA2A0B8407649B9629B28"); // hash(timestamp) - StorageChange value3 = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); + StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); // hash(0) - byte[] slot4 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - StorageChange value4 = new(2, Bytes32.Zero); + // byte[] slot4 = ToStorageSlot(0); + // byte[] slot0Old = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + StorageChange zeroStorageChangeEnd = new(2, Bytes32.Zero); + // StorageChange value4 = new(2, Bytes32.Zero); // hash(2) - byte[] slot5 = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); - StorageChange value5 = new(2, Bytes32.Zero); + byte[] slot2 = ToStorageSlot(2); + // byte[] slot2Old = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + // StorageChange value5 = new(2, Bytes32.Zero); // hash(1) - byte[] slot6 = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); - StorageChange value6 = new(2, Bytes32.Zero); + byte[] slot1 = ToStorageSlot(1); + // byte[] slot1Old = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + // StorageChange value6 = new(2, Bytes32.Zero); // StorageChange value6 = new(0, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); // hash(3) - byte[] slot7 = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); - StorageChange value7 = new(2, Bytes32.Zero); + byte[] slot3 = ToStorageSlot(3); + // byte[] slot3Old = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + // StorageChange value7 = new(2, Bytes32.Zero); // hash(0) - byte[] slot8 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - StorageChange value8 = new(2, Bytes32.Zero); + // byte[] slot8 = ToStorageSlot(0); + // byte[] slot0Old = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); + // StorageChange value8 = new(2, Bytes32.Zero); // hash(2) - byte[] slot9 = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); - StorageChange value9 = new(2, Bytes32.Zero); + // byte[] slot2 = ToStorageSlot(2); + // byte[] slot2Old = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); + // StorageChange value9 = new(2, Bytes32.Zero); // hash(1) - byte[] slota = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); - StorageChange valuea = new(2, Bytes32.Zero); + // byte[] slota = ToStorageSlot(1); + // byte[] slotaOld = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); + // StorageChange valuea = new(2, Bytes32.Zero); // StorageChange valuea = new(2, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); // hash(3) - byte[] slotb = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); - StorageChange valueb = new(2, Bytes32.Zero); + // byte[] slotb = ToStorageSlot(3); + // byte[] slotbOld = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); + // StorageChange valueb = new(2, Bytes32.Zero); using (Assert.EnterMultipleScope()) { @@ -320,7 +329,7 @@ public async Task Can_construct_BAL() Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() { Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { slot1, new SlotChanges(slot1, [value1]) } }, + StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, StorageReads = [], BalanceChanges = [], NonceChanges = [], @@ -330,7 +339,7 @@ public async Task Can_construct_BAL() Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = new(Bytes.Comparer) { { slot2, new SlotChanges(slot2, [value2]) }, { slot3, new SlotChanges(slot3, [value3]) }}, + StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) }, { eip4788Slot2, new SlotChanges(eip4788Slot2, [calldataStorageChange]) } }, StorageReads = [new(Bytes32.Wrap(Bytes.FromHexString("0x0e59911bbd9b80fd816896f0425c7a25dc9eb9092f5ac6264b432f6697f877c8")))], BalanceChanges = [], NonceChanges = [], @@ -340,7 +349,7 @@ public async Task Can_construct_BAL() Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges() { Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageChanges = new(Bytes.Comparer) { { slot4, new SlotChanges(slot4, [value4]) }, { slot5, new SlotChanges(slot5, [value5]) }, { slot6, new SlotChanges(slot6, [value6]) }, { slot7, new SlotChanges(slot7, [value7]) }}, + StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, StorageReads = [ new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), @@ -355,7 +364,7 @@ public async Task Can_construct_BAL() Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges() { Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageChanges = new(Bytes.Comparer) { { slot8, new SlotChanges(slot8, [value8]) }, { slot9, new SlotChanges(slot9, [value9]) }, { slota, new SlotChanges(slota, [valuea]) }, { slotb, new SlotChanges(slotb, [valueb]) }}, + StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, StorageReads = [ new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), @@ -395,4 +404,7 @@ private static void InitWorldState(IWorldState worldState) worldState.RecalculateStateRoot(); // Hash256 stateRoot = worldState.StateRoot; } + + private static byte[] ToStorageSlot(ulong x) + => ValueKeccak.Compute(new BigInteger(x).ToBytes32(true)).ToByteArray(); } From 194fdb521fd7a3cbb1d479e5e07fa240ca9ffc24 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:10:07 +0100 Subject: [PATCH 061/215] tidy --- .../BlockAccessListTests.cs | 80 ++----------------- 1 file changed, 6 insertions(+), 74 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 83a3c5710f0..b6c1d0e97b6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -201,88 +201,20 @@ public async Task Can_construct_BAL() AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; - // string k1 = Convert.ToHexString(eip2935Changes.StorageChanges.First().Key); - // string v1 = Convert.ToHexString(eip2935Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; - // string k2 = Convert.ToHexString(eip4788Changes.StorageChanges.First().Key); - // string v2 = Convert.ToHexString(eip4788Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); - // string k3 = Convert.ToHexString(eip4788Changes.StorageChanges.ElementAt(1).Key); - // string v3 = Convert.ToHexString(eip4788Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); AccountChanges eip7002Changes = accountChanges[Eip7002Constants.WithdrawalRequestPredeployAddress]; - // string k4 = Convert.ToHexString(eip7002Changes.StorageChanges.First().Key); - // string v4 = Convert.ToHexString(eip7002Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); - // string k5 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(1).Key); - // string v5 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); - // string k6 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(2).Key); - // string v6 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(2).Value.Changes.First().NewValue.Unwrap()); - // string k7 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(3).Key); - // string v7 = Convert.ToHexString(eip7002Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); AccountChanges eip7251Changes = accountChanges[Eip7251Constants.ConsolidationRequestPredeployAddress]; - // string k8 = Convert.ToHexString(eip7251Changes.StorageChanges.First().Key); - // string v8 = Convert.ToHexString(eip7251Changes.StorageChanges.First().Value.Changes.First().NewValue.Unwrap()); - // string k9 = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(1).Key); - // string v9 = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(1).Value.Changes.First().NewValue.Unwrap()); - // string ka = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(2).Key); - // string va = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(2).Value.Changes.First().NewValue.Unwrap()); - // string kb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Key); - // string vb = Convert.ToHexString(eip7251Changes.StorageChanges.ElementAt(3).Value.Changes.First().NewValue.Unwrap()); - - // byte[] slot0 = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - byte[] slot0 = ToStorageSlot(0); - // StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xFF483E972A04A9A62BB4B7D04AE403C615604E4090521ECC5BB7AF67F71BE09C"))); - StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); + byte[] slot0 = ToStorageSlot(0); + byte[] slot1 = ToStorageSlot(1); + byte[] slot2 = ToStorageSlot(2); + byte[] slot3 = ToStorageSlot(3); + byte[] eip4788Slot1 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); byte[] eip4788Slot2 = ValueKeccak.Compute(new BigInteger((timestamp % 8191) + 8191).ToBytes32(true)).ToByteArray(); - byte[] slot2Old = Bytes.FromHexString("0x0E59911BBD9B80FD816896F0425C7A25DC9EB9092F5AC6264B432F6697F877C8"); + StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); StorageChange calldataStorageChange = new(0, Bytes32.Zero); - - byte[] eip4788Slot1 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); - byte[] slot3Old = Bytes.FromHexString("0x2CFFC05BC4230E308FCB837385A814EED1B4C90FB58BA2A0B8407649B9629B28"); - // hash(timestamp) StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); - - // hash(0) - // byte[] slot4 = ToStorageSlot(0); - // byte[] slot0Old = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); StorageChange zeroStorageChangeEnd = new(2, Bytes32.Zero); - // StorageChange value4 = new(2, Bytes32.Zero); - - // hash(2) - byte[] slot2 = ToStorageSlot(2); - // byte[] slot2Old = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); - // StorageChange value5 = new(2, Bytes32.Zero); - - // hash(1) - byte[] slot1 = ToStorageSlot(1); - // byte[] slot1Old = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); - // StorageChange value6 = new(2, Bytes32.Zero); - // StorageChange value6 = new(0, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); - - // hash(3) - byte[] slot3 = ToStorageSlot(3); - // byte[] slot3Old = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); - // StorageChange value7 = new(2, Bytes32.Zero); - - // hash(0) - // byte[] slot8 = ToStorageSlot(0); - // byte[] slot0Old = Bytes.FromHexString("0x290DECD9548B62A8D60345A988386FC84BA6BC95484008F6362F93160EF3E563"); - // StorageChange value8 = new(2, Bytes32.Zero); - - // hash(2) - // byte[] slot2 = ToStorageSlot(2); - // byte[] slot2Old = Bytes.FromHexString("0x405787FA12A823E0F2B7631CC41B3BA8828B3321CA811111FA75CD3AA3BB5ACE"); - // StorageChange value9 = new(2, Bytes32.Zero); - - // hash(1) - // byte[] slota = ToStorageSlot(1); - // byte[] slotaOld = Bytes.FromHexString("0xB10E2D527612073B26EECDFD717E6A320CF44B4AFAC2B0732D9FCBE2B7FA0CF6"); - // StorageChange valuea = new(2, Bytes32.Zero); - // StorageChange valuea = new(2, Bytes32.Wrap(Bytes.FromHexString("00000000000000000000000000000000000000000000000000000000000F4240"))); - - // hash(3) - // byte[] slotb = ToStorageSlot(3); - // byte[] slotbOld = Bytes.FromHexString("0xC2575A0E9E593C00F959F8C92F12DB2869C3395A3B0502D05E2516446F71F85B"); - // StorageChange valueb = new(2, Bytes32.Zero); using (Assert.EnterMultipleScope()) { From df41d5d2c7e4d8715d909e94017b47201c0edcfe Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:15:08 +0100 Subject: [PATCH 062/215] simplify more --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index b6c1d0e97b6..9224fbe318b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -209,8 +209,8 @@ public async Task Can_construct_BAL() byte[] slot1 = ToStorageSlot(1); byte[] slot2 = ToStorageSlot(2); byte[] slot3 = ToStorageSlot(3); - byte[] eip4788Slot1 = ValueKeccak.Compute(new BigInteger(timestamp % 8191).ToBytes32(true)).ToByteArray(); - byte[] eip4788Slot2 = ValueKeccak.Compute(new BigInteger((timestamp % 8191) + 8191).ToBytes32(true)).ToByteArray(); + byte[] eip4788Slot1 = ToStorageSlot(timestamp % 8191); + byte[] eip4788Slot2 = ToStorageSlot((timestamp % 8191) + 8191); StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); StorageChange calldataStorageChange = new(0, Bytes32.Zero); StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); From 64d12622435ddf310b7b2fd868eaa03682e3f0ed Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:36:15 +0100 Subject: [PATCH 063/215] tidy storage reads --- .../BlockAccessListTests.cs | 19 +++++++++++-------- .../BlockAccessLists/AccountChanges.cs | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 9224fbe318b..4ac406326e1 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -283,10 +283,10 @@ public async Task Can_construct_BAL() Address = Eip7002Constants.WithdrawalRequestPredeployAddress, StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, StorageReads = [ - new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), - new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), - new(Bytes32.Wrap(Bytes.FromHexString("0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), - new(Bytes32.Wrap(Bytes.FromHexString("0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + ToStorageRead(slot1), + ToStorageRead(slot0), + ToStorageRead(slot2), + ToStorageRead(slot3), ], BalanceChanges = [], NonceChanges = [], @@ -298,10 +298,10 @@ public async Task Can_construct_BAL() Address = Eip7251Constants.ConsolidationRequestPredeployAddress, StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, StorageReads = [ - new(Bytes32.Wrap(Bytes.FromHexString("0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))), - new(Bytes32.Wrap(Bytes.FromHexString("0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563"))), - new(Bytes32.Wrap(Bytes.FromHexString("0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace"))), - new(Bytes32.Wrap(Bytes.FromHexString("0xc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b"))), + ToStorageRead(slot1), + ToStorageRead(slot0), + ToStorageRead(slot2), + ToStorageRead(slot3), ], BalanceChanges = [], NonceChanges = [], @@ -339,4 +339,7 @@ private static void InitWorldState(IWorldState worldState) private static byte[] ToStorageSlot(ulong x) => ValueKeccak.Compute(new BigInteger(x).ToBytes32(true)).ToByteArray(); + + private static StorageRead ToStorageRead(byte[] x) + => new(Bytes32.Wrap(x)); } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index 8573d45fcbf..b7afb2f2d0b 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -13,6 +13,7 @@ namespace Nethermind.Core.BlockAccessLists; { public Address Address { get; init; } public SortedDictionary StorageChanges { get; init; } + // should be ordered sets / lists public List StorageReads { get; init; } public List BalanceChanges { get; init; } public List NonceChanges { get; init; } From fc16d59e7645b58965542cfc74252d76a01085a9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:43:37 +0100 Subject: [PATCH 064/215] tidy 4788 --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 6 +++--- src/Nethermind/Nethermind.Core/Eip4788Constants.cs | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 4ac406326e1..19a7854731a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -209,8 +209,8 @@ public async Task Can_construct_BAL() byte[] slot1 = ToStorageSlot(1); byte[] slot2 = ToStorageSlot(2); byte[] slot3 = ToStorageSlot(3); - byte[] eip4788Slot1 = ToStorageSlot(timestamp % 8191); - byte[] eip4788Slot2 = ToStorageSlot((timestamp % 8191) + 8191); + byte[] eip4788Slot1 = ToStorageSlot(timestamp % Eip4788Constants.RingBufferSize); + byte[] eip4788Slot2 = ToStorageSlot((timestamp % Eip4788Constants.RingBufferSize) + Eip4788Constants.RingBufferSize); StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); StorageChange calldataStorageChange = new(0, Bytes32.Zero); StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); @@ -272,7 +272,7 @@ public async Task Can_construct_BAL() { Address = Eip4788Constants.BeaconRootsAddress, StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) }, { eip4788Slot2, new SlotChanges(eip4788Slot2, [calldataStorageChange]) } }, - StorageReads = [new(Bytes32.Wrap(Bytes.FromHexString("0x0e59911bbd9b80fd816896f0425c7a25dc9eb9092f5ac6264b432f6697f877c8")))], + StorageReads = [ToStorageRead(eip4788Slot2)], BalanceChanges = [], NonceChanges = [], CodeChanges = [] diff --git a/src/Nethermind/Nethermind.Core/Eip4788Constants.cs b/src/Nethermind/Nethermind.Core/Eip4788Constants.cs index af10e162a95..2995bceea8a 100644 --- a/src/Nethermind/Nethermind.Core/Eip4788Constants.cs +++ b/src/Nethermind/Nethermind.Core/Eip4788Constants.cs @@ -14,4 +14,9 @@ public static class Eip4788Constants /// Gets the BEACON_ROOTS_ADDRESS parameter. ///
public static readonly Address BeaconRootsAddress = new("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"); + + /// + /// The HISTORY_SERVE_WINDOW parameter. + /// + public static readonly ulong RingBufferSize = 8191; } From ebb187a26a80388ba3f6882c1f350cb6d3676b6a Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:34:35 +0100 Subject: [PATCH 065/215] use sorted lists and sets for BAL --- .../BlockAccessListTests.cs | 33 ++++++++++++++----- .../Tracing/BlockAccessTracer.cs | 18 +++++----- .../BlockAccessLists/AccountChanges.cs | 13 ++++---- .../BlockAccessLists/StorageRead.cs | 6 +++- .../Eip7928/AccountChangesDecoder.cs | 21 +++++++----- 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 19a7854731a..27db8ee6b51 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -111,14 +111,31 @@ public void Can_encode_and_decode() { slotChanges.Slot, slotChanges } }; + SortedList balanceChangesList = new() + { + { balanceChange.BlockAccessIndex, balanceChange }, + { balanceChange.BlockAccessIndex, balanceChange } + }; + + SortedList nonceChangesList = new() + { + { nonceChange.BlockAccessIndex, nonceChange }, + { nonceChange.BlockAccessIndex, nonceChange } + }; + + SortedList codeChangesList = new() + { + { codeChange.BlockAccessIndex, codeChange }, + }; + AccountChanges accountChanges = new() { Address = TestItem.AddressA, StorageChanges = storageChangesDict, StorageReads = [storageRead, storageRead], - BalanceChanges = [balanceChange, balanceChange], - NonceChanges = [nonceChange, nonceChange], - CodeChanges = [codeChange] + BalanceChanges = balanceChangesList, + NonceChanges = nonceChangesList, + CodeChanges = codeChangesList }; byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); @@ -223,8 +240,8 @@ public async Task Can_construct_BAL() Address = TestItem.AddressA, StorageChanges = [], StorageReads = [], - BalanceChanges = [new(1, _accountBalance - gasPrice * GasCostOf.Transaction)], - NonceChanges = [new(1, 1)], + BalanceChanges = new SortedList{ { 1, new(1, _accountBalance - gasPrice * GasCostOf.Transaction) } }, + NonceChanges = new SortedList{ { 1, new(1, 1) } }, CodeChanges = [] })); @@ -243,8 +260,8 @@ public async Task Can_construct_BAL() Address = TestItem.AddressC, StorageChanges = [], StorageReads = [], - BalanceChanges = [new(1, new UInt256(GasCostOf.Transaction))], - NonceChanges = [new(1, 0)], + BalanceChanges = new SortedList{ { 1, new(1, new UInt256(GasCostOf.Transaction)) } }, + NonceChanges = new SortedList{ { 1, new(1, 0) } }, CodeChanges = [] })); @@ -253,7 +270,7 @@ public async Task Can_construct_BAL() Address = TestItem.AddressD, StorageChanges = [], StorageReads = [], - BalanceChanges = [new(2, 1.GWei())], + BalanceChanges = new SortedList{ { 2, new(2, 1.GWei()) } }, NonceChanges = [], CodeChanges = [] })); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index d22cbc63b0d..4d2b910e15b 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -97,12 +97,12 @@ public void ReportBalanceChange(Address address, UInt256? before, UInt256? after return; } - List balanceChanges = accountChanges.BalanceChanges; - if (balanceChanges is not [] && balanceChanges[^1].BlockAccessIndex == _blockAccessIndex) + SortedList balanceChanges = accountChanges.BalanceChanges; + if (balanceChanges.Count != 0 && balanceChanges.Last().Key == _blockAccessIndex) { balanceChanges.RemoveAt(balanceChanges.Count - 1); } - balanceChanges.Add(balanceChange); + balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } public void ReportCodeChange(Address address, byte[] before, byte[] after) @@ -119,12 +119,12 @@ public void ReportCodeChange(Address address, byte[] before, byte[] after) _bal.AccountChanges.Add(address, accountChanges); } - List codeChanges = accountChanges.CodeChanges; - if (codeChanges is not [] && codeChanges[^1].BlockAccessIndex == _blockAccessIndex) + SortedList codeChanges = accountChanges.CodeChanges; + if (codeChanges.Count != 0 && codeChanges.Last().Key == _blockAccessIndex) { codeChanges.RemoveAt(codeChanges.Count - 1); } - codeChanges.Add(codeChange); + codeChanges.Add(codeChange.BlockAccessIndex, codeChange); } public void ReportNonceChange(Address address, UInt256? before, UInt256? after) @@ -141,12 +141,12 @@ public void ReportNonceChange(Address address, UInt256? before, UInt256? after) _bal.AccountChanges.Add(address, accountChanges); } - List nonceChanges = accountChanges.NonceChanges; - if (nonceChanges is not [] && nonceChanges[^1].BlockAccessIndex == _blockAccessIndex) + SortedList nonceChanges = accountChanges.NonceChanges; + if (nonceChanges.Count != 0 && nonceChanges.Last().Key == _blockAccessIndex) { nonceChanges.RemoveAt(nonceChanges.Count - 1); } - nonceChanges.Add(nonceChange); + nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); } public void ReportAccountRead(Address address) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index b7afb2f2d0b..6099077a8d7 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -13,11 +13,10 @@ namespace Nethermind.Core.BlockAccessLists; { public Address Address { get; init; } public SortedDictionary StorageChanges { get; init; } - // should be ordered sets / lists - public List StorageReads { get; init; } - public List BalanceChanges { get; init; } - public List NonceChanges { get; init; } - public List CodeChanges { get; init; } + public SortedSet StorageReads { get; init; } + public SortedList BalanceChanges { get; init; } + public SortedList NonceChanges { get; init; } + public SortedList CodeChanges { get; init; } public AccountChanges(Address address) { @@ -29,10 +28,10 @@ public AccountChanges(Address address) CodeChanges = []; } - public AccountChanges(Address address, SortedDictionary storageChanges, List storageReads, List balanceChanges, List nonceChanges, List codeChanges) + public AccountChanges(Address address, SortedDictionary storageChanges, SortedSet storageReads, SortedList balanceChanges, SortedList nonceChanges, SortedList codeChanges) { Address = address; - StorageChanges = new(Bytes.Comparer); + StorageChanges = storageChanges; StorageReads = storageReads; BalanceChanges = balanceChanges; NonceChanges = nonceChanges; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs index 3cae4357868..aee73820d16 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -3,13 +3,17 @@ using System; using System.Linq; +using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; -public readonly struct StorageRead(Bytes32 key) : IEquatable +public readonly struct StorageRead(Bytes32 key) : IEquatable, IComparable { public Bytes32 Key { get; init; } = key; + public int CompareTo(StorageRead other) + => Bytes.BytesComparer.Compare(Key.Unwrap(), other.Key.Unwrap()); + public readonly bool Equals(StorageRead other) => Key.Unwrap().SequenceEqual(other.Key.Unwrap()); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index db4d4c00ab0..ee96760b0a0 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -25,8 +25,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s), Bytes.Comparer); StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); + SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); + SortedList nonceChangesList = new(nonceChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); + SortedList codeChangesList = new(codeChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); if (codeChanges.Length > Eip7928Constants.MaxCodeChanges) { @@ -43,9 +46,9 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe Address = address, StorageChanges = slotChangesMap, StorageReads = [.. storageReads], - BalanceChanges = [.. balanceChanges], - NonceChanges = [.. nonceChanges], - CodeChanges = [.. codeChanges] + BalanceChanges = balanceChangesList, + NonceChanges = nonceChangesList, + CodeChanges = codeChangesList }; } @@ -68,9 +71,9 @@ public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehavi stream.Encode(item.Address); stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); - stream.EncodeArray([.. item.BalanceChanges], rlpBehaviors); - stream.EncodeArray([.. item.NonceChanges], rlpBehaviors); - stream.EncodeArray([.. item.CodeChanges], rlpBehaviors); + stream.EncodeArray([.. item.BalanceChanges.Values], rlpBehaviors); + stream.EncodeArray([.. item.NonceChanges.Values], rlpBehaviors); + stream.EncodeArray([.. item.CodeChanges.Values], rlpBehaviors); } private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) @@ -90,21 +93,21 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio storageReadsLen = Rlp.LengthOfSequence(storageReadsLen); int balanceChangesLen = 0; - foreach (BalanceChange balanceChange in item.BalanceChanges) + foreach (BalanceChange balanceChange in item.BalanceChanges.Values) { balanceChangesLen += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); } balanceChangesLen = Rlp.LengthOfSequence(balanceChangesLen); int nonceChangesLen = 0; - foreach (NonceChange nonceChange in item.NonceChanges) + foreach (NonceChange nonceChange in item.NonceChanges.Values) { nonceChangesLen += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); } nonceChangesLen = Rlp.LengthOfSequence(nonceChangesLen); int codeChangesLen = 0; - foreach (CodeChange codeChange in item.CodeChanges) + foreach (CodeChange codeChange in item.CodeChanges.Values) { codeChangesLen += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); } From cafee52e3341eea3bd5e9b3ae24319ddb3954a1e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:47:31 +0100 Subject: [PATCH 066/215] separate test constants --- .../BlockAccessListTests.cs | 10 ++++------ .../Eip2935TestConstants.cs | 17 +++++++++++++++++ .../Eip4788TestConstants.cs | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs create mode 100644 src/Nethermind/Nethermind.Core.Test/Eip4788TestConstants.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 27db8ee6b51..6a18f0a9974 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -334,13 +334,11 @@ private static void InitWorldState(IWorldState worldState) { worldState.CreateAccount(TestItem.AddressA, _accountBalance); - worldState.CreateAccount(Eip2935Constants.BlockHashHistoryAddress, 0, 1); - byte[] eip2935Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500"); - worldState.InsertCode(Eip2935Constants.BlockHashHistoryAddress, ValueKeccak.Compute(eip2935Code), eip2935Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip2935Constants.BlockHashHistoryAddress, 0, Eip2935TestConstants.Nonce); + worldState.InsertCode(Eip2935Constants.BlockHashHistoryAddress, Eip2935TestConstants.CodeHash, Eip2935TestConstants.Code, _specProvider.GenesisSpec); - worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 0, 1); - byte[] eip4788Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500"); - worldState.InsertCode(Eip4788Constants.BeaconRootsAddress, ValueKeccak.Compute(eip4788Code), eip4788Code, _specProvider.GenesisSpec); + worldState.CreateAccount(Eip4788Constants.BeaconRootsAddress, 0, Eip4788TestConstants.Nonce); + worldState.InsertCode(Eip4788Constants.BeaconRootsAddress, Eip4788TestConstants.CodeHash, Eip4788TestConstants.Code, _specProvider.GenesisSpec); worldState.CreateAccount(Eip7002Constants.WithdrawalRequestPredeployAddress, 0, Eip7002TestConstants.Nonce); worldState.InsertCode(Eip7002Constants.WithdrawalRequestPredeployAddress, Eip7002TestConstants.CodeHash, Eip7002TestConstants.Code, _specProvider.GenesisSpec); diff --git a/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs b/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs new file mode 100644 index 00000000000..93edd3b91e1 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; + +namespace Nethermind.Core.Test; + +public static class Eip2935TestConstants +{ + public static readonly byte[] Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500"); + + public static readonly ValueHash256 CodeHash = ValueKeccak.Compute(Code); + + public static readonly UInt256 Nonce = 1; +} diff --git a/src/Nethermind/Nethermind.Core.Test/Eip4788TestConstants.cs b/src/Nethermind/Nethermind.Core.Test/Eip4788TestConstants.cs new file mode 100644 index 00000000000..a581508379e --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Eip4788TestConstants.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Int256; + +namespace Nethermind.Core.Test; + +public static class Eip4788TestConstants +{ + public static readonly byte[] Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500"); + + public static readonly ValueHash256 CodeHash = ValueKeccak.Compute(Code); + + public static readonly UInt256 Nonce = 1; +} From 965f7befa55a096cd2cc0cdb8a044f0fc22c9edf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:42:25 +0100 Subject: [PATCH 067/215] add code change to test --- .../BlockAccessListTests.cs | 49 ++++++++++++++----- .../Eip2935TestConstants.cs | 1 + 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 6a18f0a9974..30f1e4528af 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -164,6 +164,7 @@ public async Task Can_construct_BAL() using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis); InitWorldState(worldState); + const long gasUsed = 92100; const ulong gasPrice = 2; const long gasLimit = 100000; const ulong timestamp = 1000000; @@ -177,14 +178,22 @@ public async Task Can_construct_BAL() .WithGasLimit(gasLimit) .TestObject; - // add code change + Transaction tx2 = Build.A.Transaction + .WithTo(null) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithNonce(1) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .WithCode(Eip2935TestConstants.InitCode) + .TestObject; BlockHeader header = Build.A.BlockHeader .WithBaseFee(1) .WithNumber(1) - .WithGasUsed(21000) - .WithReceiptsRoot(new("0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2")) - .WithStateRoot(new("0x869b0dea3e9d18f71753c2b64142901e11b6be272ddbb8975f32851528d30c36")) + .WithGasUsed(gasUsed) + .WithReceiptsRoot(new("0x6ade9745ba09d7b426314ec12280d510e4811867c2b56f215385842ffb43edf9")) + .WithStateRoot(new("0x18a32a11a465a81922828c9b2289924065a37a6961cbbef6633e57b465c11c9d")) .WithBlobGasUsed(0) .WithBeneficiary(TestItem.AddressC) .WithParentBeaconBlockRoot(Hash256.Zero) @@ -202,7 +211,7 @@ public async Task Can_construct_BAL() }; Block block = Build.A.Block - .WithTransactions(tx) + .WithTransactions([tx, tx2]) .WithBaseFeePerGas(1) .WithWithdrawals([withdrawal]) .WithHeader(header).TestObject; @@ -211,12 +220,15 @@ public async Task Can_construct_BAL() BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(8)); + Assert.That(accountChanges, Has.Count.EqualTo(9)); + + Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); AccountChanges addressAChanges = accountChanges[TestItem.AddressA]; AccountChanges addressBChanges = accountChanges[TestItem.AddressB]; AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; + AccountChanges newContractChanges = accountChanges[newContractAddress]; AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; AccountChanges eip7002Changes = accountChanges[Eip7002Constants.WithdrawalRequestPredeployAddress]; @@ -231,7 +243,10 @@ public async Task Can_construct_BAL() StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); StorageChange calldataStorageChange = new(0, Bytes32.Zero); StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); - StorageChange zeroStorageChangeEnd = new(2, Bytes32.Zero); + StorageChange zeroStorageChangeEnd = new(3, Bytes32.Zero); + + UInt256 addressABalance = _accountBalance - gasPrice * GasCostOf.Transaction; + UInt256 addressABalance2 = _accountBalance - gasPrice * gasUsed; using (Assert.EnterMultipleScope()) { @@ -240,8 +255,8 @@ public async Task Can_construct_BAL() Address = TestItem.AddressA, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList{ { 1, new(1, _accountBalance - gasPrice * GasCostOf.Transaction) } }, - NonceChanges = new SortedList{ { 1, new(1, 1) } }, + BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2)} }, + NonceChanges = new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) } }, CodeChanges = [] })); @@ -260,8 +275,8 @@ public async Task Can_construct_BAL() Address = TestItem.AddressC, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList{ { 1, new(1, new UInt256(GasCostOf.Transaction)) } }, - NonceChanges = new SortedList{ { 1, new(1, 0) } }, + BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsed))} }, + NonceChanges = new SortedList { { 1, new(1, 0) } }, CodeChanges = [] })); @@ -270,11 +285,21 @@ public async Task Can_construct_BAL() Address = TestItem.AddressD, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList{ { 2, new(2, 1.GWei()) } }, + BalanceChanges = new SortedList { { 3, new(3, 1.GWei()) } }, NonceChanges = [], CodeChanges = [] })); + Assert.That(newContractChanges, Is.EqualTo(new AccountChanges() + { + Address = newContractAddress, + StorageChanges = [], + StorageReads = [], + BalanceChanges = [], + NonceChanges = new SortedList { { 2, new(2, 1) } }, + CodeChanges = new SortedList { { 2, new(2, Eip2935TestConstants.Code) } } + })); + Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() { Address = Eip2935Constants.BlockHashHistoryAddress, diff --git a/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs b/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs index 93edd3b91e1..189ec1c9136 100644 --- a/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs +++ b/src/Nethermind/Nethermind.Core.Test/Eip2935TestConstants.cs @@ -11,6 +11,7 @@ public static class Eip2935TestConstants { public static readonly byte[] Code = Bytes.FromHexString("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500"); + public static readonly byte[] InitCode = Bytes.FromHexString("0x60538060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500"); public static readonly ValueHash256 CodeHash = ValueKeccak.Compute(Code); public static readonly UInt256 Nonce = 1; From 8c2503ca3bf9e53355c4d8b8a8698a75ce9a46fb Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 5 Sep 2025 12:44:20 +0100 Subject: [PATCH 068/215] don't record zero nonce change --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 2 +- .../Nethermind.Blockchain/Tracing/BlockAccessTracer.cs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 30f1e4528af..c1dc7ff87aa 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -276,7 +276,7 @@ public async Task Can_construct_BAL() StorageChanges = [], StorageReads = [], BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsed))} }, - NonceChanges = new SortedList { { 1, new(1, 0) } }, + NonceChanges = [], CodeChanges = [] })); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 4d2b910e15b..a784450baee 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -129,6 +129,11 @@ public void ReportCodeChange(Address address, byte[] before, byte[] after) public void ReportNonceChange(Address address, UInt256? before, UInt256? after) { + if (after == 0) + { + return; + } + NonceChange nonceChange = new() { BlockAccessIndex = _blockAccessIndex, From bb4d82fac39bdbcb70559dfe0fef3cb0b447f279 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 5 Sep 2025 16:13:38 +0100 Subject: [PATCH 069/215] refactor from tracer to inside bal --- .../BlockAccessListTests.cs | 28 ++- .../Tracing/BlockAccessTracer.cs | 137 +------------- .../BlockAccessLists/BlockAccessList.cs | 177 +++++++++++++++++- .../Eip7928/BlockAccessListDecoder.cs | 9 +- 4 files changed, 196 insertions(+), 155 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index c1dc7ff87aa..b9e48e479f0 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -50,7 +50,7 @@ public void Empty_account_changes() tracer.StartNewTxTrace(block.Transactions[0]); tracer.MarkAsSuccess(TestItem.AddressA, 100, [], [], TestItem.KeccakF); - Assert.That(tracer.BlockAccessList.AccountChanges, Has.Count.EqualTo(0)); + Assert.That(tracer.BlockAccessList.GetAccountChanges().Count, Is.Zero); } [Test] @@ -146,10 +146,7 @@ public void Can_encode_and_decode() { accountChanges.Address, accountChanges } }; - BlockAccessList blockAccessList = new() - { - AccountChanges = accountChangesDict - }; + BlockAccessList blockAccessList = new(accountChangesDict); byte[] blockAccessListBytes = Rlp.Encode(blockAccessList, RlpBehaviors.None).Bytes; BlockAccessList blockAccessListDecoded = Rlp.Decode(blockAccessListBytes, RlpBehaviors.None); Assert.That(blockAccessList, Is.EqualTo(blockAccessListDecoded)); @@ -219,20 +216,19 @@ public async Task Can_construct_BAL() (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); - SortedDictionary accountChanges = blockAccessList.AccountChanges; - Assert.That(accountChanges, Has.Count.EqualTo(9)); + Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); - AccountChanges addressAChanges = accountChanges[TestItem.AddressA]; - AccountChanges addressBChanges = accountChanges[TestItem.AddressB]; - AccountChanges addressCChanges = accountChanges[TestItem.AddressC]; - AccountChanges addressDChanges = accountChanges[TestItem.AddressD]; - AccountChanges newContractChanges = accountChanges[newContractAddress]; - AccountChanges eip2935Changes = accountChanges[Eip2935Constants.BlockHashHistoryAddress]; - AccountChanges eip4788Changes = accountChanges[Eip4788Constants.BeaconRootsAddress]; - AccountChanges eip7002Changes = accountChanges[Eip7002Constants.WithdrawalRequestPredeployAddress]; - AccountChanges eip7251Changes = accountChanges[Eip7251Constants.ConsolidationRequestPredeployAddress]; + AccountChanges addressAChanges = blockAccessList.GetAccountChanges(TestItem.AddressA)!.Value; + AccountChanges addressBChanges = blockAccessList.GetAccountChanges(TestItem.AddressB)!.Value; + AccountChanges addressCChanges = blockAccessList.GetAccountChanges(TestItem.AddressC)!.Value; + AccountChanges addressDChanges = blockAccessList.GetAccountChanges(TestItem.AddressD)!.Value; + AccountChanges newContractChanges = blockAccessList.GetAccountChanges(newContractAddress)!.Value; + AccountChanges eip2935Changes = blockAccessList.GetAccountChanges(Eip2935Constants.BlockHashHistoryAddress)!.Value; + AccountChanges eip4788Changes = blockAccessList.GetAccountChanges(Eip4788Constants.BeaconRootsAddress)!.Value; + AccountChanges eip7002Changes = blockAccessList.GetAccountChanges(Eip7002Constants.WithdrawalRequestPredeployAddress)!.Value; + AccountChanges eip7251Changes = blockAccessList.GetAccountChanges(Eip7251Constants.ConsolidationRequestPredeployAddress)!.Value; byte[] slot0 = ToStorageSlot(0); byte[] slot1 = ToStorageSlot(1); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index a784450baee..90e9c9ef2a9 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; @@ -60,107 +59,23 @@ public void SetOperationMemorySize(ulong newSize) { } public void ReportMemoryChange(long offset, in ReadOnlySpan data) { } public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) - { - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - if (currentValue != newValue) - { - StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, newValue); - } - } + => _bal.AddStorageChange(address, storageIndex, newValue, currentValue); public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) { } public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) { } public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) - { - BalanceChange balanceChange = new() - { - BlockAccessIndex = _blockAccessIndex, - PostBalance = after!.Value - }; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - // don't add zero balance transfers, but add empty account changes - if ((before ?? 0) == after) - { - return; - } - - SortedList balanceChanges = accountChanges.BalanceChanges; - if (balanceChanges.Count != 0 && balanceChanges.Last().Key == _blockAccessIndex) - { - balanceChanges.RemoveAt(balanceChanges.Count - 1); - } - balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); - } + => _bal.AddBalanceChange(address, before, after); public void ReportCodeChange(Address address, byte[] before, byte[] after) - { - CodeChange codeChange = new() - { - BlockAccessIndex = _blockAccessIndex, - NewCode = after - }; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - SortedList codeChanges = accountChanges.CodeChanges; - if (codeChanges.Count != 0 && codeChanges.Last().Key == _blockAccessIndex) - { - codeChanges.RemoveAt(codeChanges.Count - 1); - } - codeChanges.Add(codeChange.BlockAccessIndex, codeChange); - } + => _bal.AddCodeChange(address, before, after); public void ReportNonceChange(Address address, UInt256? before, UInt256? after) - { - if (after == 0) - { - return; - } - - NonceChange nonceChange = new() - { - BlockAccessIndex = _blockAccessIndex, - NewNonce = (ulong)after - }; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - SortedList nonceChanges = accountChanges.NonceChanges; - if (nonceChanges.Count != 0 && nonceChanges.Last().Key == _blockAccessIndex) - { - nonceChanges.RemoveAt(nonceChanges.Count - 1); - } - nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); - } + => _bal.AddNonceChange(address, (ulong)after); public void ReportAccountRead(Address address) - { - if (!_bal.AccountChanges.ContainsKey(address)) - { - _bal.AccountChanges.Add(address, new(address)); - } - } + => _bal.AddAccountRead(address); public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) { @@ -168,37 +83,10 @@ public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan } public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) - { - Address address = storageCell.Address; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - if (before is null || !Enumerable.SequenceEqual(before, after)) - { - StorageChange(accountChanges, storageCell.Hash.BytesAsSpan, after.AsSpan()); - } - } + => _bal.AddStorageChange(storageCell, before, after); public void ReportStorageRead(in StorageCell storageCell) - { - StorageRead storageRead = new() - { - Key = new(storageCell.Hash.ToByteArray()) - }; - Address address = storageCell.Address; - - if (!_bal.AccountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _bal.AccountChanges.Add(address, accountChanges); - } - - accountChanges.StorageReads.Add(storageRead); - } + => _bal.AddStorageRead(storageCell); public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) { } @@ -244,7 +132,7 @@ public void ReportFees(UInt256 fees, UInt256 burntFees) { } // public ITxTracer InnerTracer => _currentTxTracer; - public int TakeSnapshot() => _bal.AccountChanges.Count; + public int TakeSnapshot() => 0; public void Restore(int snapshot) { @@ -263,19 +151,14 @@ public void ReportReward(Address author, string rewardType, UInt256 rewardValue) public void StartNewBlockTrace(Block block) { Block = block; - _blockAccessIndex = 0; _bal = new(); } public ITxTracer StartNewTxTrace(Transaction? tx) - { - return this; - } + => this; public void EndTxTrace() - { - _blockAccessIndex++; - } + => _bal.IncrementBlockAccessIndex(); public void EndBlockTrace() { diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index c2dc8ddf1ac..fb456f6c245 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -4,35 +4,200 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; -public readonly struct BlockAccessList : IEquatable +public struct BlockAccessList : IEquatable { - public SortedDictionary AccountChanges { get; init; } + private SortedDictionary _accountChanges { get; init; } + private ushort _blockAccessIndex = 0; public BlockAccessList() { - AccountChanges = []; + _accountChanges = []; } public BlockAccessList(SortedDictionary accountChanges) { - AccountChanges = accountChanges; + _accountChanges = accountChanges; } public readonly bool Equals(BlockAccessList other) => - AccountChanges.SequenceEqual(other.AccountChanges); + _accountChanges.SequenceEqual(other._accountChanges); public override readonly bool Equals(object? obj) => obj is BlockAccessList other && Equals(other); public override readonly int GetHashCode() => - AccountChanges.Count.GetHashCode(); + _accountChanges.Count.GetHashCode(); public static bool operator ==(BlockAccessList left, BlockAccessList right) => left.Equals(right); public static bool operator !=(BlockAccessList left, BlockAccessList right) => !(left == right); + + public readonly IEnumerable GetAccountChanges() => _accountChanges.Values; + public readonly AccountChanges? GetAccountChanges(Address address) => _accountChanges.TryGetValue(address, out AccountChanges value) ? value : null; + + public void IncrementBlockAccessIndex() + => _blockAccessIndex++; + + public void AddBalanceChange(Address address, UInt256? before, UInt256? after) + { + BalanceChange balanceChange = new() + { + BlockAccessIndex = _blockAccessIndex, + PostBalance = after!.Value + }; + + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + // don't add zero balance transfers, but add empty account changes + if ((before ?? 0) == after) + { + return; + } + + SortedList balanceChanges = accountChanges.BalanceChanges; + if (balanceChanges.Count != 0 && balanceChanges.Last().Key == _blockAccessIndex) + { + balanceChanges.RemoveAt(balanceChanges.Count - 1); + } + balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); + } + + public void AddCodeChange(Address address, byte[] before, byte[] after) + { + CodeChange codeChange = new() + { + BlockAccessIndex = _blockAccessIndex, + NewCode = after + }; + + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + SortedList codeChanges = accountChanges.CodeChanges; + if (codeChanges.Count != 0 && codeChanges.Last().Key == _blockAccessIndex) + { + codeChanges.RemoveAt(codeChanges.Count - 1); + } + codeChanges.Add(codeChange.BlockAccessIndex, codeChange); + } + + public void AddNonceChange(Address address, ulong newNonce) + { + if (newNonce == 0) + { + return; + } + + NonceChange nonceChange = new() + { + BlockAccessIndex = _blockAccessIndex, + NewNonce = newNonce + }; + + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + SortedList nonceChanges = accountChanges.NonceChanges; + if (nonceChanges.Count != 0 && nonceChanges.Last().Key == _blockAccessIndex) + { + nonceChanges.RemoveAt(nonceChanges.Count - 1); + } + nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); + } + + public readonly void AddAccountRead(Address address) + { + if (!_accountChanges.ContainsKey(address)) + { + _accountChanges.Add(address, new(address)); + } + } + + public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) + { + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + if (currentValue != newValue) + { + StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, newValue); + } + } + + public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] after) + { + Address address = storageCell.Address; + + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + if (before is null || !Enumerable.SequenceEqual(before, after)) + { + StorageChange(accountChanges, storageCell.Hash.BytesAsSpan, after.AsSpan()); + } + } + + public void AddStorageRead(in StorageCell storageCell) + { + StorageRead storageRead = new() + { + Key = new(storageCell.Hash.ToByteArray()) + }; + Address address = storageCell.Address; + + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + } + + accountChanges.StorageReads.Add(storageRead); + } + + private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) + { + Span newValue = stackalloc byte[32]; + newValue.Clear(); + value.CopyTo(newValue[(32 - value.Length)..]); + StorageChange storageChange = new() + { + BlockAccessIndex = _blockAccessIndex, + NewValue = new(newValue.ToArray()) + }; + + byte[] storageKey = [.. key]; + + if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) + { + storageChanges = new(storageKey); + } + else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) + { + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + } + storageChanges.Changes.Add(storageChange); + accountChanges.StorageChanges[storageKey] = storageChanges; + } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 1fd7ff401cc..3c538439cf7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -29,10 +29,7 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB } SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => a.Address, a => a)); - BlockAccessList blockAccessList = new() - { - AccountChanges = accountChangesMap - }; + BlockAccessList blockAccessList = new(accountChangesMap); if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) { @@ -55,14 +52,14 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - stream.EncodeArray([.. item.AccountChanges.Values]); + stream.EncodeArray([.. item.GetAccountChanges()]); } private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) { int len = 0; - foreach (AccountChanges accountChange in item.AccountChanges.Values) + foreach (AccountChanges accountChange in item.GetAccountChanges()) { len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); } From c2bf7a25edd10ec95a08a800c3e45a1400c7b13c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 8 Sep 2025 11:38:55 +0100 Subject: [PATCH 070/215] add traced and wrapped worldstate --- .../BlockAccessListTests.cs | 4 +- .../TracedAccessWorldState.cs | 142 ++++++++++++++++++ .../Nethermind.State/WrappedWorldState.cs | 136 +++++++++++++++++ 3 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.State/TracedAccessWorldState.cs create mode 100644 src/Nethermind/Nethermind.State/WrappedWorldState.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index b9e48e479f0..8719894f3f8 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -251,7 +251,7 @@ public async Task Can_construct_BAL() Address = TestItem.AddressA, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2)} }, + BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2) } }, NonceChanges = new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) } }, CodeChanges = [] })); @@ -271,7 +271,7 @@ public async Task Can_construct_BAL() Address = TestItem.AddressC, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsed))} }, + BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsed)) } }, NonceChanges = [], CodeChanges = [] })); diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs new file mode 100644 index 00000000000..31a4a8bcb41 --- /dev/null +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Core.Eip2930; +using Nethermind.Core.Specs; +using Nethermind.Evm.State; +using Nethermind.Evm.Tracing.State; +using Nethermind.Int256; + +namespace Nethermind.State; + +public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldState(innerWorldState) +{ + private BlockAccessList _bal = new(); + // public bool IsInScope => innerWorldState.IsInScope; + + // public Hash256 StateRoot => innerWorldState.StateRoot; + + // public bool AccountExists(Address address) => innerWorldState.AccountExists(address); + + public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + UInt256 before = _innerWorldState.GetBalance(address); + UInt256 after = before + balanceChange; + _innerWorldState.AddToBalance(address, balanceChange, spec); + _bal.AddBalanceChange(address, before, after); + } + + // public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + // => innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + + // public IDisposable BeginScope(BlockHeader? baseBlock) + // => innerWorldState.BeginScope(baseBlock); + + // public void ClearStorage(Address address) + // => innerWorldState.ClearStorage(address); + + // public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) + // => innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); + + // public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) + // => innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); + + // public void CommitTree(long blockNumber) + // => innerWorldState.CommitTree(blockNumber); + + // public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) + // => + // innerWorldState.CreateAccount(address, balance, nonce); + + // public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => + // innerWorldState.CreateAccountIfNotExists(address, balance, nonce); + + // public void DecrementNonce(Address address, UInt256 delta) => + // innerWorldState.DecrementNonce(address, delta); + + // public void DeleteAccount(Address address) => + // innerWorldState.DeleteAccount(address); + + // public ReadOnlySpan Get(in StorageCell storageCell) => + // innerWorldState.Get(storageCell); + + // public ArrayPoolList? GetAccountChanges() => + // innerWorldState.GetAccountChanges(); + + // public ref readonly UInt256 GetBalance(Address address) => + // ref innerWorldState.GetBalance(address); + + // public byte[]? GetCode(Address address) => + // innerWorldState.GetCode(address); + + // public byte[]? GetCode(in ValueHash256 codeHash) => + // innerWorldState.GetCode(codeHash); + + // public ref readonly ValueHash256 GetCodeHash(Address address) => + // ref innerWorldState.GetCodeHash(address); + + // public byte[] GetOriginal(in StorageCell storageCell) => + // innerWorldState.GetOriginal(storageCell); + + // public ReadOnlySpan GetTransientState(in StorageCell storageCell) => + // innerWorldState.GetTransientState(storageCell); + + // public bool HasStateForBlock(BlockHeader? baseBlock) => + // innerWorldState.HasStateForBlock(baseBlock); + + // public void IncrementNonce(Address address, UInt256 delta) => + // innerWorldState.IncrementNonce(address, delta); + + // public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => + // innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); + + // public bool IsContract(Address address) => + // innerWorldState.IsContract(address); + + // public bool IsDeadAccount(Address address) => + // innerWorldState.IsDeadAccount(address); + + // public void RecalculateStateRoot() => + // innerWorldState.RecalculateStateRoot(); + + // public void Reset(bool resetBlockChanges = true) => + // innerWorldState.Reset(resetBlockChanges); + + // public void ResetTransient() => + // innerWorldState.ResetTransient(); + + // public void Restore(Snapshot snapshot) => + // innerWorldState.Restore(snapshot); + + // public void Set(in StorageCell storageCell, byte[] newValue) => + // innerWorldState.Set(storageCell, newValue); + + // public void SetNonce(Address address, in UInt256 nonce) => + // innerWorldState.SetNonce(address, nonce); + + // public void SetTransientState(in StorageCell storageCell, byte[] newValue) => + // innerWorldState.SetTransientState(storageCell, newValue); + + // public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => + // innerWorldState.SubtractFromBalance(address, balanceChange, spec); + + // public Snapshot TakeSnapshot(bool newTransactionStart = false) => + // innerWorldState.TakeSnapshot(newTransactionStart); + + // public bool TryGetAccount(Address address, out AccountStruct account) => + // innerWorldState.TryGetAccount(address, out account); + + // public void UpdateStorageRoot(Address address, Hash256 storageRoot) => + // innerWorldState.UpdateStorageRoot(address, storageRoot); + + // public void WarmUp(AccessList? accessList) => + // innerWorldState.WarmUp(accessList); + + // public void WarmUp(Address address) => + // innerWorldState.WarmUp(address); +} diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs new file mode 100644 index 00000000000..f5d6f7ef705 --- /dev/null +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -0,0 +1,136 @@ +// 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.Eip2930; +using Nethermind.Core.Specs; +using Nethermind.Evm.State; +using Nethermind.Evm.Tracing.State; +using Nethermind.Int256; + +namespace Nethermind.State; + +public class WrappedWorldState(IWorldState innerWorldState) : IWorldState +{ + protected IWorldState _innerWorldState = innerWorldState; + public bool IsInScope => _innerWorldState.IsInScope; + + public Hash256 StateRoot => _innerWorldState.StateRoot; + + public bool AccountExists(Address address) => _innerWorldState.AccountExists(address); + + public virtual void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => _innerWorldState.AddToBalance(address, balanceChange, spec); + + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + + public IDisposable BeginScope(BlockHeader? baseBlock) + => _innerWorldState.BeginScope(baseBlock); + + public void ClearStorage(Address address) + => _innerWorldState.ClearStorage(address); + + public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) + => _innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); + + public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) + => _innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); + + public void CommitTree(long blockNumber) + => _innerWorldState.CommitTree(blockNumber); + + public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) + => + _innerWorldState.CreateAccount(address, balance, nonce); + + public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => + _innerWorldState.CreateAccountIfNotExists(address, balance, nonce); + + public void DecrementNonce(Address address, UInt256 delta) => + _innerWorldState.DecrementNonce(address, delta); + + public void DeleteAccount(Address address) => + _innerWorldState.DeleteAccount(address); + + public ReadOnlySpan Get(in StorageCell storageCell) => + _innerWorldState.Get(storageCell); + + public ArrayPoolList? GetAccountChanges() => + _innerWorldState.GetAccountChanges(); + + public ref readonly UInt256 GetBalance(Address address) => + ref _innerWorldState.GetBalance(address); + + public byte[]? GetCode(Address address) => + _innerWorldState.GetCode(address); + + public byte[]? GetCode(in ValueHash256 codeHash) => + _innerWorldState.GetCode(codeHash); + + public ref readonly ValueHash256 GetCodeHash(Address address) => + ref _innerWorldState.GetCodeHash(address); + + public byte[] GetOriginal(in StorageCell storageCell) => + _innerWorldState.GetOriginal(storageCell); + + public ReadOnlySpan GetTransientState(in StorageCell storageCell) => + _innerWorldState.GetTransientState(storageCell); + + public bool HasStateForBlock(BlockHeader? baseBlock) => + _innerWorldState.HasStateForBlock(baseBlock); + + public void IncrementNonce(Address address, UInt256 delta) => + _innerWorldState.IncrementNonce(address, delta); + + public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => + _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); + + public bool IsContract(Address address) => + _innerWorldState.IsContract(address); + + public bool IsDeadAccount(Address address) => + _innerWorldState.IsDeadAccount(address); + + public void RecalculateStateRoot() => + _innerWorldState.RecalculateStateRoot(); + + public void Reset(bool resetBlockChanges = true) => + _innerWorldState.Reset(resetBlockChanges); + + public void ResetTransient() => + _innerWorldState.ResetTransient(); + + public void Restore(Snapshot snapshot) => + _innerWorldState.Restore(snapshot); + + public void Set(in StorageCell storageCell, byte[] newValue) => + _innerWorldState.Set(storageCell, newValue); + + public void SetNonce(Address address, in UInt256 nonce) => + _innerWorldState.SetNonce(address, nonce); + + public void SetTransientState(in StorageCell storageCell, byte[] newValue) => + _innerWorldState.SetTransientState(storageCell, newValue); + + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => + _innerWorldState.SubtractFromBalance(address, balanceChange, spec); + + public Snapshot TakeSnapshot(bool newTransactionStart = false) => + _innerWorldState.TakeSnapshot(newTransactionStart); + + public bool TryGetAccount(Address address, out AccountStruct account) => + _innerWorldState.TryGetAccount(address, out account); + + public void UpdateStorageRoot(Address address, Hash256 storageRoot) => + _innerWorldState.UpdateStorageRoot(address, storageRoot); + + public void WarmUp(AccessList? accessList) => + _innerWorldState.WarmUp(accessList); + + public void WarmUp(Address address) => + _innerWorldState.WarmUp(address); +} From 46c55bcdcd2a45ed6903269bddad45ab446656da Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:25:48 +0100 Subject: [PATCH 071/215] track state changes --- .../BlockAccessListTests.cs | 2 +- .../Tracing/BlockAccessTracer.cs | 2 +- .../Processing/BlockProcessor.cs | 105 ++++++++------ .../BlockAccessLists/BlockAccessList.cs | 11 +- .../PruningTrieStateFactory.cs | 9 +- .../TracedAccessWorldState.cs | 75 ++++++---- .../WorldStateMetricsDecorator.cs | 1 + .../Nethermind.State/WrappedWorldState.cs | 134 +++++++++--------- 8 files changed, 196 insertions(+), 143 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 8719894f3f8..d6de54f2575 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -216,7 +216,7 @@ public async Task Can_construct_BAL() (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); - Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); + // Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs index 90e9c9ef2a9..a898628f67f 100644 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs @@ -59,7 +59,7 @@ public void SetOperationMemorySize(ulong newSize) { } public void ReportMemoryChange(long offset, in ReadOnlySpan data) { } public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) - => _bal.AddStorageChange(address, storageIndex, newValue, currentValue); + => _bal.AddStorageChange(address, storageIndex, currentValue, newValue); public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) { } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index da5bca46342..12e2bdb845f 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -30,23 +30,23 @@ namespace Nethermind.Consensus.Processing; -public partial class BlockProcessor( - ISpecProvider specProvider, - IBlockValidator blockValidator, - IRewardCalculator rewardCalculator, - IBlockTransactionsExecutor blockTransactionsExecutor, - IWorldState stateProvider, - IReceiptStorage receiptStorage, - IBeaconBlockRootHandler beaconBlockRootHandler, - IBlockhashStore blockHashStore, - ILogManager logManager, - IWithdrawalProcessor withdrawalProcessor, - IExecutionRequestsProcessor executionRequestsProcessor) +public partial class BlockProcessor : IBlockProcessor { - private readonly ILogger _logger = logManager.GetClassLogger(); - protected readonly WorldStateMetricsDecorator _stateProvider = new WorldStateMetricsDecorator(stateProvider); + private readonly ILogger _logger; + private readonly TracedAccessWorldState? _tracedAccessWorldState; + protected readonly WorldStateMetricsDecorator _stateProvider; private readonly IReceiptsRootCalculator _receiptsRootCalculator = ReceiptsRootCalculator.Instance; + private readonly ISpecProvider _specProvider; + private readonly IBlockValidator _blockValidator; + private readonly IRewardCalculator _rewardCalculator; + private readonly IBlockTransactionsExecutor _blockTransactionsExecutor; + private readonly IReceiptStorage _receiptStorage; + private readonly IBeaconBlockRootHandler _beaconBlockRootHandler; + private readonly IBlockhashStore _blockHashStore; + private readonly ILogManager _logManager; + private readonly IWithdrawalProcessor _withdrawalProcessor; + private readonly IExecutionRequestsProcessor _executionRequestsProcessor; /// /// We use a single receipt tracer for all blocks. Internally receipt tracer forwards most of the calls @@ -55,10 +55,39 @@ public partial class BlockProcessor( protected BlockReceiptsTracer ReceiptsTracer { get; set; } = new(); protected BlockAccessTracer BlockAccessTracer { get; set; } = new(); + public BlockProcessor( + ISpecProvider specProvider, + IBlockValidator blockValidator, + IRewardCalculator rewardCalculator, + IBlockTransactionsExecutor blockTransactionsExecutor, + IWorldState stateProvider, + IReceiptStorage receiptStorage, + IBeaconBlockRootHandler beaconBlockRootHandler, + IBlockhashStore blockHashStore, + ILogManager logManager, + IWithdrawalProcessor withdrawalProcessor, + IExecutionRequestsProcessor executionRequestsProcessor) + { + _specProvider = specProvider; + _blockValidator = blockValidator; + _rewardCalculator = rewardCalculator; + _blockTransactionsExecutor = blockTransactionsExecutor; + _receiptStorage = receiptStorage; + _beaconBlockRootHandler = beaconBlockRootHandler; + _blockHashStore = blockHashStore; + _logManager = logManager; + _withdrawalProcessor = withdrawalProcessor; + _executionRequestsProcessor = executionRequestsProcessor; + + _logger = _logManager.GetClassLogger(); + _tracedAccessWorldState = stateProvider as TracedAccessWorldState; + _stateProvider = new(stateProvider); + } + public event EventHandler TransactionProcessed { - add { blockTransactionsExecutor.TransactionProcessed += value; } - remove { blockTransactionsExecutor.TransactionProcessed -= value; } + add { _blockTransactionsExecutor.TransactionProcessed += value; } + remove { _blockTransactionsExecutor.TransactionProcessed -= value; } } public (Block Block, TxReceipt[] Receipts) ProcessOne(Block suggestedBlock, ProcessingOptions options, IBlockTracer blockTracer, IReleaseSpec spec, CancellationToken token) @@ -79,7 +108,7 @@ public event EventHandler TransactionProcessed private void ValidateProcessedBlock(Block suggestedBlock, ProcessingOptions options, Block block, TxReceipt[] receipts) { - if (!options.ContainsFlag(ProcessingOptions.NoValidation) && !blockValidator.ValidateProcessedBlock(block, receipts, suggestedBlock, out string? error)) + if (!options.ContainsFlag(ProcessingOptions.NoValidation) && !_blockValidator.ValidateProcessedBlock(block, receipts, suggestedBlock, out string? error)) { if (_logger.IsWarn) _logger.Warn(InvalidBlockHelper.GetMessage(suggestedBlock, "invalid block after processing")); throw new InvalidBlockException(suggestedBlock, error); @@ -91,7 +120,7 @@ private void ValidateProcessedBlock(Block suggestedBlock, ProcessingOptions opti } private bool ShouldComputeStateRoot(BlockHeader header) => - !header.IsGenesis || !specProvider.GenesisStateUnavailable; + !header.IsGenesis || !_specProvider.GenesisStateUnavailable; protected virtual TxReceipt[] ProcessBlock( Block block, @@ -103,29 +132,24 @@ protected virtual TxReceipt[] ProcessBlock( BlockBody body = block.Body; BlockHeader header = block.Header; - if (spec.BlockLevelAccessListsEnabled) - { - CompositeBlockTracer compositeBlockTracer = new(); - compositeBlockTracer.Add(blockTracer); - compositeBlockTracer.Add(BlockAccessTracer); - ReceiptsTracer.SetOtherTracer(compositeBlockTracer); - } - else - { - ReceiptsTracer.SetOtherTracer(blockTracer); - } + CompositeBlockTracer compositeBlockTracer = new(); + compositeBlockTracer.Add(blockTracer); + compositeBlockTracer.Add(BlockAccessTracer); + ReceiptsTracer.SetOtherTracer(compositeBlockTracer); + // ReceiptsTracer.SetOtherTracer(blockTracer); + ReceiptsTracer.StartNewBlockTrace(block); - blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); + _blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); StoreBeaconRoot(block, spec, BlockAccessTracer); - blockHashStore.ApplyBlockhashStateChanges(header, spec, BlockAccessTracer); + _blockHashStore.ApplyBlockhashStateChanges(header, spec, BlockAccessTracer); _stateProvider.Commit(spec, commitRoots: false); // set access index to 1 since system contracts completed BlockAccessTracer.EndTxTrace(); - TxReceipt[] receipts = blockTransactionsExecutor.ProcessTransactions(block, options, ReceiptsTracer, token); + TxReceipt[] receipts = _blockTransactionsExecutor.ProcessTransactions(block, options, ReceiptsTracer, token); _stateProvider.Commit(spec, commitRoots: false); @@ -138,14 +162,14 @@ protected virtual TxReceipt[] ProcessBlock( header.ReceiptsRoot = _receiptsRootCalculator.GetReceiptsRoot(receipts, spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); - withdrawalProcessor.ProcessWithdrawals(block, spec, BlockAccessTracer); + _withdrawalProcessor.ProcessWithdrawals(block, spec, BlockAccessTracer); // We need to do a commit here as in _executionRequestsProcessor while executing system transactions // we do WorldState.Commit(SystemTransactionReleaseSpec.Instance). In SystemTransactionReleaseSpec // Eip158Enabled=false, so we end up persisting empty accounts created while processing withdrawals. _stateProvider.Commit(spec, commitRoots: false); - executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec, BlockAccessTracer); + _executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec, BlockAccessTracer); ReceiptsTracer.EndBlockTrace(); @@ -167,9 +191,8 @@ protected virtual TxReceipt[] ProcessBlock( if (spec.BlockLevelAccessListsEnabled) { - //tmp - // block.DecodedBlockAccessList = BlockAccessTracer.BlockAccessList; - body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; + // body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; + body.BlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; } return receipts; @@ -194,7 +217,7 @@ private void StoreBeaconRoot(Block block, IReleaseSpec spec, ITxTracer tracer) { try { - beaconBlockRootHandler.StoreBeaconRoot(block, spec, tracer); + _beaconBlockRootHandler.StoreBeaconRoot(block, spec, tracer); } catch (Exception e) { @@ -205,7 +228,7 @@ private void StoreBeaconRoot(Block block, IReleaseSpec spec, ITxTracer tracer) private void StoreTxReceipts(Block block, TxReceipt[] txReceipts, IReleaseSpec spec) { // Setting canonical is done when the BlockAddedToMain event is fired - receiptStorage.Insert(block, txReceipts, spec, false); + _receiptStorage.Insert(block, txReceipts, spec, false); } private Block PrepareBlockForProcessing(Block suggestedBlock) @@ -252,7 +275,7 @@ private Block PrepareBlockForProcessing(Block suggestedBlock) private void ApplyMinerRewards(Block block, IBlockTracer tracer, IReleaseSpec spec) { if (_logger.IsTrace) _logger.Trace("Applying miner rewards:"); - BlockReward[] rewards = rewardCalculator.CalculateRewards(block); + BlockReward[] rewards = _rewardCalculator.CalculateRewards(block); for (int i = 0; i < rewards.Length; i++) { BlockReward reward = rewards[i]; @@ -285,7 +308,7 @@ private void ApplyMinerReward(Block block, BlockReward reward, IReleaseSpec spec private void ApplyDaoTransition(Block block) { - long? daoBlockNumber = specProvider.DaoBlockNumber; + long? daoBlockNumber = _specProvider.DaoBlockNumber; if (daoBlockNumber.HasValue && daoBlockNumber.Value == block.Header.Number) { ApplyTransition(); diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index fb456f6c245..699398a0255 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -46,6 +46,11 @@ public void IncrementBlockAccessIndex() public void AddBalanceChange(Address address, UInt256? before, UInt256? after) { + if (address == Address.SystemUser) + { + return; + } + BalanceChange balanceChange = new() { BlockAccessIndex = _blockAccessIndex, @@ -129,7 +134,7 @@ public readonly void AddAccountRead(Address address) } } - public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) + public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan before, ReadOnlySpan after) { if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) { @@ -137,9 +142,9 @@ public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan _accountChanges.Add(address, accountChanges); } - if (currentValue != newValue) + if (before != after) { - StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, newValue); + StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, after); } } diff --git a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs index 2b024238e0f..e93bafe7bb8 100644 --- a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs +++ b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs @@ -11,7 +11,6 @@ using Nethermind.Blockchain.Utils; using Nethermind.Config; using Nethermind.Core; -using Nethermind.Core.Crypto; using Nethermind.Core.Exceptions; using Nethermind.Core.Extensions; using Nethermind.Core.Timers; @@ -80,8 +79,8 @@ ILogManager logManager // Main thread should only read from prewarm caches, not spend extra time updating them. populatePreBlockCache: false); - IWorldStateManager stateManager = new WorldStateManager( - worldState, + WorldStateManager stateManager = new( + new TracedAccessWorldState(worldState), trieStore, dbProvider, logManager, @@ -104,10 +103,10 @@ ILogManager logManager preBlockCaches ); - var verifyTrieStarter = new VerifyTrieStarter(stateManager, processExit!, logManager); + VerifyTrieStarter verifyTrieStarter = new(stateManager, processExit!, logManager); ManualPruningTrigger pruningTrigger = new(); compositePruningTrigger.Add(pruningTrigger); - PruningTrieStateAdminRpcModule adminRpcModule = new PruningTrieStateAdminRpcModule( + PruningTrieStateAdminRpcModule adminRpcModule = new( pruningTrigger, blockTree, stateManager.GlobalStateReader, diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 31a4a8bcb41..b632216e6a2 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -4,19 +4,19 @@ using System; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Collections; using Nethermind.Core.Crypto; -using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; using Nethermind.Evm.State; -using Nethermind.Evm.Tracing.State; using Nethermind.Int256; namespace Nethermind.State; -public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldState(innerWorldState) +public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldState(innerWorldState), IPreBlockCaches { - private BlockAccessList _bal = new(); + public BlockAccessList BlockAccessList = new(); + + public PreBlockCaches Caches => (_innerWorldState as IPreBlockCaches).Caches; + // public bool IsInScope => innerWorldState.IsInScope; // public Hash256 StateRoot => innerWorldState.StateRoot; @@ -27,12 +27,15 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe { UInt256 before = _innerWorldState.GetBalance(address); UInt256 after = before + balanceChange; + BlockAccessList.AddBalanceChange(address, before, after); _innerWorldState.AddToBalance(address, balanceChange, spec); - _bal.AddBalanceChange(address, before, after); } - // public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) - // => innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + BlockAccessList.AddBalanceChange(address, UInt256.Zero, balanceChange); + return _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + } // public IDisposable BeginScope(BlockHeader? baseBlock) // => innerWorldState.BeginScope(baseBlock); @@ -62,8 +65,11 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe // public void DeleteAccount(Address address) => // innerWorldState.DeleteAccount(address); - // public ReadOnlySpan Get(in StorageCell storageCell) => - // innerWorldState.Get(storageCell); + public override ReadOnlySpan Get(in StorageCell storageCell) + { + BlockAccessList.AddStorageRead(storageCell); + return _innerWorldState.Get(storageCell); + } // public ArrayPoolList? GetAccountChanges() => // innerWorldState.GetAccountChanges(); @@ -89,11 +95,19 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe // public bool HasStateForBlock(BlockHeader? baseBlock) => // innerWorldState.HasStateForBlock(baseBlock); - // public void IncrementNonce(Address address, UInt256 delta) => - // innerWorldState.IncrementNonce(address, delta); + public override void IncrementNonce(Address address, UInt256 delta) + { + UInt256 oldNonce = _innerWorldState.GetNonce(address); + BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); + _innerWorldState.IncrementNonce(address, delta); + } - // public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => - // innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); + public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + { + byte[] oldCode = _innerWorldState.GetCode(address); + BlockAccessList.AddCodeChange(address, oldCode, code.ToArray()); + return _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); + } // public bool IsContract(Address address) => // innerWorldState.IsContract(address); @@ -113,24 +127,35 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe // public void Restore(Snapshot snapshot) => // innerWorldState.Restore(snapshot); - // public void Set(in StorageCell storageCell, byte[] newValue) => - // innerWorldState.Set(storageCell, newValue); + public override void Set(in StorageCell storageCell, byte[] newValue) + { + ReadOnlySpan oldValue = _innerWorldState.Get(storageCell); + BlockAccessList.AddStorageChange(storageCell, [.. oldValue], newValue); + _innerWorldState.Set(storageCell, newValue); + } - // public void SetNonce(Address address, in UInt256 nonce) => - // innerWorldState.SetNonce(address, nonce); + // public override void SetNonce(Address address, in UInt256 nonce) => + // _innerWorldState.SetNonce(address, nonce); - // public void SetTransientState(in StorageCell storageCell, byte[] newValue) => - // innerWorldState.SetTransientState(storageCell, newValue); + // public void SetTransientState(in StorageCell storageCell, byte[] newValue) => + // innerWorldState.SetTransientState(storageCell, newValue); - // public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => - // innerWorldState.SubtractFromBalance(address, balanceChange, spec); + public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + { + UInt256 before = _innerWorldState.GetBalance(address); + UInt256 after = before - balanceChange; + _innerWorldState.SubtractFromBalance(address, balanceChange, spec); + BlockAccessList.AddBalanceChange(address, before, after); + } // public Snapshot TakeSnapshot(bool newTransactionStart = false) => // innerWorldState.TakeSnapshot(newTransactionStart); - // public bool TryGetAccount(Address address, out AccountStruct account) => - // innerWorldState.TryGetAccount(address, out account); - + public override bool TryGetAccount(Address address, out AccountStruct account) + { + BlockAccessList.AddAccountRead(address); + return _innerWorldState.TryGetAccount(address, out account); + } // public void UpdateStorageRoot(Address address, Hash256 storageRoot) => // innerWorldState.UpdateStorageRoot(address, storageRoot); diff --git a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs index 1bf89e6f60f..9269a89af91 100644 --- a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs +++ b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs @@ -14,6 +14,7 @@ namespace Nethermind.State; +// use WrappedWorldState public class WorldStateMetricsDecorator(IWorldState innerState) : IWorldState { public void Restore(Snapshot snapshot) => innerState.Restore(snapshot); diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index f5d6f7ef705..161b7db08cc 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -20,117 +20,117 @@ public class WrappedWorldState(IWorldState innerWorldState) : IWorldState public Hash256 StateRoot => _innerWorldState.StateRoot; - public bool AccountExists(Address address) => _innerWorldState.AccountExists(address); + public bool AccountExists(Address address) + => _innerWorldState.AccountExists(address); public virtual void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => _innerWorldState.AddToBalance(address, balanceChange, spec); - public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) - => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + public virtual bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); public IDisposable BeginScope(BlockHeader? baseBlock) => _innerWorldState.BeginScope(baseBlock); public void ClearStorage(Address address) - => _innerWorldState.ClearStorage(address); + => _innerWorldState.ClearStorage(address); public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) - => _innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); + => _innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) - => _innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); + => _innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); public void CommitTree(long blockNumber) - => _innerWorldState.CommitTree(blockNumber); + => _innerWorldState.CommitTree(blockNumber); public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) - => - _innerWorldState.CreateAccount(address, balance, nonce); + => _innerWorldState.CreateAccount(address, balance, nonce); - public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => - _innerWorldState.CreateAccountIfNotExists(address, balance, nonce); + public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) + => _innerWorldState.CreateAccountIfNotExists(address, balance, nonce); - public void DecrementNonce(Address address, UInt256 delta) => - _innerWorldState.DecrementNonce(address, delta); + public void DecrementNonce(Address address, UInt256 delta) + => _innerWorldState.DecrementNonce(address, delta); - public void DeleteAccount(Address address) => - _innerWorldState.DeleteAccount(address); + public void DeleteAccount(Address address) + => _innerWorldState.DeleteAccount(address); - public ReadOnlySpan Get(in StorageCell storageCell) => - _innerWorldState.Get(storageCell); + public virtual ReadOnlySpan Get(in StorageCell storageCell) + => _innerWorldState.Get(storageCell); - public ArrayPoolList? GetAccountChanges() => - _innerWorldState.GetAccountChanges(); + public ArrayPoolList? GetAccountChanges() + => _innerWorldState.GetAccountChanges(); - public ref readonly UInt256 GetBalance(Address address) => - ref _innerWorldState.GetBalance(address); + public ref readonly UInt256 GetBalance(Address address) + => ref _innerWorldState.GetBalance(address); - public byte[]? GetCode(Address address) => - _innerWorldState.GetCode(address); + public byte[]? GetCode(Address address) + => _innerWorldState.GetCode(address); - public byte[]? GetCode(in ValueHash256 codeHash) => - _innerWorldState.GetCode(codeHash); + public byte[]? GetCode(in ValueHash256 codeHash) + => _innerWorldState.GetCode(codeHash); - public ref readonly ValueHash256 GetCodeHash(Address address) => - ref _innerWorldState.GetCodeHash(address); + public ref readonly ValueHash256 GetCodeHash(Address address) + => ref _innerWorldState.GetCodeHash(address); - public byte[] GetOriginal(in StorageCell storageCell) => - _innerWorldState.GetOriginal(storageCell); + public byte[] GetOriginal(in StorageCell storageCell) + => _innerWorldState.GetOriginal(storageCell); - public ReadOnlySpan GetTransientState(in StorageCell storageCell) => - _innerWorldState.GetTransientState(storageCell); + public ReadOnlySpan GetTransientState(in StorageCell storageCell) + => _innerWorldState.GetTransientState(storageCell); - public bool HasStateForBlock(BlockHeader? baseBlock) => - _innerWorldState.HasStateForBlock(baseBlock); + public bool HasStateForBlock(BlockHeader? baseBlock) + => _innerWorldState.HasStateForBlock(baseBlock); - public void IncrementNonce(Address address, UInt256 delta) => - _innerWorldState.IncrementNonce(address, delta); + public virtual void IncrementNonce(Address address, UInt256 delta) + => _innerWorldState.IncrementNonce(address, delta); - public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => - _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); + public virtual bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + => _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); - public bool IsContract(Address address) => - _innerWorldState.IsContract(address); + public bool IsContract(Address address) + => _innerWorldState.IsContract(address); - public bool IsDeadAccount(Address address) => - _innerWorldState.IsDeadAccount(address); + public bool IsDeadAccount(Address address) + => _innerWorldState.IsDeadAccount(address); - public void RecalculateStateRoot() => - _innerWorldState.RecalculateStateRoot(); + public void RecalculateStateRoot() + => _innerWorldState.RecalculateStateRoot(); - public void Reset(bool resetBlockChanges = true) => - _innerWorldState.Reset(resetBlockChanges); + public void Reset(bool resetBlockChanges = true) + => _innerWorldState.Reset(resetBlockChanges); - public void ResetTransient() => - _innerWorldState.ResetTransient(); + public void ResetTransient() + => _innerWorldState.ResetTransient(); - public void Restore(Snapshot snapshot) => - _innerWorldState.Restore(snapshot); + public void Restore(Snapshot snapshot) + => _innerWorldState.Restore(snapshot); - public void Set(in StorageCell storageCell, byte[] newValue) => - _innerWorldState.Set(storageCell, newValue); + public virtual void Set(in StorageCell storageCell, byte[] newValue) + => _innerWorldState.Set(storageCell, newValue); - public void SetNonce(Address address, in UInt256 nonce) => - _innerWorldState.SetNonce(address, nonce); + public void SetNonce(Address address, in UInt256 nonce) + => _innerWorldState.SetNonce(address, nonce); - public void SetTransientState(in StorageCell storageCell, byte[] newValue) => - _innerWorldState.SetTransientState(storageCell, newValue); + public void SetTransientState(in StorageCell storageCell, byte[] newValue) + => _innerWorldState.SetTransientState(storageCell, newValue); - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => - _innerWorldState.SubtractFromBalance(address, balanceChange, spec); + public virtual void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => _innerWorldState.SubtractFromBalance(address, balanceChange, spec); - public Snapshot TakeSnapshot(bool newTransactionStart = false) => - _innerWorldState.TakeSnapshot(newTransactionStart); + public Snapshot TakeSnapshot(bool newTransactionStart = false) + => _innerWorldState.TakeSnapshot(newTransactionStart); - public bool TryGetAccount(Address address, out AccountStruct account) => - _innerWorldState.TryGetAccount(address, out account); + public virtual bool TryGetAccount(Address address, out AccountStruct account) + => _innerWorldState.TryGetAccount(address, out account); - public void UpdateStorageRoot(Address address, Hash256 storageRoot) => - _innerWorldState.UpdateStorageRoot(address, storageRoot); + public void UpdateStorageRoot(Address address, Hash256 storageRoot) + => _innerWorldState.UpdateStorageRoot(address, storageRoot); - public void WarmUp(AccessList? accessList) => - _innerWorldState.WarmUp(accessList); + public void WarmUp(AccessList? accessList) + => _innerWorldState.WarmUp(accessList); - public void WarmUp(Address address) => - _innerWorldState.WarmUp(address); + public void WarmUp(Address address) + => _innerWorldState.WarmUp(address); } From 6407a68e364782a5011b25566cfda83fd8c76170 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:28:06 +0100 Subject: [PATCH 072/215] reset BAL on new scope --- src/Nethermind/Nethermind.State/TracedAccessWorldState.cs | 7 +++++-- src/Nethermind/Nethermind.State/WrappedWorldState.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index b632216e6a2..3cf7a8da775 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -37,8 +37,11 @@ public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt25 return _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); } - // public IDisposable BeginScope(BlockHeader? baseBlock) - // => innerWorldState.BeginScope(baseBlock); + public override IDisposable BeginScope(BlockHeader? baseBlock) + { + BlockAccessList = new(); + return _innerWorldState.BeginScope(baseBlock); + } // public void ClearStorage(Address address) // => innerWorldState.ClearStorage(address); diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index 161b7db08cc..eab92912e5a 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -29,7 +29,7 @@ public virtual void AddToBalance(Address address, in UInt256 balanceChange, IRel public virtual bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); - public IDisposable BeginScope(BlockHeader? baseBlock) + public virtual IDisposable BeginScope(BlockHeader? baseBlock) => _innerWorldState.BeginScope(baseBlock); public void ClearStorage(Address address) From 5ec4ff50aa182f9d779c649a4608cd09bc8cb350 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 9 Sep 2025 12:27:55 +0100 Subject: [PATCH 073/215] worldstate improvements --- .../Nethermind.Blockchain.Test/BlockAccessListTests.cs | 10 +++++++--- ...ockProcessor.BlockValidationTransactionsExecutor.cs | 4 ++++ .../Nethermind.State/TracedAccessWorldState.cs | 4 +++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index d6de54f2575..11d2460b531 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -24,6 +24,7 @@ using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; +using Nethermind.State; using NUnit.Framework; //move all to correct folder @@ -161,6 +162,8 @@ public async Task Can_construct_BAL() using IDisposable _ = worldState.BeginScope(IWorldState.PreGenesis); InitWorldState(worldState); + (worldState as TracedAccessWorldState)!.BlockAccessList = new(); + const long gasUsed = 92100; const ulong gasPrice = 2; const long gasLimit = 100000; @@ -216,7 +219,7 @@ public async Task Can_construct_BAL() (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); - // Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); + Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); @@ -309,8 +312,9 @@ public async Task Can_construct_BAL() Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) }, { eip4788Slot2, new SlotChanges(eip4788Slot2, [calldataStorageChange]) } }, - StorageReads = [ToStorageRead(eip4788Slot2)], + StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, + // StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) }, { eip4788Slot2, new SlotChanges(eip4788Slot2, [calldataStorageChange]) } }, + StorageReads = [ToStorageRead(eip4788Slot1), ToStorageRead(eip4788Slot2)], BalanceChanges = [], NonceChanges = [], CodeChanges = [] diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index 3a107f65749..03c32e0753e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -11,6 +11,7 @@ using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Evm.TransactionProcessing; +using Nethermind.State; using Metrics = Nethermind.Evm.Metrics; namespace Nethermind.Consensus.Processing @@ -35,10 +36,13 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing for (int i = 0; i < block.Transactions.Length; i++) { + (stateProvider as TracedAccessWorldState)?.BlockAccessList.IncrementBlockAccessIndex(); block.TransactionProcessed = i; Transaction currentTx = block.Transactions[i]; ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); } + (stateProvider as TracedAccessWorldState)?.BlockAccessList.IncrementBlockAccessIndex(); + return [.. receiptsTracer.TxReceipts]; } diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 3cf7a8da775..cfbc7d4ef37 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -33,7 +33,9 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) { - BlockAccessList.AddBalanceChange(address, UInt256.Zero, balanceChange); + UInt256 before = _innerWorldState.GetBalance(address); + UInt256 after = before + balanceChange; + BlockAccessList.AddBalanceChange(address, before, after); return _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); } From b45709166f98c30116db3dfc30ab1b44aba380e0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 9 Sep 2025 19:17:28 +0100 Subject: [PATCH 074/215] fix test --- .../BlockAccessListTests.cs | 12 +++++++----- .../Processing/BlockProcessor.cs | 6 +----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 11d2460b531..38c02ccb2b0 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -309,24 +309,25 @@ public async Task Can_construct_BAL() CodeChanges = [] })); + // second storage read is not a change, so not recorded Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() { Address = Eip4788Constants.BeaconRootsAddress, StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, - // StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) }, { eip4788Slot2, new SlotChanges(eip4788Slot2, [calldataStorageChange]) } }, StorageReads = [ToStorageRead(eip4788Slot1), ToStorageRead(eip4788Slot2)], BalanceChanges = [], NonceChanges = [], CodeChanges = [] })); + // storage reads make no changes Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges() { Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, + StorageChanges = [], StorageReads = [ - ToStorageRead(slot1), ToStorageRead(slot0), + ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), ], @@ -335,13 +336,14 @@ public async Task Can_construct_BAL() CodeChanges = [] })); + // storage reads make no changes Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges() { Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [zeroStorageChangeEnd]) }, { slot2, new SlotChanges(slot2, [zeroStorageChangeEnd]) }, { slot1, new SlotChanges(slot1, [zeroStorageChangeEnd]) }, { slot3, new SlotChanges(slot3, [zeroStorageChangeEnd]) } }, + StorageChanges = [], StorageReads = [ - ToStorageRead(slot1), ToStorageRead(slot0), + ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), ], diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 12e2bdb845f..338b478873a 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -132,11 +132,7 @@ protected virtual TxReceipt[] ProcessBlock( BlockBody body = block.Body; BlockHeader header = block.Header; - CompositeBlockTracer compositeBlockTracer = new(); - compositeBlockTracer.Add(blockTracer); - compositeBlockTracer.Add(BlockAccessTracer); - ReceiptsTracer.SetOtherTracer(compositeBlockTracer); - // ReceiptsTracer.SetOtherTracer(blockTracer); + ReceiptsTracer.SetOtherTracer(blockTracer); ReceiptsTracer.StartNewBlockTrace(block); From f253413b82e92b7249e569238482f96eeea97004 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 9 Sep 2025 19:22:08 +0100 Subject: [PATCH 075/215] fix encoding test --- .../BlockAccessListTests.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 38c02ccb2b0..a072cbd17ec 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -76,6 +76,7 @@ public void Can_encode_and_decode() Assert.That(slotChanges, Is.EqualTo(slotChangesDecoded)); StorageRead storageRead = new(new Bytes32([.. Enumerable.Repeat(50, 32)])); + StorageRead storageRead2 = new(new Bytes32([.. Enumerable.Repeat(60, 32)])); byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); Assert.That(storageRead, Is.EqualTo(storageReadDecoded)); @@ -85,6 +86,11 @@ public void Can_encode_and_decode() BlockAccessIndex = 10, PostBalance = 0 }; + BalanceChange balanceChange2 = new() + { + BlockAccessIndex = 11, + PostBalance = 1 + }; byte[] balanceChangeBytes = Rlp.Encode(balanceChange, RlpBehaviors.None).Bytes; BalanceChange balanceChangeDecoded = Rlp.Decode(balanceChangeBytes, RlpBehaviors.None); Assert.That(balanceChange, Is.EqualTo(balanceChangeDecoded)); @@ -94,6 +100,11 @@ public void Can_encode_and_decode() BlockAccessIndex = 10, NewNonce = 0 }; + NonceChange nonceChange2 = new() + { + BlockAccessIndex = 11, + NewNonce = 0 + }; byte[] nonceChangeBytes = Rlp.Encode(nonceChange, RlpBehaviors.None).Bytes; NonceChange nonceChangeDecoded = Rlp.Decode(nonceChangeBytes, RlpBehaviors.None); Assert.That(nonceChange, Is.EqualTo(nonceChangeDecoded)); @@ -115,13 +126,13 @@ public void Can_encode_and_decode() SortedList balanceChangesList = new() { { balanceChange.BlockAccessIndex, balanceChange }, - { balanceChange.BlockAccessIndex, balanceChange } + { balanceChange2.BlockAccessIndex, balanceChange2 } }; SortedList nonceChangesList = new() { { nonceChange.BlockAccessIndex, nonceChange }, - { nonceChange.BlockAccessIndex, nonceChange } + { nonceChange2.BlockAccessIndex, nonceChange2 } }; SortedList codeChangesList = new() @@ -133,7 +144,7 @@ public void Can_encode_and_decode() { Address = TestItem.AddressA, StorageChanges = storageChangesDict, - StorageReads = [storageRead, storageRead], + StorageReads = [storageRead, storageRead2], BalanceChanges = balanceChangesList, NonceChanges = nonceChangesList, CodeChanges = codeChangesList From 371478be651e50af563ff8b573a1582a4953a29d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 9 Sep 2025 20:56:39 +0100 Subject: [PATCH 076/215] optimise incrementnonce --- src/Nethermind/Nethermind.Evm/State/IWorldState.cs | 1 + src/Nethermind/Nethermind.State/StateProvider.cs | 6 +++++- .../Nethermind.State/TracedAccessWorldState.cs | 9 +++++++-- src/Nethermind/Nethermind.State/WorldState.cs | 4 +++- .../Nethermind.State/WorldStateMetricsDecorator.cs | 2 ++ src/Nethermind/Nethermind.State/WrappedWorldState.cs | 3 +++ 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs index 1a2633aba7b..9839f6ebb14 100644 --- a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs @@ -120,6 +120,7 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider void UpdateStorageRoot(Address address, Hash256 storageRoot); void IncrementNonce(Address address, UInt256 delta); + void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce); void DecrementNonce(Address address, UInt256 delta); diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 549792456cb..753325dc0b3 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -309,10 +309,14 @@ static Account ThrowNullAccount(Address address) } public void IncrementNonce(Address address, UInt256 delta) + => IncrementNonce(address, delta, out _); + + public void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) { _needsStateRootUpdate = true; Account account = GetThroughCache(address) ?? ThrowNullAccount(address); - Account changedAccount = account.WithChangedNonce(account.Nonce + delta); + oldNonce = account.Nonce; + Account changedAccount = account.WithChangedNonce(oldNonce + delta); if (_logger.IsTrace) Trace(address, account, changedAccount); PushUpdate(address, changedAccount); diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index cfbc7d4ef37..3d66eede72a 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -102,9 +102,14 @@ public override ReadOnlySpan Get(in StorageCell storageCell) public override void IncrementNonce(Address address, UInt256 delta) { - UInt256 oldNonce = _innerWorldState.GetNonce(address); + _innerWorldState.IncrementNonce(address, delta, out UInt256 oldNonce); + BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); + } + + public override void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) + { + _innerWorldState.IncrementNonce(address, delta, out oldNonce); BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); - _innerWorldState.IncrementNonce(address, delta); } public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index f6d107c5307..6b2ed28e7ac 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -211,9 +211,11 @@ public void UpdateStorageRoot(Address address, Hash256 storageRoot) _stateProvider.UpdateStorageRoot(address, storageRoot); } public void IncrementNonce(Address address, UInt256 delta) + => IncrementNonce(address, delta, out _); + public void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) { DebugGuardInScope(); - _stateProvider.IncrementNonce(address, delta); + _stateProvider.IncrementNonce(address, delta, out oldNonce); } public void DecrementNonce(Address address, UInt256 delta) { diff --git a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs index 9269a89af91..cab44f6ebf8 100644 --- a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs +++ b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs @@ -80,6 +80,8 @@ public void UpdateStorageRoot(Address address, Hash256 storageRoot) public void IncrementNonce(Address address, UInt256 delta) => innerState.IncrementNonce(address, delta); + public void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) => innerState.IncrementNonce(address, delta, out oldNonce); + public void DecrementNonce(Address address, UInt256 delta) => innerState.DecrementNonce(address, delta); public void SetNonce(Address address, in UInt256 nonce) => innerState.SetNonce(address, nonce); diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index eab92912e5a..bc8f93eba88 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -86,6 +86,9 @@ public bool HasStateForBlock(BlockHeader? baseBlock) public virtual void IncrementNonce(Address address, UInt256 delta) => _innerWorldState.IncrementNonce(address, delta); + public virtual void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) + => _innerWorldState.IncrementNonce(address, delta, out oldNonce); + public virtual bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); From f917e8c8108d625af65e4a79ec533cfd393b06b5 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 9 Sep 2025 21:26:06 +0100 Subject: [PATCH 077/215] refactor and remove tracer code --- .../BeaconBlockRootHandlerTests.cs | 2 - .../BlockAccessListTests.cs | 13 -- .../IBeaconBlockRootHandler.cs | 1 - .../Tracing/BlockAccessTracer.cs | 213 ------------------ .../ExecutionRequestProcessor.cs | 6 +- .../IExecutionRequestProcessor.cs | 3 +- .../Processing/BlockProcessor.cs | 17 +- .../BlockProductionWithdrawalProcessor.cs | 5 +- .../Withdrawals/IWithdrawalProcessor.cs | 3 +- .../Withdrawals/NullWithdrawalProcessor.cs | 3 +- .../Withdrawals/WithdrawalProcessor.cs | 10 +- .../Withdrawals/AuraWithdrawalProcessor.cs | 2 +- .../ExecutionRequestsProcessorMock.cs | 1 - .../OptimismWithdrawals.cs | 2 +- .../WorldStateMetricsDecorator.cs | 109 +-------- .../Nethermind.State/WrappedWorldState.cs | 10 +- 16 files changed, 33 insertions(+), 367 deletions(-) delete mode 100644 src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BeaconBlockRootHandlerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BeaconBlockRootHandlerTests.cs index a82668f7d74..7288c0d85ac 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BeaconBlockRootHandlerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BeaconBlockRootHandlerTests.cs @@ -5,10 +5,8 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Eip2930; -using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; -using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index a072cbd17ec..b5adef2a911 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -41,19 +41,6 @@ public class BlockAccessListTests() private static readonly ISpecProvider _specProvider = new TestSpecProvider(_spec); private static readonly UInt256 _accountBalance = 10.Ether(); - [Test] - public void Empty_account_changes() - { - Block block = Build.A.Block.WithTransactions(Build.A.Transaction.TestObject).TestObject; - - BlockAccessTracer tracer = new(); - tracer.StartNewBlockTrace(block); - tracer.StartNewTxTrace(block.Transactions[0]); - tracer.MarkAsSuccess(TestItem.AddressA, 100, [], [], TestItem.KeccakF); - - Assert.That(tracer.BlockAccessList.GetAccountChanges().Count, Is.Zero); - } - [Test] public void Can_encode_and_decode() { diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs index 3b1a204a9b3..633bafd7c61 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs @@ -4,7 +4,6 @@ using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; -using Nethermind.Evm; using Nethermind.Evm.Tracing; namespace Nethermind.Blockchain.BeaconBlockRoot; diff --git a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs b/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs deleted file mode 100644 index a898628f67f..00000000000 --- a/src/Nethermind/Nethermind.Blockchain/Tracing/BlockAccessTracer.cs +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using System; -using System.Collections.Generic; -using Nethermind.Core; -using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Crypto; -using Nethermind.Evm; -using Nethermind.Evm.Tracing; -using Nethermind.Evm.TransactionProcessing; -using Nethermind.Int256; - -namespace Nethermind.Blockchain.Tracing; - -public class BlockAccessTracer : IBlockTracer, ITxTracer, IJournal -{ - // private IBlockTracer _otherTracer = NullBlockTracer.Instance; - protected Block Block = null!; - public bool IsTracingReceipt => false; - public bool IsTracingActions => false; - // public bool IsTracingOpLevelStorage => false; - public bool IsTracingOpLevelStorage => true; - public bool IsTracingMemory => false; - public bool IsTracingInstructions => false; - public bool IsTracingRefunds => false; - public bool IsTracingCode => false; - public bool IsTracingStack => false; - public bool IsTracingState => true; - public bool IsTracingStorage => true; - - public bool IsTracingBlockHash => false; - public bool IsTracingAccess => true; - public bool IsTracingFees => false; - public bool IsTracingLogs => false; - - public void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output, LogEntry[] logs, Hash256? stateRoot = null) - { - // _txReceipts.Add(BuildReceipt(recipient, gasSpent.SpentGas, StatusCode.Success, logs, stateRoot)); - } - - public void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, string? error, Hash256? stateRoot = null) - { - // _txReceipts.Add(BuildFailedReceipt(recipient, gasSpent.SpentGas, error, stateRoot)); - - } - - public void StartOperation(int pc, Instruction opcode, long gas, in ExecutionEnvironment env, int codeSection = 0, int functionDepth = 0) { } - - public void ReportOperationError(EvmExceptionType error) { } - - - public void ReportOperationRemainingGas(long gas) { } - - public void ReportLog(LogEntry log) { } - - public void SetOperationMemorySize(ulong newSize) { } - - public void ReportMemoryChange(long offset, in ReadOnlySpan data) { } - - public void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan newValue, ReadOnlySpan currentValue) - => _bal.AddStorageChange(address, storageIndex, currentValue, newValue); - - public void LoadOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan value) { } - - public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) { } - - public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) - => _bal.AddBalanceChange(address, before, after); - - public void ReportCodeChange(Address address, byte[] before, byte[] after) - => _bal.AddCodeChange(address, before, after); - - public void ReportNonceChange(Address address, UInt256? before, UInt256? after) - => _bal.AddNonceChange(address, (ulong)after); - - public void ReportAccountRead(Address address) - => _bal.AddAccountRead(address); - - public void ReportStorageChange(in ReadOnlySpan key, in ReadOnlySpan value) - { - // no address - } - - public void ReportStorageChange(in StorageCell storageCell, byte[] before, byte[] after) - => _bal.AddStorageChange(storageCell, before, after); - - public void ReportStorageRead(in StorageCell storageCell) - => _bal.AddStorageRead(storageCell); - - public void ReportAction(long gas, UInt256 value, Address from, Address to, ReadOnlyMemory input, ExecutionType callType, bool isPrecompileCall = false) { } - - public void ReportActionEnd(long gas, ReadOnlyMemory output) { } - - public void ReportActionError(EvmExceptionType exceptionType) { } - - public void ReportActionRevert(long gasLeft, ReadOnlyMemory output) { } - - public void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory deployedCode) { } - - public void ReportByteCode(ReadOnlyMemory byteCode) { } - - public void ReportGasUpdateForVmTrace(long refund, long gasAvailable) { } - - public void ReportRefund(long refund) { } - - public void ReportExtraGasPressure(long extraGasPressure) { } - - public void ReportAccess(IReadOnlyCollection
accessedAddresses, IReadOnlyCollection accessedStorageCells) - { - // _bal.Add(new()); - } - - public void SetOperationStack(TraceStack stack) { } - - public void ReportStackPush(in ReadOnlySpan stackItem) { } - - public void ReportBlockHash(Hash256 blockHash) { } - - public void SetOperationMemory(TraceMemory memoryTrace) { } - - public void ReportFees(UInt256 fees, UInt256 burntFees) { } - - // private ITxTracer _currentTxTracer = NullTxTracer.Instance; - private ushort _blockAccessIndex = 0; - // private readonly List _txReceipts = new(); - private BlockAccessList _bal = new(); - protected Transaction? CurrentTx; - public BlockAccessList BlockAccessList => _bal; - // public TxReceipt LastReceipt => _txReceipts[^1]; - public bool IsTracingRewards => false; - - // public ITxTracer InnerTracer => _currentTxTracer; - - public int TakeSnapshot() => 0; - - public void Restore(int snapshot) - { - // int numToRemove = _txReceipts.Count - snapshot; - - // for (int i = 0; i < numToRemove; i++) - // { - // _txReceipts.RemoveAt(_txReceipts.Count - 1); - // } - - // Block.Header.GasUsed = _txReceipts.Count > 0 ? _txReceipts.Last().GasUsedTotal : 0; - } - - public void ReportReward(Address author, string rewardType, UInt256 rewardValue) { } - - public void StartNewBlockTrace(Block block) - { - Block = block; - _bal = new(); - } - - public ITxTracer StartNewTxTrace(Transaction? tx) - => this; - - public void EndTxTrace() - => _bal.IncrementBlockAccessIndex(); - - public void EndBlockTrace() - { - // _otherTracer.EndBlockTrace(); - // if (_txReceipts.Count > 0) - // { - // Bloom blockBloom = new(); - // Block.Header.Bloom = blockBloom; - // for (int index = 0; index < _txReceipts.Count; index++) - // { - // TxReceipt? receipt = _txReceipts[index]; - // blockBloom.Accumulate(receipt.Bloom!); - // } - // } - } - - // public void SetOtherTracer(IBlockTracer blockTracer) - // { - // _otherTracer = blockTracer; - // } - - public void Dispose() - { - // _currentTxTracer.Dispose(); - } - - private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) - { - Span newValue = stackalloc byte[32]; - newValue.Clear(); - value.CopyTo(newValue[(32 - value.Length)..]); - StorageChange storageChange = new() - { - BlockAccessIndex = _blockAccessIndex, - NewValue = new(newValue.ToArray()) - }; - - byte[] storageKey = [.. key]; - - if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) - { - storageChanges = new(storageKey); - } - else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) - { - storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); - } - storageChanges.Changes.Add(storageChange); - accountChanges.StorageChanges[storageKey] = storageChanges; - } - -} diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs index 5413cd58692..3402cc750e9 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs @@ -57,7 +57,7 @@ public ExecutionRequestsProcessor(ITransactionProcessor transactionProcessor) _consolidationTransaction.Hash = _consolidationTransaction.CalculateHash(); } - public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, ITxTracer tracer) + public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) { if (!spec.RequestsEnabled || block.IsGenesis) return; @@ -69,13 +69,13 @@ public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] if (spec.WithdrawalRequestsEnabled) { ReadRequests(block, state, spec.Eip7002ContractAddress, requests, _withdrawalTransaction, ExecutionRequestType.WithdrawalRequest, - BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed, tracer); + BlockErrorMessages.WithdrawalsContractEmpty, BlockErrorMessages.WithdrawalsContractFailed); } if (spec.ConsolidationRequestsEnabled) { ReadRequests(block, state, spec.Eip7251ContractAddress, requests, _consolidationTransaction, ExecutionRequestType.ConsolidationRequest, - BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed, tracer); + BlockErrorMessages.ConsolidationsContractEmpty, BlockErrorMessages.ConsolidationsContractFailed); } block.ExecutionRequests = [.. requests]; diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs index 1a8888c8286..533ecffc794 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/IExecutionRequestProcessor.cs @@ -4,11 +4,10 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.State; -using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.ExecutionRequests; public interface IExecutionRequestsProcessor { - public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, ITxTracer? additionalTracer = null); + public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 338b478873a..f4883655f08 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -53,7 +53,6 @@ public partial class BlockProcessor /// to any block-specific tracers. ///
protected BlockReceiptsTracer ReceiptsTracer { get; set; } = new(); - protected BlockAccessTracer BlockAccessTracer { get; set; } = new(); public BlockProcessor( ISpecProvider specProvider, @@ -138,13 +137,10 @@ protected virtual TxReceipt[] ProcessBlock( _blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); - StoreBeaconRoot(block, spec, BlockAccessTracer); - _blockHashStore.ApplyBlockhashStateChanges(header, spec, BlockAccessTracer); + StoreBeaconRoot(block, spec); + _blockHashStore.ApplyBlockhashStateChanges(header, spec); _stateProvider.Commit(spec, commitRoots: false); - // set access index to 1 since system contracts completed - BlockAccessTracer.EndTxTrace(); - TxReceipt[] receipts = _blockTransactionsExecutor.ProcessTransactions(block, options, ReceiptsTracer, token); _stateProvider.Commit(spec, commitRoots: false); @@ -158,14 +154,14 @@ protected virtual TxReceipt[] ProcessBlock( header.ReceiptsRoot = _receiptsRootCalculator.GetReceiptsRoot(receipts, spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); - _withdrawalProcessor.ProcessWithdrawals(block, spec, BlockAccessTracer); + _withdrawalProcessor.ProcessWithdrawals(block, spec); // We need to do a commit here as in _executionRequestsProcessor while executing system transactions // we do WorldState.Commit(SystemTransactionReleaseSpec.Instance). In SystemTransactionReleaseSpec // Eip158Enabled=false, so we end up persisting empty accounts created while processing withdrawals. _stateProvider.Commit(spec, commitRoots: false); - _executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec, BlockAccessTracer); + _executionRequestsProcessor.ProcessExecutionRequests(block, _stateProvider, receipts, spec); ReceiptsTracer.EndBlockTrace(); @@ -187,7 +183,6 @@ protected virtual TxReceipt[] ProcessBlock( if (spec.BlockLevelAccessListsEnabled) { - // body.BlockAccessList = Rlp.Encode(BlockAccessTracer.BlockAccessList).Bytes; body.BlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; } @@ -209,11 +204,11 @@ private static void CalculateBlooms(TxReceipt[] receipts) }); } - private void StoreBeaconRoot(Block block, IReleaseSpec spec, ITxTracer tracer) + private void StoreBeaconRoot(Block block, IReleaseSpec spec) { try { - _beaconBlockRootHandler.StoreBeaconRoot(block, spec, tracer); + _beaconBlockRootHandler.StoreBeaconRoot(block, spec, NullTxTracer.Instance); } catch (Exception e) { diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs index d05d4104c11..12f2e512a3f 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/BlockProductionWithdrawalProcessor.cs @@ -5,7 +5,6 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; -using Nethermind.Evm.Tracing; using Nethermind.State.Proofs; namespace Nethermind.Consensus.Withdrawals; @@ -14,9 +13,9 @@ public class BlockProductionWithdrawalProcessor(IWithdrawalProcessor processor) { private readonly IWithdrawalProcessor _processor = processor ?? throw new ArgumentNullException(nameof(processor)); - public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null) + public void ProcessWithdrawals(Block block, IReleaseSpec spec) { - _processor.ProcessWithdrawals(block, spec, tracer); + _processor.ProcessWithdrawals(block, spec); if (spec.WithdrawalsEnabled) { diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs index 3a42e111666..5909ba1f220 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/IWithdrawalProcessor.cs @@ -3,11 +3,10 @@ using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.Withdrawals; public interface IWithdrawalProcessor { - void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null); + void ProcessWithdrawals(Block block, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs index a768b1826bf..24581691fcf 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/NullWithdrawalProcessor.cs @@ -3,13 +3,12 @@ using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Evm.Tracing; namespace Nethermind.Consensus.Withdrawals; public class NullWithdrawalProcessor : IWithdrawalProcessor { - public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) { } + public void ProcessWithdrawals(Block block, IReleaseSpec spec) { } public static IWithdrawalProcessor Instance { get; } = new NullWithdrawalProcessor(); } diff --git a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs index 1243f3e95fd..190f2ff47af 100644 --- a/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Withdrawals/WithdrawalProcessor.cs @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Diagnostics.CodeAnalysis; -using Nethermind.Blockchain.Tracing; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.State; @@ -26,7 +24,7 @@ public WithdrawalProcessor(IWorldState stateProvider, ILogManager logManager) _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); } - public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer = null) + public void ProcessWithdrawals(Block block, IReleaseSpec spec) { if (!spec.WithdrawalsEnabled) return; @@ -45,12 +43,6 @@ public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer tracer // Consensus clients are using Gwei for withdrawals amount. We need to convert it to Wei before applying state changes https://github.com/ethereum/execution-apis/pull/354 _stateProvider.AddToBalanceAndCreateIfNotExists(address, amount, spec); - - if (tracer is not null && amount != 0) - { - UInt256 newBalance = _stateProvider.GetBalance(address); - tracer.ReportBalanceChange(address, null, newBalance); - } } } diff --git a/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs b/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs index 4cdcb468e58..7e43af445ad 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/Withdrawals/AuraWithdrawalProcessor.cs @@ -30,7 +30,7 @@ public AuraWithdrawalProcessor(IWithdrawalContract contract, ILogManager logMana _logger = logManager.GetClassLogger(); } - public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) + public void ProcessWithdrawals(Block block, IReleaseSpec spec) { if (!spec.WithdrawalsEnabled || block.Withdrawals is null) // The second check seems redundant return; diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs index e1d8404c53d..8ad25a2ec4e 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs @@ -8,7 +8,6 @@ using Nethermind.Core.ExecutionRequest; using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; -using Nethermind.Evm; using Nethermind.Evm.State; namespace Nethermind.Merge.Plugin.Test; diff --git a/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs b/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs index 81aad5850dc..c465ff0b963 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismWithdrawals.cs @@ -25,7 +25,7 @@ public class OptimismWithdrawalProcessor(IWorldState state, ILogManager logManag private readonly IOptimismSpecHelper _specHelper = specHelper; private readonly ILogger _logger = logManager.GetClassLogger(); - public void ProcessWithdrawals(Block block, IReleaseSpec spec, ITxTracer? tracer = null) + public void ProcessWithdrawals(Block block, IReleaseSpec spec) { BlockHeader header = block.Header; diff --git a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs index cab44f6ebf8..638d4764abb 100644 --- a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs +++ b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs @@ -1,138 +1,51 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Diagnostics; -using Nethermind.Core; -using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; -using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; using Nethermind.Evm.State; using Nethermind.Evm.Tracing.State; -using Nethermind.Int256; namespace Nethermind.State; // use WrappedWorldState -public class WorldStateMetricsDecorator(IWorldState innerState) : IWorldState +public class WorldStateMetricsDecorator(IWorldState innerWorldState) : WrappedWorldState(innerWorldState) { - public void Restore(Snapshot snapshot) => innerState.Restore(snapshot); - - public bool TryGetAccount(Address address, out AccountStruct account) => innerState.TryGetAccount(address, out account); - - public byte[] GetOriginal(in StorageCell storageCell) => innerState.GetOriginal(in storageCell); - - public ReadOnlySpan Get(in StorageCell storageCell) => innerState.Get(in storageCell); - - public void Set(in StorageCell storageCell, byte[] newValue) => innerState.Set(in storageCell, newValue); - - public ReadOnlySpan GetTransientState(in StorageCell storageCell) => innerState.GetTransientState(in storageCell); - - public void SetTransientState(in StorageCell storageCell, byte[] newValue) => innerState.SetTransientState(in storageCell, newValue); - - public void Reset(bool resetBlockChanges = true) + public override void Reset(bool resetBlockChanges = true) { StateMerkleizationTime = 0d; - innerState.Reset(resetBlockChanges); + _innerWorldState.Reset(resetBlockChanges); } - public Snapshot TakeSnapshot(bool newTransactionStart = false) => innerState.TakeSnapshot(newTransactionStart); - - public void WarmUp(AccessList? accessList) => innerState.WarmUp(accessList); - - public void WarmUp(Address address) => innerState.WarmUp(address); - - public void ClearStorage(Address address) => innerState.ClearStorage(address); - - public void RecalculateStateRoot() + public override void RecalculateStateRoot() { long start = Stopwatch.GetTimestamp(); - innerState.RecalculateStateRoot(); + _innerWorldState.RecalculateStateRoot(); StateMerkleizationTime += Stopwatch.GetElapsedTime(start).TotalMilliseconds; } - public Hash256 StateRoot => innerState.StateRoot; - public double StateMerkleizationTime { get; private set; } - public void DeleteAccount(Address address) => innerState.DeleteAccount(address); - - public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) => - innerState.CreateAccount(address, in balance, in nonce); - - public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => - innerState.CreateAccountIfNotExists(address, in balance, in nonce); - public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => - innerState.InsertCode(address, in codeHash, code, spec, isGenesis); - - public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => - innerState.AddToBalance(address, in balanceChange, spec); - - public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) => - innerState.AddToBalanceAndCreateIfNotExists(address, in balanceChange, spec); - - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => - innerState.SubtractFromBalance(address, in balanceChange, spec); - - public void UpdateStorageRoot(Address address, Hash256 storageRoot) - => innerState.UpdateStorageRoot(address, storageRoot); - - public void IncrementNonce(Address address, UInt256 delta) => innerState.IncrementNonce(address, delta); - - public void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) => innerState.IncrementNonce(address, delta, out oldNonce); - - public void DecrementNonce(Address address, UInt256 delta) => innerState.DecrementNonce(address, delta); - - public void SetNonce(Address address, in UInt256 nonce) => innerState.SetNonce(address, nonce); - - public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) + public override void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) { long start = Stopwatch.GetTimestamp(); - innerState.Commit(releaseSpec, isGenesis, commitRoots); + _innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); if (commitRoots) StateMerkleizationTime += Stopwatch.GetElapsedTime(start).TotalMilliseconds; } - public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) + public override void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) { long start = Stopwatch.GetTimestamp(); - innerState.Commit(releaseSpec, tracer, isGenesis, commitRoots); + _innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); if (commitRoots) StateMerkleizationTime += Stopwatch.GetElapsedTime(start).TotalMilliseconds; } - public void CommitTree(long blockNumber) + public override void CommitTree(long blockNumber) { long start = Stopwatch.GetTimestamp(); - innerState.CommitTree(blockNumber); + _innerWorldState.CommitTree(blockNumber); StateMerkleizationTime += Stopwatch.GetElapsedTime(start).TotalMilliseconds; } - - public ArrayPoolList? GetAccountChanges() => innerState.GetAccountChanges(); - - public void ResetTransient() => innerState.ResetTransient(); - - public byte[]? GetCode(Address address) => innerState.GetCode(address); - - public byte[]? GetCode(in ValueHash256 codeHash) => innerState.GetCode(in codeHash); - - public bool IsContract(Address address) => innerState.IsContract(address); - - public bool AccountExists(Address address) => innerState.AccountExists(address); - - public bool IsDeadAccount(Address address) => innerState.IsDeadAccount(address); - - public bool HasStateForBlock(BlockHeader? stateRoot) => innerState.HasStateForBlock(stateRoot); - - public IDisposable BeginScope(BlockHeader? baseBlock) => innerState.BeginScope(baseBlock); - public bool IsInScope => innerState.IsInScope; - - public ref readonly UInt256 GetBalance(Address account) => ref innerState.GetBalance(account); - - UInt256 IAccountStateProvider.GetBalance(Address address) => innerState.GetBalance(address); - - public ref readonly ValueHash256 GetCodeHash(Address address) => ref innerState.GetCodeHash(address); - - ValueHash256 IAccountStateProvider.GetCodeHash(Address address) => innerState.GetCodeHash(address); } diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index bc8f93eba88..32975d05647 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -35,13 +35,13 @@ public virtual IDisposable BeginScope(BlockHeader? baseBlock) public void ClearStorage(Address address) => _innerWorldState.ClearStorage(address); - public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) + public virtual void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) => _innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); - public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) + public virtual void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) => _innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); - public void CommitTree(long blockNumber) + public virtual void CommitTree(long blockNumber) => _innerWorldState.CommitTree(blockNumber); public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) @@ -98,10 +98,10 @@ public bool IsContract(Address address) public bool IsDeadAccount(Address address) => _innerWorldState.IsDeadAccount(address); - public void RecalculateStateRoot() + public virtual void RecalculateStateRoot() => _innerWorldState.RecalculateStateRoot(); - public void Reset(bool resetBlockChanges = true) + public virtual void Reset(bool resetBlockChanges = true) => _innerWorldState.Reset(resetBlockChanges); public void ResetTransient() From cd60b07c491dd8f7d1d90306c7927c075ff2dde1 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:22:55 +0100 Subject: [PATCH 078/215] optimise adding to and subtracting from balance --- .../Nethermind.Evm/State/IWorldState.cs | 6 +++++ .../PruningTrieStateFactory.cs | 2 +- .../Nethermind.State/StateProvider.cs | 21 ++++++++++++---- .../TracedAccessWorldState.cs | 24 +++++++++---------- src/Nethermind/Nethermind.State/WorldState.cs | 18 +++++++++----- .../Nethermind.State/WrappedWorldState.cs | 9 +++++++ 6 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs index 9839f6ebb14..444b02992d1 100644 --- a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs @@ -113,10 +113,16 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); + void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance); + bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec); + bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance); + void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); + void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance); + void UpdateStorageRoot(Address address, Hash256 storageRoot); void IncrementNonce(Address address, UInt256 delta); diff --git a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs index e93bafe7bb8..73d87899011 100644 --- a/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs +++ b/src/Nethermind/Nethermind.Init/PruningTrieStateFactory.cs @@ -50,7 +50,7 @@ ILogManager logManager public (IWorldStateManager, IPruningTrieStateAdminRpcModule) Build() { - CompositePruningTrigger compositePruningTrigger = new CompositePruningTrigger(); + CompositePruningTrigger compositePruningTrigger = new(); IPruningTrieStore trieStore = mainPruningTrieStoreFactory.PruningTrieStore; ITrieStore mainWorldTrieStore = trieStore; diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 753325dc0b3..09be3d30f01 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -205,7 +205,7 @@ static Account ThrowIfNull(Address address) => throw new InvalidOperationException($"Account {address} is null when updating code hash"); } - private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSubtracting) + private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSubtracting, out UInt256 oldBalance) { _needsStateRootUpdate = true; @@ -241,6 +241,7 @@ static void ThrowNonExistingAccount() } } + oldBalance = 0; return; } @@ -251,6 +252,7 @@ static void ThrowNonExistingAccount() ThrowInsufficientBalanceException(address); } + oldBalance = account.Balance; UInt256 newBalance = isSubtracting ? account.Balance - balanceChange : account.Balance + balanceChange; Account changedAccount = account.WithChangedBalance(newBalance); @@ -271,15 +273,20 @@ static void ThrowInsufficientBalanceException(Address address) } public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec) + => SubtractFromBalance(address, balanceChange, releaseSpec, out _); + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, out UInt256 oldBalance) { _needsStateRootUpdate = true; - SetNewBalance(address, balanceChange, releaseSpec, true); + SetNewBalance(address, balanceChange, releaseSpec, true, out oldBalance); } public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec) + => AddToBalance(address, balanceChange, releaseSpec, out _); + + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, out UInt256 oldBalance) { _needsStateRootUpdate = true; - SetNewBalance(address, balanceChange, releaseSpec, false); + SetNewBalance(address, balanceChange, releaseSpec, false, out oldBalance); } /// @@ -496,20 +503,24 @@ public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UIn } } - public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balance, IReleaseSpec spec) + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balance, IReleaseSpec spec, out UInt256 oldBalance) { if (AccountExists(address)) { - AddToBalance(address, balance, spec); + AddToBalance(address, balance, spec, out oldBalance); return false; } else { + oldBalance = 0; CreateAccount(address, balance); return true; } } + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balance, IReleaseSpec spec) + => AddToBalanceAndCreateIfNotExists(address, balance, spec, out _); + public void Commit(IReleaseSpec releaseSpec, bool commitRoots, bool isGenesis) => Commit(releaseSpec, NullStateTracer.Instance, commitRoots, isGenesis); diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 3d66eede72a..f735b6f76c9 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -24,19 +24,22 @@ public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldS // public bool AccountExists(Address address) => innerWorldState.AccountExists(address); public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => AddToBalance(address, balanceChange, spec, out _); + public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { - UInt256 before = _innerWorldState.GetBalance(address); - UInt256 after = before + balanceChange; - BlockAccessList.AddBalanceChange(address, before, after); - _innerWorldState.AddToBalance(address, balanceChange, spec); + _innerWorldState.AddToBalance(address, balanceChange, spec, out oldBalance); + UInt256 newBalance = oldBalance + balanceChange; + BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); } public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out _); + public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { - UInt256 before = _innerWorldState.GetBalance(address); - UInt256 after = before + balanceChange; - BlockAccessList.AddBalanceChange(address, before, after); - return _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + bool res = _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out oldBalance); + UInt256 newBalance = oldBalance + balanceChange; + BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); + return res; } public override IDisposable BeginScope(BlockHeader? baseBlock) @@ -101,10 +104,7 @@ public override ReadOnlySpan Get(in StorageCell storageCell) // innerWorldState.HasStateForBlock(baseBlock); public override void IncrementNonce(Address address, UInt256 delta) - { - _innerWorldState.IncrementNonce(address, delta, out UInt256 oldNonce); - BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); - } + => IncrementNonce(address, delta, out _); public override void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) { diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 6b2ed28e7ac..c4ec6a1b15a 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -190,21 +190,27 @@ public bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory DebugGuardInScope(); return _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis); } - public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { DebugGuardInScope(); - _stateProvider.AddToBalance(address, balanceChange, spec); + _stateProvider.AddToBalance(address, balanceChange, spec, out oldBalance); } - public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => AddToBalance(address, balanceChange, spec, out _); + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { DebugGuardInScope(); - return _stateProvider.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + return _stateProvider.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out oldBalance); } - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + public bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out _); + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { DebugGuardInScope(); - _stateProvider.SubtractFromBalance(address, balanceChange, spec); + _stateProvider.SubtractFromBalance(address, balanceChange, spec, out oldBalance); } + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + => SubtractFromBalance(address, balanceChange, spec, out _); public void UpdateStorageRoot(Address address, Hash256 storageRoot) { DebugGuardInScope(); diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index 32975d05647..e51f5152ece 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -26,9 +26,15 @@ public bool AccountExists(Address address) public virtual void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => _innerWorldState.AddToBalance(address, balanceChange, spec); + public virtual void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) + => _innerWorldState.AddToBalance(address, balanceChange, spec, out oldBalance); + public virtual bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); + public virtual bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) + => _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out oldBalance); + public virtual IDisposable BeginScope(BlockHeader? baseBlock) => _innerWorldState.BeginScope(baseBlock); @@ -122,6 +128,9 @@ public void SetTransientState(in StorageCell storageCell, byte[] newValue) public virtual void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => _innerWorldState.SubtractFromBalance(address, balanceChange, spec); + public virtual void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) + => _innerWorldState.SubtractFromBalance(address, balanceChange, spec, out oldBalance); + public Snapshot TakeSnapshot(bool newTransactionStart = false) => _innerWorldState.TakeSnapshot(newTransactionStart); From 9af311215bd70e1c8a5f80950237339a860d6a05 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:51:51 +0100 Subject: [PATCH 079/215] ignore old code --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 2 +- src/Nethermind/Nethermind.State/TracedAccessWorldState.cs | 3 +-- src/Nethermind/Nethermind.State/WorldState.cs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 699398a0255..dab232bae41 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -77,7 +77,7 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } - public void AddCodeChange(Address address, byte[] before, byte[] after) + public void AddCodeChange(Address address, byte[] after) { CodeChange codeChange = new() { diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index f735b6f76c9..4c6acd27608 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -114,8 +114,7 @@ public override void IncrementNonce(Address address, UInt256 delta, out UInt256 public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { - byte[] oldCode = _innerWorldState.GetCode(address); - BlockAccessList.AddCodeChange(address, oldCode, code.ToArray()); + BlockAccessList.AddCodeChange(address, code.ToArray()); return _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index c4ec6a1b15a..b7c6bc6e81a 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -14,7 +14,6 @@ using Nethermind.Evm.Tracing.State; using Nethermind.Int256; using Nethermind.Logging; -using Nethermind.Trie; using Nethermind.Trie.Pruning; [assembly: InternalsVisibleTo("Ethereum.Test.Base")] From fb14ec57a48f6d85de9a9e8f217c5c77d5623fe0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:45:55 +0100 Subject: [PATCH 080/215] check BAL ordering --- .../Eip7928/AccountChangesDecoder.cs | 66 +++++++++++++++++-- .../Eip7928/BlockAccessListDecoder.cs | 17 ++++- .../WorldStateMetricsDecorator.cs | 1 - 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index ee96760b0a0..b20cff501c9 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -21,21 +21,77 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe int check = length + ctx.Position; Address address = ctx.DecodeAddress(); + SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); - SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => s.Slot, s => s), Bytes.Comparer); + byte[] lastSlot = Bytes32.Zero.Unwrap(); + SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => + { + byte[] slot = s.Slot; + if (Bytes.BytesComparer.Compare(slot, lastSlot) <= 0) + { + throw new RlpException("Storage changes were in incorrect order."); + } + lastSlot = slot; + return slot; + }, s => s), Bytes.Comparer); + StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); + SortedSet storareReadsList = []; + StorageRead lastRead = new(Bytes32.Zero); + foreach (StorageRead storageRead in storageReads) + { + if (storageRead.CompareTo(lastRead) <= 0) + { + throw new RlpException("Storage reads were in incorrect order."); + } + storareReadsList.Add(storageRead); + lastRead = storageRead; + } + BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); - SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); + ushort lastIndex = 0; + SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { + ushort index = s.BlockAccessIndex; + if (index <= lastIndex) + { + throw new RlpException("Balance changes were in incorrect order."); + } + lastIndex = index; + return index; + }, s => s)); + + lastIndex = 0; NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); - SortedList nonceChangesList = new(nonceChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); + SortedList nonceChangesList = new(nonceChanges.ToDictionary(s => + { + ushort index = s.BlockAccessIndex; + if (index <= lastIndex) + { + throw new RlpException("Nonce changes were in incorrect order."); + } + lastIndex = index; + return index; + }, s => s)); + CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); - SortedList codeChangesList = new(codeChanges.ToDictionary(s => s.BlockAccessIndex, s => s)); if (codeChanges.Length > Eip7928Constants.MaxCodeChanges) { throw new RlpException("Number of code changes exceeded maximum."); } + lastIndex = 0; + SortedList codeChangesList = new(codeChanges.ToDictionary(s => + { + ushort index = s.BlockAccessIndex; + if (index <= lastIndex) + { + throw new RlpException("Code changes were in incorrect order."); + } + lastIndex = index; + return index; + }, s => s)); + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) { ctx.Check(check); @@ -45,7 +101,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe { Address = address, StorageChanges = slotChangesMap, - StorageReads = [.. storageReads], + StorageReads = storareReadsList, BalanceChanges = balanceChangesList, NonceChanges = nonceChangesList, CodeChanges = codeChangesList diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 3c538439cf7..0495fd6eda8 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -28,9 +28,24 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB throw new RlpException("Number of accounts exceeded maximum."); } - SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => a.Address, a => a)); + Address lastAddress = Address.Zero; + SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => + { + Address address = a.Address; + if (address.CompareTo(lastAddress) <= 0) + { + throw new RlpException("Account changes were in incorrect order."); + } + lastAddress = address; + return address; + }, a => a)); BlockAccessList blockAccessList = new(accountChangesMap); + if (!accountChanges.SequenceEqual(accountChangesMap.Values)) + { + throw new RlpException("Accounts were in incorrect order."); + } + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) { ctx.Check(check); diff --git a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs index 638d4764abb..60d49cc089f 100644 --- a/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs +++ b/src/Nethermind/Nethermind.State/WorldStateMetricsDecorator.cs @@ -8,7 +8,6 @@ namespace Nethermind.State; -// use WrappedWorldState public class WorldStateMetricsDecorator(IWorldState innerWorldState) : WrappedWorldState(innerWorldState) { public override void Reset(bool resetBlockChanges = true) From 6eb72328b524fea986d766c5e357998fdf29471c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 10 Sep 2025 14:51:00 +0100 Subject: [PATCH 081/215] tidy --- .../Eip7928/AccountChangesDecoder.cs | 5 +- .../TracedAccessWorldState.cs | 92 +------------------ 2 files changed, 5 insertions(+), 92 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index b20cff501c9..39684917f1e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -47,10 +47,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe storareReadsList.Add(storageRead); lastRead = storageRead; } - + BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); ushort lastIndex = 0; - SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { + SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => + { ushort index = s.BlockAccessIndex; if (index <= lastIndex) { diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 4c6acd27608..10be05b2366 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -17,14 +17,9 @@ public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldS public PreBlockCaches Caches => (_innerWorldState as IPreBlockCaches).Caches; - // public bool IsInScope => innerWorldState.IsInScope; - - // public Hash256 StateRoot => innerWorldState.StateRoot; - - // public bool AccountExists(Address address) => innerWorldState.AccountExists(address); - public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) => AddToBalance(address, balanceChange, spec, out _); + public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { _innerWorldState.AddToBalance(address, balanceChange, spec, out oldBalance); @@ -34,6 +29,7 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) => AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out _); + public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { bool res = _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out oldBalance); @@ -48,61 +44,12 @@ public override IDisposable BeginScope(BlockHeader? baseBlock) return _innerWorldState.BeginScope(baseBlock); } - // public void ClearStorage(Address address) - // => innerWorldState.ClearStorage(address); - - // public void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) - // => innerWorldState.Commit(releaseSpec, isGenesis, commitRoots); - - // public void Commit(IReleaseSpec releaseSpec, IWorldStateTracer tracer, bool isGenesis = false, bool commitRoots = true) - // => innerWorldState.Commit(releaseSpec, tracer, isGenesis, commitRoots); - - // public void CommitTree(long blockNumber) - // => innerWorldState.CommitTree(blockNumber); - - // public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) - // => - // innerWorldState.CreateAccount(address, balance, nonce); - - // public void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) => - // innerWorldState.CreateAccountIfNotExists(address, balance, nonce); - - // public void DecrementNonce(Address address, UInt256 delta) => - // innerWorldState.DecrementNonce(address, delta); - - // public void DeleteAccount(Address address) => - // innerWorldState.DeleteAccount(address); - public override ReadOnlySpan Get(in StorageCell storageCell) { BlockAccessList.AddStorageRead(storageCell); return _innerWorldState.Get(storageCell); } - // public ArrayPoolList? GetAccountChanges() => - // innerWorldState.GetAccountChanges(); - - // public ref readonly UInt256 GetBalance(Address address) => - // ref innerWorldState.GetBalance(address); - - // public byte[]? GetCode(Address address) => - // innerWorldState.GetCode(address); - - // public byte[]? GetCode(in ValueHash256 codeHash) => - // innerWorldState.GetCode(codeHash); - - // public ref readonly ValueHash256 GetCodeHash(Address address) => - // ref innerWorldState.GetCodeHash(address); - - // public byte[] GetOriginal(in StorageCell storageCell) => - // innerWorldState.GetOriginal(storageCell); - - // public ReadOnlySpan GetTransientState(in StorageCell storageCell) => - // innerWorldState.GetTransientState(storageCell); - - // public bool HasStateForBlock(BlockHeader? baseBlock) => - // innerWorldState.HasStateForBlock(baseBlock); - public override void IncrementNonce(Address address, UInt256 delta) => IncrementNonce(address, delta, out _); @@ -118,24 +65,6 @@ public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadO return _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); } - // public bool IsContract(Address address) => - // innerWorldState.IsContract(address); - - // public bool IsDeadAccount(Address address) => - // innerWorldState.IsDeadAccount(address); - - // public void RecalculateStateRoot() => - // innerWorldState.RecalculateStateRoot(); - - // public void Reset(bool resetBlockChanges = true) => - // innerWorldState.Reset(resetBlockChanges); - - // public void ResetTransient() => - // innerWorldState.ResetTransient(); - - // public void Restore(Snapshot snapshot) => - // innerWorldState.Restore(snapshot); - public override void Set(in StorageCell storageCell, byte[] newValue) { ReadOnlySpan oldValue = _innerWorldState.Get(storageCell); @@ -143,12 +72,6 @@ public override void Set(in StorageCell storageCell, byte[] newValue) _innerWorldState.Set(storageCell, newValue); } - // public override void SetNonce(Address address, in UInt256 nonce) => - // _innerWorldState.SetNonce(address, nonce); - - // public void SetTransientState(in StorageCell storageCell, byte[] newValue) => - // innerWorldState.SetTransientState(storageCell, newValue); - public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { UInt256 before = _innerWorldState.GetBalance(address); @@ -157,20 +80,9 @@ public override void SubtractFromBalance(Address address, in UInt256 balanceChan BlockAccessList.AddBalanceChange(address, before, after); } - // public Snapshot TakeSnapshot(bool newTransactionStart = false) => - // innerWorldState.TakeSnapshot(newTransactionStart); - public override bool TryGetAccount(Address address, out AccountStruct account) { BlockAccessList.AddAccountRead(address); return _innerWorldState.TryGetAccount(address, out account); } - // public void UpdateStorageRoot(Address address, Hash256 storageRoot) => - // innerWorldState.UpdateStorageRoot(address, storageRoot); - - // public void WarmUp(AccessList? accessList) => - // innerWorldState.WarmUp(accessList); - - // public void WarmUp(Address address) => - // innerWorldState.WarmUp(address); } From f0e7d59a1fa32da10a44857c926c9c9d191be695 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:42:27 +0100 Subject: [PATCH 082/215] make bal a journal --- .../BlockAccessLists/BlockAccessList.cs | 35 ++++++++++++------- .../Nethermind.Evm/State/Snapshot.cs | 5 +-- .../Nethermind.State/StateProvider.cs | 10 +++--- .../TracedAccessWorldState.cs | 13 +++++++ src/Nethermind/Nethermind.State/WorldState.cs | 6 ++-- .../Nethermind.State/WrappedWorldState.cs | 4 +-- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index dab232bae41..cee8557df27 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -8,10 +8,10 @@ namespace Nethermind.Core.BlockAccessLists; -public struct BlockAccessList : IEquatable +public struct BlockAccessList : IEquatable, IJournal { - private SortedDictionary _accountChanges { get; init; } - private ushort _blockAccessIndex = 0; + private readonly SortedDictionary _accountChanges; + public ushort Index = 0; public BlockAccessList() { @@ -42,7 +42,7 @@ public override readonly int GetHashCode() => public readonly AccountChanges? GetAccountChanges(Address address) => _accountChanges.TryGetValue(address, out AccountChanges value) ? value : null; public void IncrementBlockAccessIndex() - => _blockAccessIndex++; + => Index++; public void AddBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -53,7 +53,7 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) BalanceChange balanceChange = new() { - BlockAccessIndex = _blockAccessIndex, + BlockAccessIndex = Index, PostBalance = after!.Value }; @@ -70,7 +70,7 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) } SortedList balanceChanges = accountChanges.BalanceChanges; - if (balanceChanges.Count != 0 && balanceChanges.Last().Key == _blockAccessIndex) + if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { balanceChanges.RemoveAt(balanceChanges.Count - 1); } @@ -81,7 +81,7 @@ public void AddCodeChange(Address address, byte[] after) { CodeChange codeChange = new() { - BlockAccessIndex = _blockAccessIndex, + BlockAccessIndex = Index, NewCode = after }; @@ -92,7 +92,7 @@ public void AddCodeChange(Address address, byte[] after) } SortedList codeChanges = accountChanges.CodeChanges; - if (codeChanges.Count != 0 && codeChanges.Last().Key == _blockAccessIndex) + if (codeChanges.Count != 0 && codeChanges.Last().Key == Index) { codeChanges.RemoveAt(codeChanges.Count - 1); } @@ -108,7 +108,7 @@ public void AddNonceChange(Address address, ulong newNonce) NonceChange nonceChange = new() { - BlockAccessIndex = _blockAccessIndex, + BlockAccessIndex = Index, NewNonce = newNonce }; @@ -119,7 +119,7 @@ public void AddNonceChange(Address address, ulong newNonce) } SortedList nonceChanges = accountChanges.NonceChanges; - if (nonceChanges.Count != 0 && nonceChanges.Last().Key == _blockAccessIndex) + if (nonceChanges.Count != 0 && nonceChanges.Last().Key == Index) { nonceChanges.RemoveAt(nonceChanges.Count - 1); } @@ -188,7 +188,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan value.CopyTo(newValue[(32 - value.Length)..]); StorageChange storageChange = new() { - BlockAccessIndex = _blockAccessIndex, + BlockAccessIndex = Index, NewValue = new(newValue.ToArray()) }; @@ -198,11 +198,22 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan { storageChanges = new(storageKey); } - else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == _blockAccessIndex) + else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } storageChanges.Changes.Add(storageChange); accountChanges.StorageChanges[storageKey] = storageChanges; } + + public int TakeSnapshot() + { + // throw new NotImplementedException(); + return 0; + } + + public void Restore(int snapshot) + { + throw new NotImplementedException(); + } } diff --git a/src/Nethermind/Nethermind.Evm/State/Snapshot.cs b/src/Nethermind/Nethermind.Evm/State/Snapshot.cs index 7f5a123bc6d..4dce2166e5d 100644 --- a/src/Nethermind/Nethermind.Evm/State/Snapshot.cs +++ b/src/Nethermind/Nethermind.Evm/State/Snapshot.cs @@ -7,9 +7,9 @@ namespace Nethermind.Evm.State; /// Stores state and storage snapshots (as the change index that we can revert to) /// At the beginning and after each commit the snapshot is set to the value of EmptyPosition /// -public readonly struct Snapshot(in Snapshot.Storage storageSnapshot, int stateSnapshot) +public readonly struct Snapshot(in Snapshot.Storage storageSnapshot, int stateSnapshot, int balSnapshot) { - public static readonly Snapshot Empty = new(Storage.Empty, EmptyPosition); + public static readonly Snapshot Empty = new(Storage.Empty, EmptyPosition, EmptyPosition); /// /// Tracks snapshot positions for Persistent and Transient storage @@ -24,6 +24,7 @@ public readonly struct Storage(int storageSnapshot, int transientStorageSnapshot public Storage StorageSnapshot { get; } = storageSnapshot; public int StateSnapshot { get; } = stateSnapshot; + public int BlockAccessListSnapshot { get; } = balSnapshot; public const int EmptyPosition = -1; } diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 09be3d30f01..29094593911 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -29,13 +29,13 @@ namespace Nethermind.State { - internal class StateProvider + internal class StateProvider : IJournal { private static readonly UInt256 _zero = UInt256.Zero; - private readonly Dictionary> _intraTxCache = new(); - private readonly HashSet _committedThisRound = new(); - private readonly HashSet _nullAccountReads = new(); + private readonly Dictionary> _intraTxCache = []; + private readonly HashSet _committedThisRound = []; + private readonly HashSet _nullAccountReads = []; // Only guarding against hot duplicates so filter doesn't need to be too big // Note: // False negatives are fine as they will just result in a overwrite set @@ -44,7 +44,7 @@ internal class StateProvider private readonly Dictionary _blockChanges = new(4_096); private readonly ConcurrentDictionary? _preBlockCache; - private readonly List _keptInCache = new(); + private readonly List _keptInCache = []; private readonly ILogger _logger; private readonly IKeyValueStoreWithBatching _codeDb; private Dictionary _codeBatch; diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 10be05b2366..15418241d1e 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -85,4 +85,17 @@ public override bool TryGetAccount(Address address, out AccountStruct account) BlockAccessList.AddAccountRead(address); return _innerWorldState.TryGetAccount(address, out account); } + + public override void Restore(Snapshot snapshot) + { + BlockAccessList.Restore(snapshot.BlockAccessListSnapshot); + _innerWorldState.Restore(snapshot); + } + + public override Snapshot TakeSnapshot(bool newTransactionStart = false) + { + int blockAccessListSnapshot = BlockAccessList.TakeSnapshot(); + Snapshot snapshot = _innerWorldState.TakeSnapshot(newTransactionStart); + return new(snapshot.StorageSnapshot, snapshot.StateSnapshot, blockAccessListSnapshot); + } } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index b7c6bc6e81a..1116ed58184 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -347,9 +347,9 @@ public Snapshot TakeSnapshot(bool newTransactionStart = false) DebugGuardInScope(); int persistentSnapshot = _persistentStorageProvider.TakeSnapshot(newTransactionStart); int transientSnapshot = _transientStorageProvider.TakeSnapshot(newTransactionStart); - Snapshot.Storage storageSnapshot = new Snapshot.Storage(persistentSnapshot, transientSnapshot); + Snapshot.Storage storageSnapshot = new (persistentSnapshot, transientSnapshot); int stateSnapshot = _stateProvider.TakeSnapshot(); - return new Snapshot(storageSnapshot, stateSnapshot); + return new Snapshot(storageSnapshot, stateSnapshot, -1); } public void Restore(Snapshot snapshot) @@ -363,7 +363,7 @@ public void Restore(Snapshot snapshot) internal void Restore(int state, int persistentStorage, int transientStorage) { DebugGuardInScope(); - Restore(new Snapshot(new Snapshot.Storage(persistentStorage, transientStorage), state)); + Restore(new Snapshot(new Snapshot.Storage(persistentStorage, transientStorage), state, -1)); } public void SetNonce(Address address, in UInt256 nonce) diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index e51f5152ece..b6d63c01834 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -113,7 +113,7 @@ public virtual void Reset(bool resetBlockChanges = true) public void ResetTransient() => _innerWorldState.ResetTransient(); - public void Restore(Snapshot snapshot) + public virtual void Restore(Snapshot snapshot) => _innerWorldState.Restore(snapshot); public virtual void Set(in StorageCell storageCell, byte[] newValue) @@ -131,7 +131,7 @@ public virtual void SubtractFromBalance(Address address, in UInt256 balanceChang public virtual void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) => _innerWorldState.SubtractFromBalance(address, balanceChange, spec, out oldBalance); - public Snapshot TakeSnapshot(bool newTransactionStart = false) + public virtual Snapshot TakeSnapshot(bool newTransactionStart = false) => _innerWorldState.TakeSnapshot(newTransactionStart); public virtual bool TryGetAccount(Address address, out AccountStruct account) From 595b62ca21d93c2bf7df81cecceb9b1d303caf9c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:28:34 +0100 Subject: [PATCH 083/215] add reverting transaction test --- .../BlockAccessListTests.cs | 42 ++++++++++++++++--- .../BlockAccessLists/BlockAccessList.cs | 2 +- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index b5adef2a911..d52754d7cfa 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -162,7 +162,7 @@ public async Task Can_construct_BAL() (worldState as TracedAccessWorldState)!.BlockAccessList = new(); - const long gasUsed = 92100; + const long gasUsed = 167340; const ulong gasPrice = 2; const long gasLimit = 100000; const ulong timestamp = 1000000; @@ -186,12 +186,32 @@ public async Task Can_construct_BAL() .WithCode(Eip2935TestConstants.InitCode) .TestObject; + /* + Store followed by revert should undo storage change + PUSH1 1 + PUSH1 1 + SSTORE + PUSH0 + PUSH0 + REVERT + */ + byte[] code = Bytes.FromHexString("0x60016001555f5ffd"); + Transaction tx3 = Build.A.Transaction + .WithTo(null) + .WithSenderAddress(TestItem.AddressA) + .WithValue(0) + .WithNonce(2) + .WithGasPrice(gasPrice) + .WithGasLimit(gasLimit) + .WithCode(code) + .TestObject; + BlockHeader header = Build.A.BlockHeader .WithBaseFee(1) .WithNumber(1) .WithGasUsed(gasUsed) - .WithReceiptsRoot(new("0x6ade9745ba09d7b426314ec12280d510e4811867c2b56f215385842ffb43edf9")) - .WithStateRoot(new("0x18a32a11a465a81922828c9b2289924065a37a6961cbbef6633e57b465c11c9d")) + .WithReceiptsRoot(new("0x3d4548dff4e45f6e7838b223bf9476cd5ba4fd05366e8cb4e6c9b65763209569")) + .WithStateRoot(new("0x9399acd9f2603778c11646f05f7827509b5319815da74b5721a07defb6285c8d")) .WithBlobGasUsed(0) .WithBeneficiary(TestItem.AddressC) .WithParentBeaconBlockRoot(Hash256.Zero) @@ -209,7 +229,7 @@ public async Task Can_construct_BAL() }; Block block = Build.A.Block - .WithTransactions([tx, tx2]) + .WithTransactions([tx, tx2, tx3]) .WithBaseFeePerGas(1) .WithWithdrawals([withdrawal]) .WithHeader(header).TestObject; @@ -217,15 +237,17 @@ public async Task Can_construct_BAL() (Block processedBlock, TxReceipt[] _) = testBlockchain.BlockProcessor.ProcessOne(block, ProcessingOptions.None, NullBlockTracer.Instance, _spec, CancellationToken.None); BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); - Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(9)); + Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(10)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); + Address newContractAddress2 = ContractAddress.From(TestItem.AddressA, 2); AccountChanges addressAChanges = blockAccessList.GetAccountChanges(TestItem.AddressA)!.Value; AccountChanges addressBChanges = blockAccessList.GetAccountChanges(TestItem.AddressB)!.Value; AccountChanges addressCChanges = blockAccessList.GetAccountChanges(TestItem.AddressC)!.Value; AccountChanges addressDChanges = blockAccessList.GetAccountChanges(TestItem.AddressD)!.Value; AccountChanges newContractChanges = blockAccessList.GetAccountChanges(newContractAddress)!.Value; + AccountChanges newContractChanges2 = blockAccessList.GetAccountChanges(newContractAddress2)!.Value; AccountChanges eip2935Changes = blockAccessList.GetAccountChanges(Eip2935Constants.BlockHashHistoryAddress)!.Value; AccountChanges eip4788Changes = blockAccessList.GetAccountChanges(Eip4788Constants.BeaconRootsAddress)!.Value; AccountChanges eip7002Changes = blockAccessList.GetAccountChanges(Eip7002Constants.WithdrawalRequestPredeployAddress)!.Value; @@ -297,6 +319,16 @@ public async Task Can_construct_BAL() CodeChanges = new SortedList { { 2, new(2, Eip2935TestConstants.Code) } } })); + Assert.That(newContractChanges2, Is.EqualTo(new AccountChanges() + { + Address = newContractAddress, + StorageChanges = [], + StorageReads = [ToStorageRead(slot1)], + BalanceChanges = [], + NonceChanges = new SortedList { { 3, new(3, 1) } }, + CodeChanges = [] + })); + Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() { Address = Eip2935Constants.BlockHashHistoryAddress, diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index cee8557df27..b52133a5e4d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -214,6 +214,6 @@ public int TakeSnapshot() public void Restore(int snapshot) { - throw new NotImplementedException(); + // throw new NotImplementedException(); } } From 72957993727b7465ff9bb59405fee75d4e6bd5d4 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:14:26 +0100 Subject: [PATCH 084/215] only construct BAL when enabled --- .../BlockAccessListTests.cs | 16 +- .../Processing/BlockProcessor.cs | 3 +- .../BlockAccessLists/BalanceChange.cs | 2 +- .../BlockAccessLists/BlockAccessList.cs | 138 +++++++++++++++++- .../BlockAccessLists/CodeChange.cs | 2 +- .../BlockAccessLists/NonceChange.cs | 2 +- .../BlockAccessLists/StorageChange.cs | 2 +- .../TracedAccessWorldState.cs | 57 ++++++-- src/Nethermind/Nethermind.State/WorldState.cs | 2 +- 9 files changed, 193 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index d52754d7cfa..b7df52bd5f4 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -163,6 +163,7 @@ public async Task Can_construct_BAL() (worldState as TracedAccessWorldState)!.BlockAccessList = new(); const long gasUsed = 167340; + const long gasUsedBeforeFinal = 92100; const ulong gasPrice = 2; const long gasLimit = 100000; const ulong timestamp = 1000000; @@ -265,7 +266,8 @@ PUSH1 1 StorageChange zeroStorageChangeEnd = new(3, Bytes32.Zero); UInt256 addressABalance = _accountBalance - gasPrice * GasCostOf.Transaction; - UInt256 addressABalance2 = _accountBalance - gasPrice * gasUsed; + UInt256 addressABalance2 = _accountBalance - gasPrice * gasUsedBeforeFinal; + UInt256 addressABalance3 = _accountBalance - gasPrice * gasUsed; using (Assert.EnterMultipleScope()) { @@ -274,8 +276,8 @@ PUSH1 1 Address = TestItem.AddressA, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2) } }, - NonceChanges = new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) } }, + BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2) }, { 3, new(3, addressABalance3) } }, + NonceChanges = new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) }, { 3, new(3, 3) } }, CodeChanges = [] })); @@ -294,7 +296,7 @@ PUSH1 1 Address = TestItem.AddressC, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsed)) } }, + BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsedBeforeFinal)) }, { 3, new(3, new UInt256(gasUsed)) } }, NonceChanges = [], CodeChanges = [] })); @@ -304,7 +306,7 @@ PUSH1 1 Address = TestItem.AddressD, StorageChanges = [], StorageReads = [], - BalanceChanges = new SortedList { { 3, new(3, 1.GWei()) } }, + BalanceChanges = new SortedList { { 4, new(4, 1.GWei()) } }, NonceChanges = [], CodeChanges = [] })); @@ -321,11 +323,11 @@ PUSH1 1 Assert.That(newContractChanges2, Is.EqualTo(new AccountChanges() { - Address = newContractAddress, + Address = newContractAddress2, StorageChanges = [], StorageReads = [ToStorageRead(slot1)], BalanceChanges = [], - NonceChanges = new SortedList { { 3, new(3, 1) } }, + NonceChanges = [], CodeChanges = [] })); diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index f4883655f08..07b1d7d526c 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -132,11 +132,12 @@ protected virtual TxReceipt[] ProcessBlock( BlockHeader header = block.Header; ReceiptsTracer.SetOtherTracer(blockTracer); - ReceiptsTracer.StartNewBlockTrace(block); _blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); + _tracedAccessWorldState.Enabled = spec.BlockLevelAccessListsEnabled; + StoreBeaconRoot(block, spec); _blockHashStore.ApplyBlockhashStateChanges(header, spec); _stateProvider.Commit(spec, commitRoots: false); diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index 5625667417c..15b822be3f8 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -6,7 +6,7 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct BalanceChange(ushort blockAccessIndex, UInt256 postBalance) : IEquatable +public readonly struct BalanceChange(ushort blockAccessIndex, UInt256 postBalance) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; public UInt256 PostBalance { get; init; } = postBalance; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index b52133a5e4d..25e94653c75 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -10,17 +10,20 @@ namespace Nethermind.Core.BlockAccessLists; public struct BlockAccessList : IEquatable, IJournal { - private readonly SortedDictionary _accountChanges; public ushort Index = 0; + private readonly SortedDictionary _accountChanges; + private readonly Stack _changes; public BlockAccessList() { _accountChanges = []; + _changes = new(); } public BlockAccessList(SortedDictionary accountChanges) { _accountChanges = accountChanges; + _changes = new(); } public readonly bool Equals(BlockAccessList other) => @@ -42,7 +45,10 @@ public override readonly int GetHashCode() => public readonly AccountChanges? GetAccountChanges(Address address) => _accountChanges.TryGetValue(address, out AccountChanges value) ? value : null; public void IncrementBlockAccessIndex() - => Index++; + { + _changes.Clear(); + Index++; + } public void AddBalanceChange(Address address, UInt256? before, UInt256? after) { @@ -72,8 +78,23 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) SortedList balanceChanges = accountChanges.BalanceChanges; if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { + _changes.Push(new() + { + Address = address, + Type = ChangeType.BalanceChange, + PreviousValue = balanceChanges.Last().Value + }); balanceChanges.RemoveAt(balanceChanges.Count - 1); } + else + { + _changes.Push(new() + { + Address = address, + Type = ChangeType.BalanceChange, + PreviousValue = null + }); + } balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } @@ -94,8 +115,23 @@ public void AddCodeChange(Address address, byte[] after) SortedList codeChanges = accountChanges.CodeChanges; if (codeChanges.Count != 0 && codeChanges.Last().Key == Index) { + _changes.Push(new() + { + Address = address, + Type = ChangeType.CodeChange, + PreviousValue = codeChanges.Last().Value + }); codeChanges.RemoveAt(codeChanges.Count - 1); } + else + { + _changes.Push(new() + { + Address = address, + Type = ChangeType.CodeChange, + PreviousValue = null + }); + } codeChanges.Add(codeChange.BlockAccessIndex, codeChange); } @@ -121,8 +157,23 @@ public void AddNonceChange(Address address, ulong newNonce) SortedList nonceChanges = accountChanges.NonceChanges; if (nonceChanges.Count != 0 && nonceChanges.Last().Key == Index) { + _changes.Push(new() + { + Address = address, + Type = ChangeType.NonceChange, + PreviousValue = nonceChanges.Last().Value + }); nonceChanges.RemoveAt(nonceChanges.Count - 1); } + else + { + _changes.Push(new() + { + Address = address, + Type = ChangeType.NonceChange, + PreviousValue = null + }); + } nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); } @@ -193,6 +244,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan }; byte[] storageKey = [.. key]; + StorageChange? previousStorage = null; if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) { @@ -200,20 +252,94 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan } else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { + previousStorage = storageChanges.Changes[^1]; storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } storageChanges.Changes.Add(storageChange); accountChanges.StorageChanges[storageKey] = storageChanges; + _changes.Push(new() + { + Address = accountChanges.Address, + Slot = storageKey, + Type = ChangeType.StorageChange, + PreviousValue = previousStorage + }); } - public int TakeSnapshot() + public readonly int TakeSnapshot() + => _changes.Count; + + public void Restore(int snapshot) { + snapshot = int.Max(0, snapshot); + while (_changes.Count > snapshot) + { + Change change = _changes.Pop(); + switch (change.Type) + { + case ChangeType.BalanceChange: + BalanceChange? previousBalance = change.PreviousValue is null ? null : (BalanceChange)change.PreviousValue; + SortedList balanceChanges = _accountChanges[change.Address].BalanceChanges; + + balanceChanges.RemoveAt(balanceChanges.Count - 1); + if (previousBalance is not null) + { + balanceChanges.Add(Index, previousBalance.Value); + } + break; + case ChangeType.CodeChange: + CodeChange? previousCode = change.PreviousValue is null ? null : (CodeChange)change.PreviousValue; + SortedList codeChanges = _accountChanges[change.Address].CodeChanges; + + codeChanges.RemoveAt(codeChanges.Count - 1); + if (previousCode is not null) + { + codeChanges.Add(Index, previousCode.Value); + } + break; + case ChangeType.NonceChange: + NonceChange? previousNode = change.PreviousValue is null ? null : (NonceChange)change.PreviousValue; + SortedList nonceChanges = _accountChanges[change.Address].NonceChanges; + + nonceChanges.RemoveAt(nonceChanges.Count - 1); + if (previousNode is not null) + { + nonceChanges.Add(Index, previousNode.Value); + } + break; + case ChangeType.StorageChange: + StorageChange? previousStorage = change.PreviousValue is null ? null : (StorageChange)change.PreviousValue; + SlotChanges storageChanges = _accountChanges[change.Address].StorageChanges[change.Slot!]; + + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + if (previousStorage is not null) + { + storageChanges.Changes.Add(previousStorage.Value); + } + + if (storageChanges.Changes.Count == 0) + { + _accountChanges[change.Address].StorageChanges.Remove(change.Slot!); + } + break; + } + } + // throw new NotImplementedException(); - return 0; } - public void Restore(int snapshot) + private enum ChangeType { - // throw new NotImplementedException(); + BalanceChange = 0, + CodeChange = 1, + NonceChange = 2, + StorageChange = 3 + } + private readonly struct Change + { + public Address Address { get; init; } + public byte[]? Slot { get; init; } + public ChangeType Type { get; init; } + public IIndexedChange? PreviousValue { get; init; } } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index dcc1782dd4f..36b4afdb5c1 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -7,7 +7,7 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct CodeChange(ushort blockAccessIndex, byte[] newCode) : IEquatable +public readonly struct CodeChange(ushort blockAccessIndex, byte[] newCode) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; public byte[] NewCode { get; init; } = newCode; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs index 5b163343341..3907f1c218f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -5,7 +5,7 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct NonceChange(ushort blockAccessIndex, ulong newNonce) : IEquatable +public readonly struct NonceChange(ushort blockAccessIndex, ulong newNonce) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; public ulong NewNonce { get; init; } = newNonce; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index 6efe2af7fff..021a20de7cb 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -7,7 +7,7 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct StorageChange(ushort blockAccessIndex, Bytes32 newValue) : IEquatable +public readonly struct StorageChange(ushort blockAccessIndex, Bytes32 newValue) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; public Bytes32 NewValue { get; init; } = newValue; diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 15418241d1e..b5e706d19d1 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -13,6 +13,7 @@ namespace Nethermind.State; public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldState(innerWorldState), IPreBlockCaches { + public bool Enabled { get; set; } = false; public BlockAccessList BlockAccessList = new(); public PreBlockCaches Caches => (_innerWorldState as IPreBlockCaches).Caches; @@ -23,8 +24,12 @@ public override void AddToBalance(Address address, in UInt256 balanceChange, IRe public override void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { _innerWorldState.AddToBalance(address, balanceChange, spec, out oldBalance); - UInt256 newBalance = oldBalance + balanceChange; - BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); + + if (Enabled) + { + UInt256 newBalance = oldBalance + balanceChange; + BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); + } } public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) @@ -33,8 +38,13 @@ public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt25 public override bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec, out UInt256 oldBalance) { bool res = _innerWorldState.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec, out oldBalance); - UInt256 newBalance = oldBalance + balanceChange; - BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); + + if (Enabled) + { + UInt256 newBalance = oldBalance + balanceChange; + BlockAccessList.AddBalanceChange(address, oldBalance, newBalance); + } + return res; } @@ -46,7 +56,10 @@ public override IDisposable BeginScope(BlockHeader? baseBlock) public override ReadOnlySpan Get(in StorageCell storageCell) { - BlockAccessList.AddStorageRead(storageCell); + if (Enabled) + { + BlockAccessList.AddStorageRead(storageCell); + } return _innerWorldState.Get(storageCell); } @@ -56,19 +69,29 @@ public override void IncrementNonce(Address address, UInt256 delta) public override void IncrementNonce(Address address, UInt256 delta, out UInt256 oldNonce) { _innerWorldState.IncrementNonce(address, delta, out oldNonce); - BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); + + if (Enabled) + { + BlockAccessList.AddNonceChange(address, (ulong)(oldNonce + delta)); + } } public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { - BlockAccessList.AddCodeChange(address, code.ToArray()); + if (Enabled) + { + BlockAccessList.AddCodeChange(address, code.ToArray()); + } return _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); } public override void Set(in StorageCell storageCell, byte[] newValue) { - ReadOnlySpan oldValue = _innerWorldState.Get(storageCell); - BlockAccessList.AddStorageChange(storageCell, [.. oldValue], newValue); + if (Enabled) + { + ReadOnlySpan oldValue = _innerWorldState.Get(storageCell); + BlockAccessList.AddStorageChange(storageCell, [.. oldValue], newValue); + } _innerWorldState.Set(storageCell, newValue); } @@ -77,18 +100,28 @@ public override void SubtractFromBalance(Address address, in UInt256 balanceChan UInt256 before = _innerWorldState.GetBalance(address); UInt256 after = before - balanceChange; _innerWorldState.SubtractFromBalance(address, balanceChange, spec); - BlockAccessList.AddBalanceChange(address, before, after); + + if (Enabled) + { + BlockAccessList.AddBalanceChange(address, before, after); + } } public override bool TryGetAccount(Address address, out AccountStruct account) { - BlockAccessList.AddAccountRead(address); + if (Enabled) + { + BlockAccessList.AddAccountRead(address); + } return _innerWorldState.TryGetAccount(address, out account); } public override void Restore(Snapshot snapshot) { - BlockAccessList.Restore(snapshot.BlockAccessListSnapshot); + if (Enabled) + { + BlockAccessList.Restore(snapshot.BlockAccessListSnapshot); + } _innerWorldState.Restore(snapshot); } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 1116ed58184..986be3effe7 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -347,7 +347,7 @@ public Snapshot TakeSnapshot(bool newTransactionStart = false) DebugGuardInScope(); int persistentSnapshot = _persistentStorageProvider.TakeSnapshot(newTransactionStart); int transientSnapshot = _transientStorageProvider.TakeSnapshot(newTransactionStart); - Snapshot.Storage storageSnapshot = new (persistentSnapshot, transientSnapshot); + Snapshot.Storage storageSnapshot = new(persistentSnapshot, transientSnapshot); int stateSnapshot = _stateProvider.TakeSnapshot(); return new Snapshot(storageSnapshot, stateSnapshot, -1); } From 2c5a9e905530e322c83353f5232e1ac161cc1103 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:14:36 +0100 Subject: [PATCH 085/215] add IIndexedChange --- .../Nethermind.Core/BlockAccessLists/IIndexedChange.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Nethermind/Nethermind.Core/BlockAccessLists/IIndexedChange.cs diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/IIndexedChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/IIndexedChange.cs new file mode 100644 index 00000000000..abeb28f06b3 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/IIndexedChange.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.BlockAccessLists; + +public interface IIndexedChange +{ + public ushort BlockAccessIndex { get; init; } +} From aceb9e0c4a3d6b8d8e7a1d823ff8193eddaa8f87 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:51:28 +0100 Subject: [PATCH 086/215] check for null traced access worldstate --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 9a61670d02c..f181d77adb9 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -130,7 +130,10 @@ protected virtual TxReceipt[] ProcessBlock( _blockTransactionsExecutor.SetBlockExecutionContext(new BlockExecutionContext(block.Header, spec)); - _tracedAccessWorldState.Enabled = spec.BlockLevelAccessListsEnabled; + if (_tracedAccessWorldState is not null) + { + _tracedAccessWorldState.Enabled = spec.BlockLevelAccessListsEnabled; + } StoreBeaconRoot(block, spec); _blockHashStore.ApplyBlockhashStateChanges(header, spec); @@ -176,7 +179,7 @@ protected virtual TxReceipt[] ProcessBlock( header.Hash = header.CalculateHash(); - if (spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { body.BlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; } From 5b016d0f6dc327eee3c2bc98b0753e10f40627a0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:25:20 +0100 Subject: [PATCH 087/215] fix hash calc --- src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index a3bbb7eb2e4..cc9b7b35a11 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -169,7 +169,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) TotalDifficulty = totalDifficulty, TxRoot = TxTrie.CalculateRoot(transactions.Transactions), WithdrawalsRoot = BuildWithdrawalsRoot(), - BlockAccessListHash = new Hash256(BlockAccessList) //valuehash + BlockAccessListHash = new(ValueKeccak.Compute(BlockAccessList).Bytes) }; return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, BlockAccessList)); From 95a5da78f7dab4d689699d8d5ccde857aeb3e905 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:31:41 +0100 Subject: [PATCH 088/215] disable in chainspec params test --- src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs index a1f74e3bca3..15840bf7f84 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainParametersTests.cs @@ -40,7 +40,8 @@ public void ChainParameters_should_be_loaded_from_chainSpecParamsJson() "MaxCodeSizeTransitionTimestamp", "Eip4844FeeCollectorTransitionTimestamp", "Eip6110TransitionTimestamp", - "Eip7692TransitionTimestamp" + "Eip7692TransitionTimestamp", + "Eip7928TransitionTimestamp" // tmp ]; const ulong testValue = 1ul; From f05fcd7de8d3bd6482a23b3d9948ebac785c83a9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:45:22 +0100 Subject: [PATCH 089/215] bal snapshot fix --- src/Nethermind/Nethermind.Evm/State/Snapshot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/State/Snapshot.cs b/src/Nethermind/Nethermind.Evm/State/Snapshot.cs index 05f6d24759e..50234115347 100644 --- a/src/Nethermind/Nethermind.Evm/State/Snapshot.cs +++ b/src/Nethermind/Nethermind.Evm/State/Snapshot.cs @@ -20,7 +20,7 @@ public Snapshot(in Storage storageSnapshot, int stateSnapshot) { StorageSnapshot = storageSnapshot; StateSnapshot = stateSnapshot; - BlockAccessListSnapshot = 0; + BlockAccessListSnapshot = EmptyPosition; } public static readonly Snapshot Empty = new(Storage.Empty, EmptyPosition, EmptyPosition); From 31fdc5de7ef944c826b648519782a1a49f5285c0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:49:02 +0100 Subject: [PATCH 090/215] fix hash match check --- .../Nethermind.Consensus/Validators/BlockValidator.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index dd2dc29be67..cf7cf13c087 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -422,7 +422,7 @@ public virtual bool ValidateBlockLevelAccessList(Block block, IReleaseSpec spec, if (block.BlockAccessList is not null) { - if (!ValidateBlockLevelAccessListHashMatces(block.Header, block.Body, out Hash256 blockLevelAccessListRoot)) + if (!ValidateBlockLevelAccessListHashMatches(block.Header, block.Body, out Hash256 blockLevelAccessListRoot)) { error = BlockErrorMessages.InvalidBlockLevelAccessListRoot(block.Header.BlockAccessListHash, blockLevelAccessListRoot); if (_logger.IsWarn) _logger.Warn($"Block level access list root hash mismatch in block {block.ToString(Block.Format.FullHashAndNumber)}: expected {block.Header.BlockAccessListHash}, got {blockLevelAccessListRoot}"); @@ -463,7 +463,7 @@ public static bool ValidateWithdrawalsHashMatches(BlockHeader header, BlockBody return (withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash) == header.WithdrawalsRoot; } - public static bool ValidateBlockLevelAccessListHashMatces(BlockHeader header, BlockBody body, out Hash256? blockLevelAccessListRoot) + public static bool ValidateBlockLevelAccessListHashMatches(BlockHeader header, BlockBody body, out Hash256? blockLevelAccessListRoot) { if (body.BlockAccessList is null) { @@ -471,8 +471,9 @@ public static bool ValidateBlockLevelAccessListHashMatces(BlockHeader header, Bl return header.BlockAccessListHash is null; } - // todo: SSZ encode here - return (blockLevelAccessListRoot = Hash256.Zero) == header.BlockAccessListHash; + blockLevelAccessListRoot = new(ValueKeccak.Compute(body.BlockAccessList).Bytes); + + return blockLevelAccessListRoot == header.BlockAccessListHash; } private static string Invalid(Block block) => From f3df8ba0295575a11487481d500d6f6e76f9b222 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:39:25 +0100 Subject: [PATCH 091/215] remove comments --- .../Blockchain/TestBlockchain.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index f0c3f0bbaff..a7e4aa5d7d4 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -350,18 +350,6 @@ public Block Build() state.InsertCode(Eip7251Constants.ConsolidationRequestPredeployAddress, Eip7251TestConstants.CodeHash, Eip7251TestConstants.Code, specProvider.GenesisSpec); } - // if (finalSpec?.IsEip2935Enabled is true) - // { - // state.CreateAccount(Eip2935Constants.BlockHashHistoryAddress, 0, Eip2935TestConstants.Nonce); - // state.InsertCode(Eip2935Constants.BlockHashHistoryAddress, Eip2935TestConstants.CodeHash, Eip2935TestConstants.Code, specProvider.GenesisSpec); - // } - - // if (finalSpec?.IsEip4788Enabled is true) - // { - // state.CreateAccount(Eip4788Constants.BeaconRootsAddress, 0, Eip4788TestConstants.Nonce); - // state.InsertCode(Eip4788Constants.BeaconRootsAddress, Eip4788TestConstants.CodeHash, Eip4788TestConstants.Code, specProvider.GenesisSpec); - // } - BlockBuilder genesisBlockBuilder = Builders.Build.A.Block.Genesis; if (specProvider.SealEngine == Core.SealEngineType.AuRa) From ad7d5c89f694a367cc52a6ed8a8b6126af71ef07 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:05:55 +0100 Subject: [PATCH 092/215] null hash for empty access lsit --- .../Nethermind.Core.Test/Builders/BlockBuilder.cs | 6 ++++++ .../Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs index dfb6db340be..0da252922be 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs @@ -301,5 +301,11 @@ public BlockBuilder WithEncodedSize(int? encodedSize) TestObjectInternal.EncodedSize = encodedSize; return this; } + + public BlockBuilder WithBlockAccessListHash(Hash256? hash) + { + TestObjectInternal.Header.BlockAccessListHash = hash; + return this; + } } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index cc9b7b35a11..b5e449ce311 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -169,7 +169,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) TotalDifficulty = totalDifficulty, TxRoot = TxTrie.CalculateRoot(transactions.Transactions), WithdrawalsRoot = BuildWithdrawalsRoot(), - BlockAccessListHash = new(ValueKeccak.Compute(BlockAccessList).Bytes) + BlockAccessListHash = BlockAccessList.Length == 0 ? null : new(ValueKeccak.Compute(BlockAccessList).Bytes) }; return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, BlockAccessList)); From e2643aa40e8c0107a415252696fadf8d2155e837 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:02:50 +0100 Subject: [PATCH 093/215] amsterdam fork --- .../Nethermind.Specs/Forks/20_Amsterdam.cs | 21 +++++++++++++++++++ .../Nethermind.Specs/SpecNameParser.cs | 1 + 2 files changed, 22 insertions(+) create mode 100644 src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs diff --git a/src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs b/src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs new file mode 100644 index 00000000000..ed01d5d6ea3 --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using Nethermind.Core.Specs; + +namespace Nethermind.Specs.Forks; + +public class Amsterdam : Osaka +{ + private static IReleaseSpec _instance; + + public Amsterdam() + { + Name = "Amsterdam"; + IsEip7928Enabled = true; + Released = false; + } + + public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new Amsterdam()); +} diff --git a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs index 0b4abd07435..a3022fe5487 100644 --- a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs +++ b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs @@ -54,6 +54,7 @@ public static IReleaseSpec Parse(string specName) "Paris" => Paris.Instance, "Prague" => Prague.Instance, "Osaka" => Osaka.Instance, + "Amsterdam" => Amsterdam.Instance, _ => throw new NotSupportedException() }; } From cf9629ec24614164a7ddf6fcd80e0e7f373c8e0d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:01:36 +0100 Subject: [PATCH 094/215] add bal to block & header decoder, store encoded bal in block, update blockchain test runners --- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 2 + .../Ethereum.Test.Base/TestBlockHeaderJson.cs | 2 + .../BlockAccessListTests.cs | 3 +- .../Messages/BlockErrorMessages.cs | 3 ++ .../Processing/BlockProcessor.cs | 12 +++-- .../Validators/BlockValidator.cs | 44 ++++++++++++------- src/Nethermind/Nethermind.Core/Block.cs | 10 +++-- src/Nethermind/Nethermind.Core/BlockBody.cs | 6 ++- .../Data/ExecutionPayload.cs | 5 ++- .../BlockBodyDecoder.cs | 30 ++++++++++--- .../BlockDecoder.cs | 19 +++++--- .../HeaderDecoder.cs | 43 ++++++++++-------- .../Nethermind.Serialization.Rlp/RlpStream.cs | 36 +++++++-------- .../BlockchainTestsRunner.cs | 13 +++++- 14 files changed, 148 insertions(+), 80 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 9a09b17017c..656d5f681be 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -67,6 +67,8 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) header.ReceiptsRoot = new Hash256(headerJson.ReceiptTrie); header.StateRoot = new Hash256(headerJson.StateRoot); header.TxRoot = new Hash256(headerJson.TransactionsTrie); + header.WithdrawalsRoot = new Hash256(headerJson.WithdrawalsRoot); + header.BlockAccessListHash = new Hash256(headerJson.BlockAccessListHash); return header; } diff --git a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs index 3508dba38c6..826c1e3cbc1 100644 --- a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs @@ -21,5 +21,7 @@ public class TestBlockHeaderJson public string Timestamp { get; set; } public string TransactionsTrie { get; set; } public string UncleHash { get; set; } + public string WithdrawalsRoot { get; set; } + public string BlockAccessListHash { get; set; } } } diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 3585a8972f0..de995f9e9f1 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -245,7 +245,8 @@ PUSH1 1 // Block processedBlock = res[0]; // Block processedBlock = Build.A.Block.TestObject; - BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); + // BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); + BlockAccessList blockAccessList = processedBlock.BlockAccessList!.Value; Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(10)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); diff --git a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs index 414f7cde0c4..b25c3b54751 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs @@ -156,6 +156,9 @@ public static string ExceededBlockSizeLimit(int limit) => public const string InvalidBlockLevelAccessList = $"InvalidBlockLevelAccessList: Unable to decode."; + public const string BlockLevelAccessListNotEnabled = + "BlockLevelAccessListNotEnabled: Block body cannot have block level access list."; + public static string InvalidBlockLevelAccessListRoot(Hash256 expected, Hash256 actual) => $"InvalidBlockLevelAccessListRoot: Expected {expected}, got {actual}"; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index f181d77adb9..82eff985a3e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -15,6 +15,7 @@ using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Core.Threading; using Nethermind.Crypto; @@ -177,13 +178,18 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - header.Hash = header.CalculateHash(); - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); } + header.Hash = header.CalculateHash(); + return receipts; } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index cf7cf13c087..4235cce867f 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -135,23 +135,23 @@ public bool ValidateSuggestedBlock(Block block, BlockHeader? parent, out string? if (spec.BlockLevelAccessListsEnabled) { - if (block.BlockAccessList is null || block.BlockAccessList.Length == 0) + if (block.BlockAccessList is null) { if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list was missing or empty"); errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; return false; } - try - { - block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); - } - catch (RlpException e) - { - if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list could not be decoded: {e}"); - errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; - return false; - } + // try + // { + // block.DecodedBlockAccessList = Rlp.Decode(block.BlockAccessList); + // } + // catch (RlpException e) + // { + // if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list could not be decoded: {e}"); + // errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; + // return false; + // } } return true; @@ -236,6 +236,18 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B error ??= BlockErrorMessages.InvalidRequestsHash(suggestedBlock.Header.RequestsHash, processedBlock.Header.RequestsHash); } + if (processedBlock.Header.WithdrawalsRoot != suggestedBlock.Header.WithdrawalsRoot) + { + if (_logger.IsWarn) _logger.Warn($"- withdrawals root : expected {suggestedBlock.Header.WithdrawalsRoot}, got {processedBlock.Header.WithdrawalsRoot}"); + error ??= BlockErrorMessages.InvalidWithdrawalsRoot(suggestedBlock.Header.WithdrawalsRoot, processedBlock.Header.WithdrawalsRoot); + } + + if (processedBlock.Header.BlockAccessListHash != suggestedBlock.Header.BlockAccessListHash) + { + if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); + error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); + } + for (int i = 0; i < processedBlock.Transactions.Length; i++) { if (receipts[i].Error is not null && receipts[i].GasUsed == 0 && receipts[i].Error == "invalid") @@ -413,7 +425,7 @@ public virtual bool ValidateBlockLevelAccessList(Block block, IReleaseSpec spec, if (!spec.BlockLevelAccessListsEnabled && block.BlockAccessList is not null) { - error = BlockErrorMessages.WithdrawalsNotEnabled; + error = BlockErrorMessages.BlockLevelAccessListNotEnabled; if (_logger.IsWarn) _logger.Warn($"Block level access list must be null in block {block.Hash} when EIP-7928 not activated."); @@ -422,7 +434,7 @@ public virtual bool ValidateBlockLevelAccessList(Block block, IReleaseSpec spec, if (block.BlockAccessList is not null) { - if (!ValidateBlockLevelAccessListHashMatches(block.Header, block.Body, out Hash256 blockLevelAccessListRoot)) + if (!ValidateBlockLevelAccessListHashMatches(block, out Hash256 blockLevelAccessListRoot)) { error = BlockErrorMessages.InvalidBlockLevelAccessListRoot(block.Header.BlockAccessListHash, blockLevelAccessListRoot); if (_logger.IsWarn) _logger.Warn($"Block level access list root hash mismatch in block {block.ToString(Block.Format.FullHashAndNumber)}: expected {block.Header.BlockAccessListHash}, got {blockLevelAccessListRoot}"); @@ -463,15 +475,17 @@ public static bool ValidateWithdrawalsHashMatches(BlockHeader header, BlockBody return (withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash) == header.WithdrawalsRoot; } - public static bool ValidateBlockLevelAccessListHashMatches(BlockHeader header, BlockBody body, out Hash256? blockLevelAccessListRoot) + public static bool ValidateBlockLevelAccessListHashMatches(Block block, out Hash256? blockLevelAccessListRoot) { + BlockBody body = block.Body; + BlockHeader header = block.Header; if (body.BlockAccessList is null) { blockLevelAccessListRoot = null; return header.BlockAccessListHash is null; } - blockLevelAccessListRoot = new(ValueKeccak.Compute(body.BlockAccessList).Bytes); + blockLevelAccessListRoot = new(ValueKeccak.Compute(block.EncodedBlockAccessList!).Bytes); return blockLevelAccessListRoot == header.BlockAccessListHash; } diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 48c9af9dafc..7637fec19ba 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -29,7 +29,7 @@ public Block(BlockHeader header, IEnumerable transactions, IEnumerable uncles, IEnumerable? withdrawals = null, - byte[]? blockAccessList = null) + BlockAccessList? blockAccessList = null) { Header = header ?? throw new ArgumentNullException(nameof(header)); Body = new(transactions.ToArray(), uncles.ToArray(), withdrawals?.ToArray(), blockAccessList); @@ -118,7 +118,8 @@ public Transaction[] Transactions public Hash256? ParentBeaconBlockRoot => Header.ParentBeaconBlockRoot; // do not add setter here public Hash256? RequestsHash => Header.RequestsHash; // do not add setter here - public byte[]? BlockAccessList => Body.BlockAccessList; // do not add setter here + public Hash256? BlockAccessListHash => Header.BlockAccessListHash; // do not add setter here + public BlockAccessList? BlockAccessList => Body.BlockAccessList; // do not add setter here [JsonIgnore] public byte[][]? ExecutionRequests { get; set; } @@ -130,8 +131,11 @@ public Transaction[] Transactions public int? EncodedSize { get; set; } + // [JsonIgnore] + // public BlockAccessList? DecodedBlockAccessList { get; set; } + [JsonIgnore] - public BlockAccessList? DecodedBlockAccessList { get; set; } + public byte[]? EncodedBlockAccessList { get; set; } public override string ToString() => ToString(Format.Short); diff --git a/src/Nethermind/Nethermind.Core/BlockBody.cs b/src/Nethermind/Nethermind.Core/BlockBody.cs index 0485b8f51dd..6ea1d038ef6 100644 --- a/src/Nethermind/Nethermind.Core/BlockBody.cs +++ b/src/Nethermind/Nethermind.Core/BlockBody.cs @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using Nethermind.Core.BlockAccessLists; + namespace Nethermind.Core { - public class BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null, byte[]? blockLevelAccessList = null) + public class BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null, BlockAccessList? blockLevelAccessList = null) { public BlockBody() : this(null, null, null) { } @@ -20,7 +22,7 @@ public BlockBody() : this(null, null, null) { } public BlockHeader[] Uncles { get; } = uncles ?? []; public Withdrawal[]? Withdrawals { get; } = withdrawals; - public byte[]? BlockAccessList { get; internal set; } = blockLevelAccessList ?? []; + public BlockAccessList? BlockAccessList { get; internal set; } = blockLevelAccessList; public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index b5e449ce311..9425168c84f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -12,6 +12,7 @@ using Nethermind.State.Proofs; using System.Text.Json.Serialization; using Nethermind.Core.ExecutionRequest; +using Nethermind.Core.BlockAccessLists; namespace Nethermind.Merge.Plugin.Data; @@ -126,7 +127,7 @@ public byte[][] Transactions Timestamp = block.Timestamp, BaseFeePerGas = block.BaseFeePerGas, Withdrawals = block.Withdrawals, - BlockAccessList = block.BlockAccessList!, + BlockAccessList = block.EncodedBlockAccessList!, }; executionPayload.SetTransactions(block.Transactions); return executionPayload; @@ -172,7 +173,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) BlockAccessListHash = BlockAccessList.Length == 0 ? null : new(ValueKeccak.Compute(BlockAccessList).Bytes) }; - return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, BlockAccessList)); + return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, Rlp.Decode(BlockAccessList))); } protected virtual Hash256? BuildWithdrawalsRoot() diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs index f7fc6028159..d3330627027 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs @@ -3,6 +3,8 @@ using System; using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; +using Nethermind.Serialization.Rlp.Eip7928; namespace Nethermind.Serialization.Rlp; @@ -11,6 +13,7 @@ public class BlockBodyDecoder : IRlpValueDecoder, IRlpStreamDecoder _instance ??= new BlockBodyDecoder(); @@ -27,17 +30,19 @@ public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) public int GetBodyLength(BlockBody b) { - (int txs, int uncles, int? withdrawals) = GetBodyComponentLength(b); + (int txs, int uncles, int? withdrawals, int? blockAccessList) = GetBodyComponentLength(b); return Rlp.LengthOfSequence(txs) + Rlp.LengthOfSequence(uncles) + - (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0); + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + + (blockAccessList is not null ? Rlp.LengthOfSequence(blockAccessList.Value) : 0); } - public (int Txs, int Uncles, int? Withdrawals) GetBodyComponentLength(BlockBody b) => + public (int Txs, int Uncles, int? Withdrawals, int? BlockAccessList) GetBodyComponentLength(BlockBody b) => ( GetTxLength(b.Transactions), GetUnclesLength(b.Uncles), - b.Withdrawals is not null ? GetWithdrawalsLength(b.Withdrawals) : null + b.Withdrawals is not null ? GetWithdrawalsLength(b.Withdrawals) : null, + b.BlockAccessList is not null ? _blockAccessListDecoder.GetLength(b.BlockAccessList.Value, RlpBehaviors.None) : null ); private int GetTxLength(Transaction[] transactions) @@ -93,19 +98,25 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition) { - // quite significant allocations (>0.5%) here based on a sample 3M blocks sync // (just on these delegates) Transaction[] transactions = ctx.DecodeArray(_txDecoder); BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); Withdrawal[]? withdrawals = null; + BlockAccessList? blockAccessList = null; - if (ctx.PeekNumberOfItemsRemaining(lastPosition, 1) > 0) + int remaining = ctx.PeekNumberOfItemsRemaining(lastPosition, 1); + if (remaining > 0) { withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); } - return new BlockBody(transactions, uncles, withdrawals); + if (remaining > 1) + { + blockAccessList = _blockAccessListDecoder.Decode(ref ctx, RlpBehaviors.None); + } + + return new BlockBody(transactions, uncles, withdrawals, blockAccessList); } public BlockBody Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) @@ -141,5 +152,10 @@ public void Encode(RlpStream stream, BlockBody body, RlpBehaviors rlpBehaviors = stream.Encode(withdrawal); } } + + if (body.BlockAccessList is not null) + { + stream.Encode(body.BlockAccessList.Value); + } } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 50704bb6b42..87fff22af6f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -34,18 +34,19 @@ public BlockDecoder() : this(new HeaderDecoder()) { } return decoded; } - private (int Total, int Txs, int Uncles, int? Withdrawals) GetContentLength(Block item, RlpBehaviors rlpBehaviors) + private (int Total, int Txs, int Uncles, int? Withdrawals, int? BlockAccessList) GetContentLength(Block item, RlpBehaviors rlpBehaviors) { int headerLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); - (int txs, int uncles, int? withdrawals) = _blockBodyDecoder.GetBodyComponentLength(item.Body); + (int txs, int uncles, int? withdrawals, int? blockAccessList) = _blockBodyDecoder.GetBodyComponentLength(item.Body); int contentLength = headerLength + Rlp.LengthOfSequence(txs) + Rlp.LengthOfSequence(uncles) + - (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0); - return (contentLength, txs, uncles, withdrawals); + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + + (blockAccessList is not null ? Rlp.LengthOfSequence(blockAccessList.Value) : 0); + return (contentLength, txs, uncles, withdrawals, blockAccessList); } public int GetLength(Block? item, RlpBehaviors rlpBehaviors) @@ -74,7 +75,8 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) Block block = new(header, body) { - EncodedSize = Rlp.LengthOfSequence(sequenceLength) + EncodedSize = Rlp.LengthOfSequence(sequenceLength), + EncodedBlockAccessList = body.BlockAccessList is null ? null : Rlp.Encode(body.BlockAccessList.Value).Bytes // todo: possible without reencoding? }; return block; @@ -100,7 +102,7 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl return; } - (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength) = GetContentLength(item, rlpBehaviors); + (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength, int? _) = GetContentLength(item, rlpBehaviors); stream.StartSequence(contentLength); stream.Encode(item.Header); stream.StartSequence(txsLength); @@ -124,6 +126,11 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl stream.Encode(item.Withdrawals[i]); } } + + if (item.BlockAccessList is not null) + { + stream.Encode(item.BlockAccessList.Value); + } } public static ReceiptRecoveryBlock? DecodeToReceiptRecoveryBlock(MemoryManager? memoryManager, Memory memory, RlpBehaviors rlpBehaviors) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs index 3f081afa742..d10328c52ec 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs @@ -75,6 +75,7 @@ public class HeaderDecoder : IHeaderDecoder if (decoderContext.Position != headerCheck) blockHeader.ExcessBlobGas = decoderContext.DecodeULong(); if (decoderContext.Position != headerCheck) blockHeader.ParentBeaconBlockRoot = decoderContext.DecodeKeccak(); if (decoderContext.Position != headerCheck) blockHeader.RequestsHash = decoderContext.DecodeKeccak(); + if (decoderContext.Position != headerCheck) blockHeader.BlockAccessListHash = decoderContext.DecodeKeccak(); if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) { @@ -145,6 +146,7 @@ public class HeaderDecoder : IHeaderDecoder if (rlpStream.Position != headerCheck) blockHeader.ExcessBlobGas = rlpStream.DecodeULong(); if (rlpStream.Position != headerCheck) blockHeader.ParentBeaconBlockRoot = rlpStream.DecodeKeccak(); if (rlpStream.Position != headerCheck) blockHeader.RequestsHash = rlpStream.DecodeKeccak(); + if (rlpStream.Position != headerCheck) blockHeader.BlockAccessListHash = rlpStream.DecodeKeccak(); if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) { @@ -193,15 +195,16 @@ public void Encode(RlpStream rlpStream, BlockHeader? header, RlpBehaviors rlpBeh } } - Span requiredItems = stackalloc bool[6]; + Span requiredItems = stackalloc bool[7]; requiredItems[0] = !header.BaseFeePerGas.IsZero; - requiredItems[1] = (header.WithdrawalsRoot is not null); - requiredItems[2] = (header.BlobGasUsed is not null); - requiredItems[3] = (header.BlobGasUsed is not null || header.ExcessBlobGas is not null); - requiredItems[4] = (header.ParentBeaconBlockRoot is not null); - requiredItems[5] = (header.RequestsHash is not null); - - for (int i = 4; i >= 0; i--) + requiredItems[1] = header.WithdrawalsRoot is not null; + requiredItems[2] = header.BlobGasUsed is not null; + requiredItems[3] = header.BlobGasUsed is not null || header.ExcessBlobGas is not null; + requiredItems[4] = header.ParentBeaconBlockRoot is not null; + requiredItems[5] = header.RequestsHash is not null; + requiredItems[6] = header.BlockAccessListHash is not null; + + for (int i = 5; i >= 0; i--) { requiredItems[i] |= requiredItems[i + 1]; } @@ -212,6 +215,7 @@ public void Encode(RlpStream rlpStream, BlockHeader? header, RlpBehaviors rlpBeh if (requiredItems[3]) rlpStream.Encode(header.ExcessBlobGas.GetValueOrDefault()); if (requiredItems[4]) rlpStream.Encode(header.ParentBeaconBlockRoot); if (requiredItems[5]) rlpStream.Encode(header.RequestsHash); + if (requiredItems[6]) rlpStream.Encode(header.BlockAccessListHash); } public Rlp Encode(BlockHeader? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) @@ -266,15 +270,16 @@ private static int GetContentLength(BlockHeader? item, RlpBehaviors rlpBehaviors } - Span requiredItems = stackalloc bool[6]; + Span requiredItems = stackalloc bool[7]; requiredItems[0] = !item.BaseFeePerGas.IsZero; - requiredItems[1] = (item.WithdrawalsRoot is not null); - requiredItems[2] = (item.BlobGasUsed is not null); - requiredItems[3] = (item.BlobGasUsed is not null || item.ExcessBlobGas is not null); - requiredItems[4] = (item.ParentBeaconBlockRoot is not null); - requiredItems[5] = (item.RequestsHash is not null); - - for (int i = 4; i >= 0; i--) + requiredItems[1] = item.WithdrawalsRoot is not null; + requiredItems[2] = item.BlobGasUsed is not null; + requiredItems[3] = item.BlobGasUsed is not null || item.ExcessBlobGas is not null; + requiredItems[4] = item.ParentBeaconBlockRoot is not null; + requiredItems[5] = item.RequestsHash is not null; + requiredItems[6] = item.BlockAccessListHash is not null; + + for (int i = 5; i >= 0; i--) { requiredItems[i] |= requiredItems[i + 1]; } @@ -285,12 +290,12 @@ private static int GetContentLength(BlockHeader? item, RlpBehaviors rlpBehaviors if (requiredItems[3]) contentLength += Rlp.LengthOf(item.ExcessBlobGas.GetValueOrDefault()); if (requiredItems[4]) contentLength += Rlp.LengthOf(item.ParentBeaconBlockRoot); if (requiredItems[5]) contentLength += Rlp.LengthOf(item.RequestsHash); + if (requiredItems[6]) contentLength += Rlp.LengthOf(item.BlockAccessListHash); + return contentLength; } public int GetLength(BlockHeader? item, RlpBehaviors rlpBehaviors) - { - return Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); - } + => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs index 28ea90b7b2a..bb7f833285b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs @@ -11,11 +11,13 @@ using System.Runtime.InteropServices; using System.Text; using Nethermind.Core; +using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Buffers; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Int256; +using Nethermind.Serialization.Rlp.Eip7928; namespace Nethermind.Serialization.Rlp { @@ -28,6 +30,7 @@ public class RlpStream private static readonly TxDecoder _txDecoder = TxDecoder.Instance; private static readonly ReceiptMessageDecoder _receiptDecoder = new(); private static readonly WithdrawalDecoder _withdrawalDecoder = new(); + private static readonly BlockAccessListDecoder _blockAccessListDecoder = BlockAccessListDecoder.Instance; private static readonly LogEntryDecoder _logEntryDecoder = LogEntryDecoder.Instance; private readonly CappedArray _data; @@ -68,37 +71,32 @@ public void EncodeArray(T?[]? items, RlpBehaviors rlpBehaviors = RlpBehaviors StartSequence(contentLength); - foreach (var item in items) + foreach (T item in items) { decoder.Encode(this, item, rlpBehaviors); } } + public void Encode(Block value) - { - _blockDecoder.Encode(this, value); - } + => _blockDecoder.Encode(this, value); public void Encode(BlockHeader value) - { - _headerDecoder.Encode(this, value); - } + => _headerDecoder.Encode(this, value); public void Encode(Transaction value, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - _txDecoder.Encode(this, value, rlpBehaviors); - } + => _txDecoder.Encode(this, value, rlpBehaviors); - public void Encode(Withdrawal value) => _withdrawalDecoder.Encode(this, value); + public void Encode(Withdrawal value) + => _withdrawalDecoder.Encode(this, value); public void Encode(LogEntry value) - { - _logEntryDecoder.Encode(this, value); - } + => _logEntryDecoder.Encode(this, value); public void Encode(BlockInfo value) - { - _blockInfoDecoder.Encode(this, value); - } + => _blockInfoDecoder.Encode(this, value); + + public void Encode(BlockAccessList value) + => _blockAccessListDecoder.Encode(this, value); public void StartByteArray(int contentLength, bool firstByteLessThan128) { @@ -207,9 +205,7 @@ public virtual int Position public virtual bool HasBeenRead => Position >= Data!.Length; public bool IsSequenceNext() - { - return PeekByte() >= 192; - } + => PeekByte() >= 192; public void Encode(Hash256? keccak) { diff --git a/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs b/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs index 0856b9002fc..b9b3013d39f 100644 --- a/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs +++ b/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs @@ -29,9 +29,18 @@ public BlockchainTestsRunner(ITestSourceLoader testsSource, string? filter, ulon public async Task> RunTestsAsync() { List testResults = new(); - IEnumerable tests = _testsSource.LoadTests(); - foreach (BlockchainTest test in tests) + IEnumerable tests = _testsSource.LoadTests(); + foreach (EthereumTest loadedTest in tests) { + if (loadedTest as FailedToLoadTest is not null) + { + WriteRed(loadedTest.LoadFailure); + testResults.Add(new EthereumTestResult(loadedTest.Name, loadedTest.LoadFailure)); + continue; + } + + BlockchainTest test = loadedTest as BlockchainTest; + if (_filter is not null && !Regex.Match(test.Name, $"^({_filter})").Success) continue; Setup(); From 894f5c84cd00dec0db84fac7cde6062a3e5a836c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:48:18 +0100 Subject: [PATCH 095/215] string encoding --- .../BlockAccessLists/AccountChanges.cs | 15 +++++++++++++++ .../BlockAccessLists/BalanceChange.cs | 3 +++ .../BlockAccessLists/BlockAccessList.cs | 3 +++ .../BlockAccessLists/CodeChange.cs | 4 ++++ .../BlockAccessLists/NonceChange.cs | 3 +++ .../BlockAccessLists/SlotChanges.cs | 9 ++++++++- .../BlockAccessLists/StorageChange.cs | 3 +++ 7 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index 6099077a8d7..f2dd0001598 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -57,4 +57,19 @@ public override readonly int GetHashCode() => public static bool operator !=(AccountChanges left, AccountChanges right) => !(left == right); + + public override readonly string? ToString() + { + string storageChangesList = string.Join(",\n\t\t\t", [.. StorageChanges.Values.Select(s => s.ToString())]); + string storageChanges = StorageChanges.Count == 0 ? "[] #storage_changes" : $"[ #storage_changes\n\t\t\t{storageChangesList}\n\t\t]"; + string storageReadsList = string.Join(",\n\t\t\t", [.. StorageReads.Select(s => s.ToString())]); + string storageReads = StorageReads.Count == 0 ? "[] #storage_reads" : $"[ #storage_reads\n\t\t\t{storageReadsList}\n\t\t]"; + string balanceChangesList = string.Join(",\n\t\t\t", [.. BalanceChanges.Values.Select(s => s.ToString())]); + string balanceChanges = BalanceChanges.Count == 0 ? "[] #balance_changes" : $"[ #balance_changes\n\t\t\t{balanceChangesList}\n\t\t]"; + string nonceChangesList = string.Join(",\n\t\t\t", [.. NonceChanges.Values.Select(s => s.ToString())]); + string nonceChanges = NonceChanges.Count == 0 ? "[] #nonce_changes" : $"[ #nonce_changes\n\t\t\t{nonceChangesList}\n\t\t]"; + string codeChangesList = string.Join(",\n\t\t\t", [.. CodeChanges.Values.Select(s => s.ToString())]); + string codeChanges = CodeChanges.Count == 0 ? "[] #code_changes" : $"[ #code_changes\n\t\t\t{codeChangesList}\n\t\t]"; + return $"\t[\n\t\t{Address},\n\t\t{storageChanges},\n\t\t{storageReads},\n\t\t{balanceChanges},\n\t\t{nonceChanges},\n\t\t{codeChanges}\n\t]"; + } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index 15b822be3f8..67408d2a81d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -26,4 +26,7 @@ public override readonly int GetHashCode() => public static bool operator !=(BalanceChange left, BalanceChange right) => !(left == right); + + public override readonly string? ToString() + => $"{BlockAccessIndex}, {PostBalance}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 5fd88c3bbd0..9040fbf6551 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -326,6 +326,9 @@ public readonly void Restore(int snapshot) } } + public override readonly string? ToString() + => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; + private enum ChangeType { BalanceChange = 0, diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index 36b4afdb5c1..32767d88ca6 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -4,6 +4,7 @@ using System; using System.Linq; +using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; @@ -36,4 +37,7 @@ private static bool CompareByteArrays(byte[]? left, byte[]? right) => public static bool operator !=(CodeChange left, CodeChange right) => !(left == right); + + public override readonly string? ToString() + => $"{BlockAccessIndex}, 0x{Bytes.ToHexString(NewCode)}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs index 3907f1c218f..d43d6c8c8c4 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -25,4 +25,7 @@ public override readonly int GetHashCode() => public static bool operator !=(NonceChange left, NonceChange right) => !(left == right); + + public override readonly string? ToString() + => $"{BlockAccessIndex}, {NewNonce}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs index c647a2f1ef2..9f1368097ef 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; @@ -41,4 +42,10 @@ private static bool CompareByteArrays(byte[]? left, byte[]? right) => public static bool operator !=(SlotChanges left, SlotChanges right) => !(left == right); -} + + public override readonly string? ToString() + { + string changes = string.Join(", ", [.. Changes.Select(s => s.ToString())]); + return $"[0x{Bytes.ToHexString(Slot)}, [{changes}]]"; + } +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index 021a20de7cb..d8f06595771 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -27,4 +27,7 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageChange left, StorageChange right) => !(left == right); + + public override readonly string? ToString() + => $"{BlockAccessIndex}, {NewValue}"; } From bdd2448781180f8804c3c95a5301a8e869f6a409 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:07:11 +0100 Subject: [PATCH 096/215] fix decoding --- .../BlockAccessListTests.cs | 11 ++++++++++- .../Nethermind.Core/BlockAccessLists/StorageRead.cs | 3 +++ .../BlockBodyDecoder.cs | 6 +++--- .../Eip7928/AccountChangesDecoder.cs | 13 ++++--------- .../Eip7928/BlockAccessListDecoder.cs | 8 -------- .../Eip7928/StorageReadDecoder.cs | 8 -------- src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs | 2 +- 7 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index de995f9e9f1..6e75343e737 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -12,6 +12,7 @@ using Nethermind.Consensus.Processing; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -42,7 +43,15 @@ public class BlockAccessListTests() private static readonly UInt256 _accountBalance = 10.Ether(); [Test] - public void Can_encode_and_decode() + public void Can_decode_block_with_BAL() + { + const string rlp = "0xf90580f9027aa0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0e64c7e084d1335d4ed6923e124d0bb8d962ae65323cde22bb6af286eaa1e5f96a0d172c17fe6451f634f21773c7b0a4a8bcbdbf2c27d41e75795229bb1cda41a52a0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800184055d4a808252080c80a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855a0c616d499eecff62312242ac084a62f9c04aa4cc65cab20ef6b43f46dcf779322f865f86380843b9aca008255f094d9c0e57d447779673b236c7423aeab84e931f3ba648026a0339110b435e122cbcca84763561c0b6d2cf0b0aa64597fe2e923686809de9d6fa05e7a19b0de89e3317da6e4585bff1f5b03a2927feb68c8307fb95405f812d695c0c0f90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0"; + Block block = Rlp.Decode(Bytes.FromHexString(rlp).AsRlpStream()); + Assert.That(block, Is.Not.Null); + } + + [Test] + public void Can_encode_then_decode() { StorageChange storageChange = new() { diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs index aee73820d16..9183c2aa12b 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -28,4 +28,7 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageRead left, StorageRead right) => !(left == right); + + public override readonly string? ToString() + => $"0x{Bytes.ToHexString(Key.Unwrap())}"; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs index d3330627027..b6dfa96aa3c 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockBodyDecoder.cs @@ -96,7 +96,7 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) return DecodeUnwrapped(ref ctx, startingPosition + sequenceLength); } - public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition) + public BlockBody? DecodeUnwrapped(ref Rlp.ValueDecoderContext ctx, int lastPosition, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { // quite significant allocations (>0.5%) here based on a sample 3M blocks sync // (just on these delegates) @@ -105,7 +105,7 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) Withdrawal[]? withdrawals = null; BlockAccessList? blockAccessList = null; - int remaining = ctx.PeekNumberOfItemsRemaining(lastPosition, 1); + int remaining = ctx.PeekNumberOfItemsRemaining(lastPosition, 2); if (remaining > 0) { withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); @@ -113,7 +113,7 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) if (remaining > 1) { - blockAccessList = _blockAccessListDecoder.Decode(ref ctx, RlpBehaviors.None); + blockAccessList = _blockAccessListDecoder.Decode(ref ctx, rlpBehaviors); } return new BlockBody(transactions, uncles, withdrawals, blockAccessList); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 39684917f1e..554441864ad 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -23,11 +23,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe Address address = ctx.DecodeAddress(); SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); - byte[] lastSlot = Bytes32.Zero.Unwrap(); + byte[]? lastSlot = null; SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => { byte[] slot = s.Slot; - if (Bytes.BytesComparer.Compare(slot, lastSlot) <= 0) + if (lastSlot is not null && Bytes.BytesComparer.Compare(slot, lastSlot) <= 0) { throw new RlpException("Storage changes were in incorrect order."); } @@ -37,10 +37,10 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); SortedSet storareReadsList = []; - StorageRead lastRead = new(Bytes32.Zero); + StorageRead? lastRead = null; foreach (StorageRead storageRead in storageReads) { - if (storageRead.CompareTo(lastRead) <= 0) + if (lastRead is not null && storageRead.CompareTo(lastRead.Value) <= 0) { throw new RlpException("Storage reads were in incorrect order."); } @@ -93,11 +93,6 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe return index; }, s => s)); - if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) - { - ctx.Check(check); - } - return new() { Address = address, diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 0495fd6eda8..c73a9f94872 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -19,9 +19,6 @@ public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - int length = ctx.ReadSequenceLength(); - int check = length + ctx.Position; - AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); if (accountChanges.Length > Eip7928Constants.MaxAccounts) { @@ -46,11 +43,6 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB throw new RlpException("Accounts were in incorrect order."); } - if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) - { - ctx.Check(check); - } - return blockAccessList; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index 0d2864aef5f..2e3369bba87 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -16,9 +16,6 @@ public int GetLength(StorageRead item, RlpBehaviors rlpBehaviors) public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - int length = ctx.ReadSequenceLength(); - int check = length + ctx.Position; - byte[] key = ctx.DecodeByteArray(); if (key.Length != 32) { @@ -30,11 +27,6 @@ public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav Key = new(key), }; - if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) - { - ctx.Check(check); - } - return storageRead; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index 905b27cfd42..20c60d48ac4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -1078,7 +1078,7 @@ public void DecodeZeroPrefixedKeccakStructRef(out Hash256StructRef keccak, Span< static void ThrowInvalidPrefix(ref ValueDecoderContext ctx, int prefix) { - throw new RlpException($"Unexpected prefix of {prefix} when decoding {nameof(Hash256)} at position {ctx.Position} in the message of length {ctx.Data.Length} starting with {ctx.Data[..Math.Min(DebugMessageContentLength, ctx.Data.Length)].ToHexString()}"); + throw new RlpException($"Unexpected prefix of {prefix} when decoding {nameof(Address)} at position {ctx.Position} in the message of length {ctx.Data.Length} starting with {ctx.Data[..Math.Min(DebugMessageContentLength, ctx.Data.Length)].ToHexString()}"); } } From ef6f37ba9e4d19ee12301cacddaac4de170709b4 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:41:55 +0100 Subject: [PATCH 097/215] improve encoding test --- .../BlockAccessListTests.cs | 86 +++++++++++++++++-- .../BlockAccessLists/AccountChanges.cs | 10 +++ 2 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 6e75343e737..7d2a727ac66 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -12,7 +12,6 @@ using Nethermind.Consensus.Processing; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -43,11 +42,77 @@ public class BlockAccessListTests() private static readonly UInt256 _accountBalance = 10.Ether(); [Test] - public void Can_decode_block_with_BAL() + public void Can_decode_then_encode() { - const string rlp = "0xf90580f9027aa0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0e64c7e084d1335d4ed6923e124d0bb8d962ae65323cde22bb6af286eaa1e5f96a0d172c17fe6451f634f21773c7b0a4a8bcbdbf2c27d41e75795229bb1cda41a52a0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800184055d4a808252080c80a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855a0c616d499eecff62312242ac084a62f9c04aa4cc65cab20ef6b43f46dcf779322f865f86380843b9aca008255f094d9c0e57d447779673b236c7423aeab84e931f3ba648026a0339110b435e122cbcca84763561c0b6d2cf0b0aa64597fe2e923686809de9d6fa05e7a19b0de89e3317da6e4585bff1f5b03a2927feb68c8307fb95405f812d695c0c0f90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0"; - Block block = Rlp.Decode(Bytes.FromHexString(rlp).AsRlpStream()); - Assert.That(block, Is.Not.Null); + const string rlp = "0xf90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0"; + BlockAccessList bal = Rlp.Decode(Bytes.FromHexString(rlp).AsRlpStream()); + + byte[] slot0 = ToStorageSlot(0); + byte[] slot1 = ToStorageSlot(1); + byte[] slot2 = ToStorageSlot(2); + byte[] slot3 = ToStorageSlot(3); + byte[] eip4788Slot1 = ToStorageSlot(0xc); + StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x000000000000000000000000000000000000000000000000000000000000000c"))); + SortedDictionary expectedAccountChanges = new() + { + {Eip7002Constants.WithdrawalRequestPredeployAddress, new() + { + Address = Eip7002Constants.WithdrawalRequestPredeployAddress, + StorageReads = [ + ToStorageRead(slot0), + ToStorageRead(slot1), + ToStorageRead(slot2), + ToStorageRead(slot3), + ], + }}, + {Eip7251Constants.ConsolidationRequestPredeployAddress, new() + { + Address = Eip7251Constants.ConsolidationRequestPredeployAddress, + StorageReads = [ + ToStorageRead(slot0), + ToStorageRead(slot1), + ToStorageRead(slot2), + ToStorageRead(slot3), + ], + }}, + {Eip2935Constants.BlockHashHistoryAddress, new() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, + }}, + {Eip4788Constants.BeaconRootsAddress, new() + { + Address = Eip4788Constants.BeaconRootsAddress, + StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, + StorageReads = [ + ToStorageRead([0x20, 0x0b]) + ], + }}, + {new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), new() + { + Address = new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + BalanceChanges = new SortedList { { 1, new(1, 0x1319718811c8) } }, + }}, + {new("0xaccc7d92b051544a255b8a899071040739bada75"), new() + { + Address = new("0xaccc7d92b051544a255b8a899071040739bada75"), + NonceChanges = new SortedList { { 1, new(1, 1) } }, + BalanceChanges = new SortedList { { 1, new(1, new(Bytes.FromHexString("0x3635c99aac6d15af9c"))) } }, + }}, + {new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), new() + { + Address = new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), + BalanceChanges = new SortedList { { 1, new(1, 0x64) } }, + }}, + }; + BlockAccessList expected = new(expectedAccountChanges); + // Console.WriteLine(bal); + // Console.WriteLine(expected); + Assert.That(bal, Is.EqualTo(expected)); + + byte[] hash = [.. ValueKeccak.Compute(Rlp.Encode(bal).Bytes).Bytes]; + Assert.That(hash, Is.EqualTo(Bytes.FromHexString("0xc616d499eecff62312242ac084a62f9c04aa4cc65cab20ef6b43f46dcf779322"))); } [Test] @@ -429,9 +494,16 @@ private static void InitWorldState(IWorldState worldState) // Hash256 stateRoot = worldState.StateRoot; } + // should hash? private static byte[] ToStorageSlot(ulong x) - => ValueKeccak.Compute(new BigInteger(x).ToBytes32(true)).ToByteArray(); + => new BigInteger(x).ToBytes32(true); + // => ValueKeccak.Compute(new BigInteger(x).ToBytes32(true)).ToByteArray(); private static StorageRead ToStorageRead(byte[] x) - => new(Bytes32.Wrap(x)); + { + Span newValue = new byte[32]; + newValue.Clear(); + x.CopyTo(newValue[(32 - x.Length)..]); + return new(Bytes32.Wrap([.. newValue])); + } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index f2dd0001598..edb2e54ba85 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -18,6 +18,16 @@ namespace Nethermind.Core.BlockAccessLists; public SortedList NonceChanges { get; init; } public SortedList CodeChanges { get; init; } + public AccountChanges() + { + Address = Address.Zero; + StorageChanges = new(Bytes.Comparer); + StorageReads = []; + BalanceChanges = []; + NonceChanges = []; + CodeChanges = []; + } + public AccountChanges(Address address) { Address = address; From dc8122cd5075e862c4d63988cdb0ee562147b794 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:18:45 +0100 Subject: [PATCH 098/215] fix encoding, add tests --- .../BlockAccessListTests.cs | 114 +++++++++++++++++- .../Eip7928/AccountChangesDecoder.cs | 9 ++ .../Eip7928/BalanceChangeDecoder.cs | 7 ++ .../Eip7928/BlockAccessListDecoder.cs | 14 +-- .../Eip7928/CodeChangeDecoder.cs | 7 ++ .../Eip7928/NonceChangeDecoder.cs | 7 ++ .../Eip7928/SlotChangesDecoder.cs | 9 ++ .../Eip7928/StorageChangeDecoder.cs | 7 ++ .../Eip7928/StorageReadDecoder.cs | 7 +- 9 files changed, 162 insertions(+), 19 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 7d2a727ac66..1c7b0754660 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -21,6 +21,7 @@ using Nethermind.Evm.State; using Nethermind.Int256; using Nethermind.Serialization.Rlp; +using Nethermind.Serialization.Rlp.Eip7928; using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; @@ -111,8 +112,117 @@ public void Can_decode_then_encode() // Console.WriteLine(expected); Assert.That(bal, Is.EqualTo(expected)); - byte[] hash = [.. ValueKeccak.Compute(Rlp.Encode(bal).Bytes).Bytes]; - Assert.That(hash, Is.EqualTo(Bytes.FromHexString("0xc616d499eecff62312242ac084a62f9c04aa4cc65cab20ef6b43f46dcf779322"))); + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(bal).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + + [Test] + public void Can_decode_then_encode_balance_change() + { + const string rlp = "0xc801861319718811c8"; + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + BalanceChange balanceChange = BalanceChangeDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + BalanceChange expected = new(1, 0x1319718811c8); + Assert.That(balanceChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(balanceChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + + [Test] + public void Can_decode_then_encode_nonce_change() + { + const string rlp = "0xc20101"; + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + NonceChange nonceChange = NonceChangeDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + NonceChange expected = new(1, 1); + Assert.That(nonceChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(nonceChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + + [Test] + public void Can_decode_then_encode_slot_change() + { + byte[] slot0 = ToStorageSlot(0); + StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + const string rlp = "0xf845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"; + + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + SlotChanges slotChange = SlotChangesDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + SlotChanges expected = new(slot0, [parentHashStorageChange]); + Assert.That(slotChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(slotChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + + [Test] + public void Can_decode_then_encode_storage_change() + { + const string rlp = "0xe280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"; + + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + StorageChange storageChange = StorageChangeDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + StorageChange expected = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + Assert.That(storageChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(storageChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + + // todo: code change test + // move to RLP tests? + + private static IEnumerable AccountChangesTestSource + { + get + { + yield return new TestCaseData( + "0xf89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0", + new AccountChanges() + { + Address = Eip7002Constants.WithdrawalRequestPredeployAddress, + StorageReads = [ + ToStorageRead(ToStorageSlot(0)), + ToStorageRead(ToStorageSlot(1)), + ToStorageRead(ToStorageSlot(2)), + ToStorageRead(ToStorageSlot(3)) + ], + }); + + yield return new TestCaseData( + "0xf862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0", + new AccountChanges() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = new(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")))]) } }, + }); + } + } + [TestCaseSource(nameof(AccountChangesTestSource))] + public void Can_decode_then_encode_account_change(string rlp, AccountChanges expected) + { + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + AccountChanges accountChange = AccountChangesDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + + Assert.That(accountChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(accountChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); } [Test] diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 554441864ad..ea2d5f1a124 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -17,9 +17,18 @@ public class AccountChangesDecoder : IRlpValueDecoder, IRlpStrea public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + + // Console.WriteLine("account change uncut:"); + // Console.WriteLine(Bytes.ToHexString(tmp)); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 1)]; + // Console.WriteLine("account change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + Address address = ctx.DecodeAddress(); SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index 791e70c2dd1..ba570dec95b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -3,6 +3,7 @@ using System; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -16,9 +17,15 @@ public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 1)]; + // Console.WriteLine("balance change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + BalanceChange balanceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index c73a9f94872..cac12e0dea7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -58,19 +58,9 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.StartSequence(GetContentLength(item, rlpBehaviors)); - stream.EncodeArray([.. item.GetAccountChanges()]); + stream.EncodeArray([.. item.GetAccountChanges()], rlpBehaviors); } private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) - { - int len = 0; - - foreach (AccountChanges accountChange in item.GetAccountChanges()) - { - len += AccountChangesDecoder.Instance.GetLength(accountChange, rlpBehaviors); - } - - return Rlp.LengthOfSequence(len); - } + => AccountChangesDecoder.Instance.GetContentLength([.. item.GetAccountChanges()], rlpBehaviors); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 4eedac27e1b..1dfad9d7532 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -4,6 +4,7 @@ using System; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -14,9 +15,15 @@ public class CodeChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 1)]; + // Console.WriteLine("code change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + ushort blockAccessIndex = ctx.DecodeUShort(); byte[] newCode = ctx.DecodeByteArray(); if (newCode.Length > Eip7928Constants.MaxCodeSize) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index 352bd7ccedd..468fd1488d2 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -3,6 +3,7 @@ using System; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -13,9 +14,15 @@ public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecod public NonceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 1)]; + // Console.WriteLine("nonce change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + NonceChange nonceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 11a270e9936..471578f153b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -15,9 +15,18 @@ public class SlotChangesDecoder : IRlpValueDecoder, IRlpStreamDecod public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + + // Console.WriteLine("slot change uncut:"); + // Console.WriteLine(Bytes.ToHexString(tmp)); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 2)]; + // Console.WriteLine("slot change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + byte[] slot = ctx.DecodeByteArray(); if (slot.Length != 32) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 18c894012da..8516d3f1415 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -3,6 +3,7 @@ using System; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -13,9 +14,15 @@ public class StorageChangeDecoder : IRlpValueDecoder, IRlpStreamD public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; + // tmp = tmp[..(length + 1)]; + // Console.WriteLine("storage change:" + length); + // Console.WriteLine(Bytes.ToHexString(tmp)); + ushort blockAccessIndex = ctx.DecodeUShort(); byte[] newValue = ctx.DecodeByteArray(); if (newValue.Length != 32) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index 2e3369bba87..6574f9c56e0 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -12,7 +12,7 @@ public class StorageReadDecoder : IRlpValueDecoder, IRlpStreamDecod public static StorageReadDecoder Instance => _instance ??= new(); public int GetLength(StorageRead item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + => GetContentLength(item, rlpBehaviors); public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { @@ -41,10 +41,7 @@ public StorageRead Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) } public void Encode(RlpStream stream, StorageRead item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - stream.StartSequence(GetContentLength(item, rlpBehaviors)); - stream.Encode(item.Key.Unwrap()); - } + => stream.Encode(item.Key.Unwrap()); public static int GetContentLength(StorageRead item, RlpBehaviors rlpBehaviors) => Rlp.LengthOf(item.Key.Unwrap()); From 50ab9c15391b1a3ee9af2a53c6d45a4dbb30ca9d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:26:55 +0100 Subject: [PATCH 099/215] refactor bal encoding test --- .../BlockAccessListTests.cs | 189 +++++++++--------- 1 file changed, 100 insertions(+), 89 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 1c7b0754660..98f709e1bf6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -42,72 +42,11 @@ public class BlockAccessListTests() private static readonly ISpecProvider _specProvider = new TestSpecProvider(_spec); private static readonly UInt256 _accountBalance = 10.Ether(); - [Test] - public void Can_decode_then_encode() + [TestCaseSource(nameof(BlockAccessListTestSource))] + public void Can_decode_then_encode(string rlp, BlockAccessList expected) { - const string rlp = "0xf90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0"; BlockAccessList bal = Rlp.Decode(Bytes.FromHexString(rlp).AsRlpStream()); - byte[] slot0 = ToStorageSlot(0); - byte[] slot1 = ToStorageSlot(1); - byte[] slot2 = ToStorageSlot(2); - byte[] slot3 = ToStorageSlot(3); - byte[] eip4788Slot1 = ToStorageSlot(0xc); - StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); - StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x000000000000000000000000000000000000000000000000000000000000000c"))); - SortedDictionary expectedAccountChanges = new() - { - {Eip7002Constants.WithdrawalRequestPredeployAddress, new() - { - Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageReads = [ - ToStorageRead(slot0), - ToStorageRead(slot1), - ToStorageRead(slot2), - ToStorageRead(slot3), - ], - }}, - {Eip7251Constants.ConsolidationRequestPredeployAddress, new() - { - Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageReads = [ - ToStorageRead(slot0), - ToStorageRead(slot1), - ToStorageRead(slot2), - ToStorageRead(slot3), - ], - }}, - {Eip2935Constants.BlockHashHistoryAddress, new() - { - Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, - }}, - {Eip4788Constants.BeaconRootsAddress, new() - { - Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, - StorageReads = [ - ToStorageRead([0x20, 0x0b]) - ], - }}, - {new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), new() - { - Address = new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - BalanceChanges = new SortedList { { 1, new(1, 0x1319718811c8) } }, - }}, - {new("0xaccc7d92b051544a255b8a899071040739bada75"), new() - { - Address = new("0xaccc7d92b051544a255b8a899071040739bada75"), - NonceChanges = new SortedList { { 1, new(1, 1) } }, - BalanceChanges = new SortedList { { 1, new(1, new(Bytes.FromHexString("0x3635c99aac6d15af9c"))) } }, - }}, - {new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), new() - { - Address = new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), - BalanceChanges = new SortedList { { 1, new(1, 0x64) } }, - }}, - }; - BlockAccessList expected = new(expectedAccountChanges); // Console.WriteLine(bal); // Console.WriteLine(expected); Assert.That(bal, Is.EqualTo(expected)); @@ -185,32 +124,6 @@ public void Can_decode_then_encode_storage_change() // todo: code change test // move to RLP tests? - private static IEnumerable AccountChangesTestSource - { - get - { - yield return new TestCaseData( - "0xf89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0", - new AccountChanges() - { - Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageReads = [ - ToStorageRead(ToStorageSlot(0)), - ToStorageRead(ToStorageSlot(1)), - ToStorageRead(ToStorageSlot(2)), - ToStorageRead(ToStorageSlot(3)) - ], - }); - - yield return new TestCaseData( - "0xf862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0", - new AccountChanges() - { - Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")))]) } }, - }); - } - } [TestCaseSource(nameof(AccountChangesTestSource))] public void Can_decode_then_encode_account_change(string rlp, AccountChanges expected) { @@ -616,4 +529,102 @@ private static StorageRead ToStorageRead(byte[] x) x.CopyTo(newValue[(32 - x.Length)..]); return new(Bytes32.Wrap([.. newValue])); } + + private static IEnumerable AccountChangesTestSource + { + get + { + yield return new TestCaseData( + "0xf89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0", + new AccountChanges() + { + Address = Eip7002Constants.WithdrawalRequestPredeployAddress, + StorageReads = [ + ToStorageRead(ToStorageSlot(0)), + ToStorageRead(ToStorageSlot(1)), + ToStorageRead(ToStorageSlot(2)), + ToStorageRead(ToStorageSlot(3)) + ], + }); + + yield return new TestCaseData( + "0xf862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0", + new AccountChanges() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = new(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")))]) } }, + }); + } + } + + private static IEnumerable BlockAccessListTestSource + { + get + { + byte[] slot0 = ToStorageSlot(0); + byte[] slot1 = ToStorageSlot(1); + byte[] slot2 = ToStorageSlot(2); + byte[] slot3 = ToStorageSlot(3); + byte[] eip4788Slot1 = ToStorageSlot(0xc); + StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x000000000000000000000000000000000000000000000000000000000000000c"))); + SortedDictionary expectedAccountChanges = new() + { + {Eip7002Constants.WithdrawalRequestPredeployAddress, new() + { + Address = Eip7002Constants.WithdrawalRequestPredeployAddress, + StorageReads = [ + ToStorageRead(slot0), + ToStorageRead(slot1), + ToStorageRead(slot2), + ToStorageRead(slot3), + ], + }}, + {Eip7251Constants.ConsolidationRequestPredeployAddress, new() + { + Address = Eip7251Constants.ConsolidationRequestPredeployAddress, + StorageReads = [ + ToStorageRead(slot0), + ToStorageRead(slot1), + ToStorageRead(slot2), + ToStorageRead(slot3), + ], + }}, + {Eip2935Constants.BlockHashHistoryAddress, new() + { + Address = Eip2935Constants.BlockHashHistoryAddress, + StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, + }}, + {Eip4788Constants.BeaconRootsAddress, new() + { + Address = Eip4788Constants.BeaconRootsAddress, + StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, + StorageReads = [ + ToStorageRead([0x20, 0x0b]) + ], + }}, + {new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), new() + { + Address = new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + BalanceChanges = new SortedList { { 1, new(1, 0x1319718811c8) } }, + }}, + {new("0xaccc7d92b051544a255b8a899071040739bada75"), new() + { + Address = new("0xaccc7d92b051544a255b8a899071040739bada75"), + NonceChanges = new SortedList { { 1, new(1, 1) } }, + BalanceChanges = new SortedList { { 1, new(1, new(Bytes.FromHexString("0x3635c99aac6d15af9c"))) } }, + }}, + {new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), new() + { + Address = new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), + BalanceChanges = new SortedList { { 1, new(1, 0x64) } }, + }}, + }; + BlockAccessList expected = new(expectedAccountChanges); + yield return new TestCaseData( + "0xf90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0", + expected); + } + } + } From d067106fc5f8baaa269ab3a6ad48883657a2d5e5 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:58:14 +0100 Subject: [PATCH 100/215] code changes test --- .../BlockAccessListTests.cs | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 98f709e1bf6..29e04adfcd0 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -42,6 +42,7 @@ public class BlockAccessListTests() private static readonly ISpecProvider _specProvider = new TestSpecProvider(_spec); private static readonly UInt256 _accountBalance = 10.Ether(); + // todo: move to RLP tests? [TestCaseSource(nameof(BlockAccessListTestSource))] public void Can_decode_then_encode(string rlp, BlockAccessList expected) { @@ -121,8 +122,22 @@ public void Can_decode_then_encode_storage_change() Assert.That(encoded, Is.EqualTo(rlp)); } - // todo: code change test - // move to RLP tests? + [Test] + public void Can_decode_then_encode_code_change() + { + const string rlp = "0xc20100"; + + Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); + CodeChange codeChange = CodeChangeDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); + CodeChange expected = new(1, [0x0]); + Assert.That(codeChange, Is.EqualTo(expected)); + + string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(codeChange).Bytes); + Console.WriteLine(encoded); + Console.WriteLine(rlp); + Assert.That(encoded, Is.EqualTo(rlp)); + } + [TestCaseSource(nameof(AccountChangesTestSource))] public void Can_decode_then_encode_account_change(string rlp, AccountChanges expected) @@ -545,7 +560,8 @@ private static IEnumerable AccountChangesTestSource ToStorageRead(ToStorageSlot(2)), ToStorageRead(ToStorageSlot(3)) ], - }); + }) + { TestName = "storage_reads" }; yield return new TestCaseData( "0xf862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0", @@ -553,7 +569,8 @@ private static IEnumerable AccountChangesTestSource { Address = Eip2935Constants.BlockHashHistoryAddress, StorageChanges = new(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")))]) } }, - }); + }) + { TestName = "storage_changes" }; } } @@ -623,7 +640,14 @@ private static IEnumerable BlockAccessListTestSource BlockAccessList expected = new(expectedAccountChanges); yield return new TestCaseData( "0xf90297f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0e3942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c9c801861319718811c8c0c0e994accc7d92b051544a255b8a899071040739bada75c0c0cccb01893635c99aac6d15af9cc3c20101c0dd94d9c0e57d447779673b236c7423aeab84e931f3bac0c0c3c20164c0c0", - expected); + expected) + { TestName = "balance_changes" }; + + yield return new TestCaseData( + "0xf902b5f89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f89f940000bbddc7ce488642fb579f8b00f3a590007251c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0f862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0a0f0963cedb68172fe724f0b345a86ee23926af1874e31a5fc50e9cc521d1556c0c0c0c0f88394000f3df6d732807ef1319fb7b8bb8522d0beac02f847f845a0000000000000000000000000000000000000000000000000000000000000000ce3e280a0000000000000000000000000000000000000000000000000000000000000000ce1a0000000000000000000000000000000000000000000000000000000000000200bc0c0c0dd941a7d50de1c4dc7d5b696f53b65594f21aa55a826c0c0c0c3c20102c0e0942adc25665018aa1fe0e6bc666dac8fc2697ff9bac0c0c6c50183026ffdc0c0e0947a8a0e14723feddb342a0273c72c07b88b25a5ffc0c0c0c3c20101c3c20100e994eed26eb981405168f24f2c9ad9cf427e1e39de43c0c0cccb01893635c9adc5de97e00ac3c20101c0", + expected + ) + { TestName = "code_changes" }; } } From e3d9fb5c6a9548f3eee46251a8dde6eef08cc4c7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:34:44 +0100 Subject: [PATCH 101/215] don't use hash of storage index --- .../BlockAccessLists/BlockAccessList.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 9040fbf6551..245859a656b 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -195,7 +195,9 @@ public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan if (before != after) { - StorageChange(accountChanges, new StorageCell(address, storageIndex).Hash.BytesAsSpan, after); + Span key = new byte[32]; + storageIndex.ToBigEndian(key); + StorageChange(accountChanges, key, after); } } @@ -211,15 +213,19 @@ public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] a if (before is null || !Enumerable.SequenceEqual(before, after)) { - StorageChange(accountChanges, storageCell.Hash.BytesAsSpan, after.AsSpan()); + Span key = new byte[32]; + storageCell.Index.ToBigEndian(key); + StorageChange(accountChanges, key, after.AsSpan()); } } public void AddStorageRead(in StorageCell storageCell) { + Span key = new byte[32]; + storageCell.Index.ToBigEndian(key); StorageRead storageRead = new() { - Key = new(storageCell.Hash.ToByteArray()) + Key = new(key) }; Address address = storageCell.Address; From 53948269ba430fd869fa0fa688ae06243ba05b33 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:41:47 +0100 Subject: [PATCH 102/215] don't add redundant reads --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 245859a656b..1e79ad32215 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -221,7 +221,7 @@ public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] a public void AddStorageRead(in StorageCell storageCell) { - Span key = new byte[32]; + byte[] key = new byte[32]; storageCell.Index.ToBigEndian(key); StorageRead storageRead = new() { @@ -235,7 +235,10 @@ public void AddStorageRead(in StorageCell storageCell) _accountChanges.Add(address, accountChanges); } - accountChanges.StorageReads.Add(storageRead); + if (!accountChanges.StorageChanges.ContainsKey(key)) + { + accountChanges.StorageReads.Add(storageRead); + } } private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) @@ -270,6 +273,8 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan Type = ChangeType.StorageChange, PreviousValue = previousStorage }); + + accountChanges.StorageReads.Remove(new(Bytes32.Wrap(storageKey))); } public readonly int TakeSnapshot() From 92c7d5ad92bc40a153f04013b3743196e940d43b Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:54:39 +0100 Subject: [PATCH 103/215] add new fields to blockchain tests --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 7 ++++++- src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs | 5 +++++ src/Nethermind/Nethermind.Core/Block.cs | 3 ++- src/Nethermind/Nethermind.Core/BlockHeader.cs | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 656d5f681be..f1f66c00355 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -56,7 +56,11 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) (long)Bytes.FromHexString(headerJson.Number).ToUInt256(), (long)Bytes.FromHexString(headerJson.GasLimit).ToUnsignedBigInteger(), (ulong)Bytes.FromHexString(headerJson.Timestamp).ToUnsignedBigInteger(), - Bytes.FromHexString(headerJson.ExtraData) + Bytes.FromHexString(headerJson.ExtraData), + (ulong)Bytes.FromHexString(headerJson.BlobGasUsed).ToUnsignedBigInteger(), + (ulong)Bytes.FromHexString(headerJson.ExcessBlobGas).ToUnsignedBigInteger(), + new Hash256(headerJson.ParentBeaconBlockRoot), + new Hash256(headerJson.RequestsHash) ); header.Bloom = new Bloom(Bytes.FromHexString(headerJson.Bloom)); @@ -69,6 +73,7 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) header.TxRoot = new Hash256(headerJson.TransactionsTrie); header.WithdrawalsRoot = new Hash256(headerJson.WithdrawalsRoot); header.BlockAccessListHash = new Hash256(headerJson.BlockAccessListHash); + header.BaseFeePerGas = (ulong)Bytes.FromHexString(headerJson.BaseFeePerGas).ToUnsignedBigInteger(); return header; } diff --git a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs index 826c1e3cbc1..d60fe9348fd 100644 --- a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs @@ -22,6 +22,11 @@ public class TestBlockHeaderJson public string TransactionsTrie { get; set; } public string UncleHash { get; set; } public string WithdrawalsRoot { get; set; } + public string ParentBeaconBlockRoot { get; set; } + public string RequestsHash { get; set; } public string BlockAccessListHash { get; set; } + public string BlobGasUsed { get; set; } + public string ExcessBlobGas { get; set; } + public string BaseFeePerGas { get; set; } } } diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 7637fec19ba..78931055c6a 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -40,7 +40,8 @@ public Block(BlockHeader header) : this( new( null, null, - header.WithdrawalsRoot is null ? null : []) + header.WithdrawalsRoot is null ? null : [], + header.BlockAccessListHash is null ? null : new()) ) { } diff --git a/src/Nethermind/Nethermind.Core/BlockHeader.cs b/src/Nethermind/Nethermind.Core/BlockHeader.cs index f4862b24a53..2c06a7d2629 100644 --- a/src/Nethermind/Nethermind.Core/BlockHeader.cs +++ b/src/Nethermind/Nethermind.Core/BlockHeader.cs @@ -77,7 +77,8 @@ public BlockHeader( public ulong? ExcessBlobGas { get; set; } public bool HasBody => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash) || (UnclesHash is not null && UnclesHash != Keccak.OfAnEmptySequenceRlp) - || (WithdrawalsRoot is not null && WithdrawalsRoot != Keccak.EmptyTreeHash); + || (WithdrawalsRoot is not null && WithdrawalsRoot != Keccak.EmptyTreeHash) + || (BlockAccessListHash is not null && BlockAccessListHash != Keccak.OfAnEmptySequenceRlp); public bool HasTransactions => TxRoot is not null && TxRoot != Keccak.EmptyTreeHash; From 1d6e8da364f1ef7c26aef2886af1d92714bd348b Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 26 Sep 2025 18:09:35 +0100 Subject: [PATCH 104/215] tidy code --- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 182 +++++++++--------- 1 file changed, 96 insertions(+), 86 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index f1f66c00355..abed2616a10 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -61,38 +61,40 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) (ulong)Bytes.FromHexString(headerJson.ExcessBlobGas).ToUnsignedBigInteger(), new Hash256(headerJson.ParentBeaconBlockRoot), new Hash256(headerJson.RequestsHash) - ); - - header.Bloom = new Bloom(Bytes.FromHexString(headerJson.Bloom)); - header.GasUsed = (long)Bytes.FromHexString(headerJson.GasUsed).ToUnsignedBigInteger(); - header.Hash = new Hash256(headerJson.Hash); - header.MixHash = new Hash256(headerJson.MixHash); - header.Nonce = (ulong)Bytes.FromHexString(headerJson.Nonce).ToUnsignedBigInteger(); - header.ReceiptsRoot = new Hash256(headerJson.ReceiptTrie); - header.StateRoot = new Hash256(headerJson.StateRoot); - header.TxRoot = new Hash256(headerJson.TransactionsTrie); - header.WithdrawalsRoot = new Hash256(headerJson.WithdrawalsRoot); - header.BlockAccessListHash = new Hash256(headerJson.BlockAccessListHash); - header.BaseFeePerGas = (ulong)Bytes.FromHexString(headerJson.BaseFeePerGas).ToUnsignedBigInteger(); + ) + { + Bloom = new Bloom(Bytes.FromHexString(headerJson.Bloom)), + GasUsed = (long)Bytes.FromHexString(headerJson.GasUsed).ToUnsignedBigInteger(), + Hash = new Hash256(headerJson.Hash), + MixHash = new Hash256(headerJson.MixHash), + Nonce = (ulong)Bytes.FromHexString(headerJson.Nonce).ToUnsignedBigInteger(), + ReceiptsRoot = new Hash256(headerJson.ReceiptTrie), + StateRoot = new Hash256(headerJson.StateRoot), + TxRoot = new Hash256(headerJson.TransactionsTrie), + WithdrawalsRoot = new Hash256(headerJson.WithdrawalsRoot), + BlockAccessListHash = new Hash256(headerJson.BlockAccessListHash), + BaseFeePerGas = (ulong)Bytes.FromHexString(headerJson.BaseFeePerGas).ToUnsignedBigInteger() + }; return header; } public static Transaction Convert(PostStateJson postStateJson, TransactionJson transactionJson) { - Transaction transaction = new(); - - transaction.Type = transactionJson.Type; - transaction.Value = transactionJson.Value[postStateJson.Indexes.Value]; - transaction.GasLimit = transactionJson.GasLimit[postStateJson.Indexes.Gas]; - transaction.GasPrice = transactionJson.GasPrice ?? transactionJson.MaxPriorityFeePerGas ?? 0; - transaction.DecodedMaxFeePerGas = transactionJson.MaxFeePerGas ?? 0; - transaction.Nonce = transactionJson.Nonce; - transaction.To = transactionJson.To; - transaction.Data = transactionJson.Data[postStateJson.Indexes.Data]; - transaction.SenderAddress = new PrivateKey(transactionJson.SecretKey).Address; - transaction.Signature = new Signature(1, 1, 27); - transaction.BlobVersionedHashes = transactionJson.BlobVersionedHashes; - transaction.MaxFeePerBlobGas = transactionJson.MaxFeePerBlobGas; + Transaction transaction = new() + { + Type = transactionJson.Type, + Value = transactionJson.Value[postStateJson.Indexes.Value], + GasLimit = transactionJson.GasLimit[postStateJson.Indexes.Gas], + GasPrice = transactionJson.GasPrice ?? transactionJson.MaxPriorityFeePerGas ?? 0, + DecodedMaxFeePerGas = transactionJson.MaxFeePerGas ?? 0, + Nonce = transactionJson.Nonce, + To = transactionJson.To, + Data = transactionJson.Data[postStateJson.Indexes.Data], + SenderAddress = new PrivateKey(transactionJson.SecretKey).Address, + Signature = new Signature(1, 1, 27), + BlobVersionedHashes = transactionJson.BlobVersionedHashes, + MaxFeePerBlobGas = transactionJson.MaxFeePerBlobGas + }; transaction.Hash = transaction.CalculateHash(); AccessList.Builder builder = new(); @@ -115,7 +117,7 @@ public static Transaction Convert(PostStateJson postStateJson, TransactionJson t if (transactionJson.AuthorizationList is not null) { transaction.AuthorizationList = - transactionJson.AuthorizationList + [.. transactionJson.AuthorizationList .Select(i => { if (i.Nonce > ulong.MaxValue) @@ -155,7 +157,7 @@ public static Transaction Convert(PostStateJson postStateJson, TransactionJson t (byte)i.V, r, s); - }).ToArray(); + })]; if (transaction.AuthorizationList.Any()) { transaction.Type = TxType.SetCode; @@ -179,14 +181,16 @@ public static void ProcessAccessList(AccessListItemJson[]? accessList, AccessLis public static Transaction Convert(LegacyTransactionJson transactionJson) { - Transaction transaction = new(); - transaction.Value = transactionJson.Value; - transaction.GasLimit = transactionJson.GasLimit; - transaction.GasPrice = transactionJson.GasPrice; - transaction.Nonce = transactionJson.Nonce; - transaction.To = transactionJson.To; - transaction.Data = transactionJson.Data; - transaction.Signature = new Signature(transactionJson.R, transactionJson.S, transactionJson.V); + Transaction transaction = new() + { + Value = transactionJson.Value, + GasLimit = transactionJson.GasLimit, + GasPrice = transactionJson.GasPrice, + Nonce = transactionJson.Nonce, + To = transactionJson.To, + Data = transactionJson.Data, + Signature = new Signature(transactionJson.R, transactionJson.S, transactionJson.V) + }; transaction.Hash = transaction.CalculateHash(); return transaction; } @@ -198,41 +202,42 @@ public static IEnumerable Convert(string name, string category return Enumerable.Repeat(new GeneralStateTest { Name = name, Category = category, LoadFailure = testJson.LoadFailure }, 1); } - List blockchainTests = new(); + List blockchainTests = []; foreach (KeyValuePair postStateBySpec in testJson.Post) { int iterationNumber = 0; foreach (PostStateJson stateJson in postStateBySpec.Value) { - GeneralStateTest test = new(); - test.Name = Path.GetFileName(name) + - $"_d{stateJson.Indexes.Data}g{stateJson.Indexes.Gas}v{stateJson.Indexes.Value}_"; + GeneralStateTest test = new() + { + Name = Path.GetFileName(name) + + $"_d{stateJson.Indexes.Data}g{stateJson.Indexes.Gas}v{stateJson.Indexes.Value}_", + Category = category, + ForkName = postStateBySpec.Key, + Fork = SpecNameParser.Parse(postStateBySpec.Key), + PreviousHash = testJson.Env.PreviousHash, + CurrentCoinbase = testJson.Env.CurrentCoinbase, + CurrentDifficulty = testJson.Env.CurrentDifficulty, + CurrentGasLimit = testJson.Env.CurrentGasLimit, + CurrentNumber = testJson.Env.CurrentNumber, + CurrentTimestamp = testJson.Env.CurrentTimestamp, + CurrentBaseFee = testJson.Env.CurrentBaseFee, + CurrentRandom = testJson.Env.CurrentRandom, + CurrentBeaconRoot = testJson.Env.CurrentBeaconRoot, + CurrentWithdrawalsRoot = testJson.Env.CurrentWithdrawalsRoot, + CurrentExcessBlobGas = testJson.Env.CurrentExcessBlobGas, + ParentBlobGasUsed = testJson.Env.ParentBlobGasUsed, + ParentExcessBlobGas = testJson.Env.ParentExcessBlobGas, + PostReceiptsRoot = stateJson.Logs, + PostHash = stateJson.Hash, + Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value), + Transaction = Convert(stateJson, testJson.Transaction) + }; + if (testJson.Info?.Labels?.ContainsKey(iterationNumber.ToString()) ?? false) { test.Name += testJson.Info?.Labels?[iterationNumber.ToString()]?.Replace(":label ", string.Empty); } - test.Category = category; - - test.ForkName = postStateBySpec.Key; - test.Fork = SpecNameParser.Parse(postStateBySpec.Key); - test.PreviousHash = testJson.Env.PreviousHash; - test.CurrentCoinbase = testJson.Env.CurrentCoinbase; - test.CurrentDifficulty = testJson.Env.CurrentDifficulty; - test.CurrentGasLimit = testJson.Env.CurrentGasLimit; - test.CurrentNumber = testJson.Env.CurrentNumber; - test.CurrentTimestamp = testJson.Env.CurrentTimestamp; - test.CurrentBaseFee = testJson.Env.CurrentBaseFee; - test.CurrentRandom = testJson.Env.CurrentRandom; - test.CurrentBeaconRoot = testJson.Env.CurrentBeaconRoot; - test.CurrentWithdrawalsRoot = testJson.Env.CurrentWithdrawalsRoot; - test.CurrentExcessBlobGas = testJson.Env.CurrentExcessBlobGas; - test.ParentBlobGasUsed = testJson.Env.ParentBlobGasUsed; - test.ParentExcessBlobGas = testJson.Env.ParentExcessBlobGas; - test.PostReceiptsRoot = stateJson.Logs; - test.PostHash = stateJson.Hash; - test.Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value); - test.Transaction = Convert(stateJson, testJson.Transaction); - blockchainTests.Add(test); ++iterationNumber; } @@ -248,17 +253,19 @@ public static BlockchainTest Convert(string name, string category, BlockchainTes return new BlockchainTest { Name = name, Category = category, LoadFailure = testJson.LoadFailure }; } - BlockchainTest test = new(); - test.Name = name; - test.Category = category; - test.Network = testJson.EthereumNetwork; - test.NetworkAfterTransition = testJson.EthereumNetworkAfterTransition; - test.TransitionForkActivation = testJson.TransitionForkActivation; - test.LastBlockHash = new Hash256(testJson.LastBlockHash); - test.GenesisRlp = testJson.GenesisRlp is null ? null : new Rlp(Bytes.FromHexString(testJson.GenesisRlp)); - test.GenesisBlockHeader = testJson.GenesisBlockHeader; - test.Blocks = testJson.Blocks; - test.Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value); + BlockchainTest test = new() + { + Name = name, + Category = category, + Network = testJson.EthereumNetwork, + NetworkAfterTransition = testJson.EthereumNetworkAfterTransition, + TransitionForkActivation = testJson.TransitionForkActivation, + LastBlockHash = new Hash256(testJson.LastBlockHash), + GenesisRlp = testJson.GenesisRlp is null ? null : new Rlp(Bytes.FromHexString(testJson.GenesisRlp)), + GenesisBlockHeader = testJson.GenesisBlockHeader, + Blocks = testJson.Blocks, + Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value) + }; HalfBlockchainTestJson half = testJson as HalfBlockchainTestJson; if (half is not null) @@ -279,7 +286,7 @@ public static BlockchainTest Convert(string name, string category, BlockchainTes public static IEnumerable ConvertToEofTests(string json) { Dictionary testsInFile = _serializer.Deserialize>(json); - List tests = new(); + List tests = []; foreach (KeyValuePair namedTest in testsInFile) { (string name, string category) = GetNameAndCategory(namedTest.Key); @@ -288,11 +295,13 @@ public static IEnumerable ConvertToEofTests(string json) foreach (KeyValuePair pair in namedTest.Value.Vectors) { VectorTestJson vectorJson = pair.Value; - VectorTest vector = new(); - vector.Code = Bytes.FromHexString(vectorJson.Code); - vector.ContainerKind = ParseContainerKind(vectorJson.ContainerKind); + VectorTest vector = new() + { + Code = Bytes.FromHexString(vectorJson.Code), + ContainerKind = ParseContainerKind(vectorJson.ContainerKind) + }; - foreach (var result in vectorJson.Results) + foreach (KeyValuePair result in vectorJson.Results) { EofTest test = new() { @@ -300,10 +309,10 @@ public static IEnumerable ConvertToEofTests(string json) Category = $"{category} [{result.Key}]", Url = url, Description = description, - Spec = spec + Spec = spec, + Vector = vector, + Result = result.ToTestResult() }; - test.Vector = vector; - test.Result = result.ToTestResult(); tests.Add(test); } } @@ -339,7 +348,7 @@ public static IEnumerable ConvertStateTest(string json) Dictionary testsInFile = _serializer.Deserialize>(json); - List tests = new(); + List tests = []; foreach (KeyValuePair namedTest in testsInFile) { (string name, string category) = GetNameAndCategory(namedTest.Key); @@ -358,15 +367,16 @@ public static IEnumerable ConvertToBlockchainTests(string json) } catch (Exception) { - var half = _serializer.Deserialize>(json); - testsInFile = new Dictionary(); + Dictionary half = + _serializer.Deserialize>(json); + testsInFile = []; foreach (KeyValuePair pair in half) { testsInFile[pair.Key] = pair.Value; } } - List testsByName = new(); + List testsByName = []; foreach ((string testName, BlockchainTestJson testSpec) in testsInFile) { string[] transitionInfo = testSpec.Network.Split("At"); From 2d3a412134581648c833289137a13df7eae99e9c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:15:45 +0100 Subject: [PATCH 105/215] add blockchain engine test support --- .../Ethereum.Test.Base/BlockchainTest.cs | 1 + .../Ethereum.Test.Base/BlockchainTestBase.cs | 84 +++++++++++++------ .../Ethereum.Test.Base/BlockchainTestJson.cs | 1 + .../Ethereum.Test.Base/JsonToEthereumTest.cs | 44 +++++++++- .../TestEngineNewPayloadsJson.cs | 45 ++++++++++ .../Nethermind.Merge.Plugin/MergePlugin.cs | 2 +- .../NoEngineRequestTracker.cs | 17 ++++ 7 files changed, 166 insertions(+), 28 deletions(-) create mode 100644 src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs create mode 100644 src/Nethermind/Nethermind.Merge.Plugin/NoEngineRequestTracker.cs diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs index 19f62afb800..b4f78c9a057 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTest.cs @@ -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? Pre { get; set; } public Dictionary? PostState { get; set; } diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 413fd4c60f4..3aef7f794a4 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -35,6 +35,8 @@ using Nethermind.Init.Modules; using Nethermind.TxPool; using NUnit.Framework; +using Nethermind.Merge.Plugin.Data; +using Nethermind.Merge.Plugin; namespace Ethereum.Test.Base; @@ -129,6 +131,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? // configProvider.GetConfig().PreWarmStateOnBlockProcessing = false; await using IContainer container = new ContainerBuilder() .AddModule(new TestNethermindModule(configProvider)) + .AddModule(new TestMergeModule(configProvider)) .AddSingleton(specProvider) .AddSingleton(_logManager) .AddSingleton(rewardCalculator) @@ -141,9 +144,11 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? IBlockchainProcessor blockchainProcessor = mainBlockProcessingContext.BlockchainProcessor; IBlockTree blockTree = container.Resolve(); IBlockValidator blockValidator = container.Resolve(); + IEngineRpcModule engineRpcModule = container.Resolve(); blockchainProcessor.Start(); BlockHeader parentHeader; + ExecutionPayload[] payloads; // Genesis processing using (stateProvider.BeginScope(null)) { @@ -154,8 +159,11 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? test.GenesisRlp ??= Rlp.Encode(new Block(JsonToEthereumTest.Convert(test.GenesisBlockHeader))); Block genesisBlock = Rlp.Decode(test.GenesisRlp.Bytes); + // Console.WriteLine(genesisBlock.ToString(Block.Format.Full)); Assert.That(genesisBlock.Header.Hash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash))); + payloads = [.. JsonToEthereumTest.Convert(test.EngineNewPayloads)]; + ManualResetEvent genesisProcessed = new(false); blockTree.NewHeadBlock += (_, args) => @@ -172,6 +180,53 @@ protected async Task 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) + { + // blockchain test engine + foreach (ExecutionPayload executionPayload in payloads) + { + await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, [], executionPayload.ParentBeaconBlockRoot, []); + } + } + else + { + Assert.Fail("Invalid blockchain test, did not contain blocks or new payloads."); + } + + await blockchainProcessor.StopAsync(true); + stopwatch?.Stop(); + + IBlockCachePreWarmer? preWarmer = container.Resolve().LifetimeScope.ResolveOptional(); + 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 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 + ); + } + + private 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++) { @@ -211,31 +266,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? parentHeader = correctRlp[i].Block.Header; } - await blockchainProcessor.StopAsync(true); - stopwatch?.Stop(); - - IBlockCachePreWarmer? preWarmer = container.Resolve().LifetimeScope.ResolveOptional(); - 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 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) @@ -248,6 +279,9 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? { var rlpContext = Bytes.FromHexString(testBlockJson.Rlp).AsRlpStream(); Block suggestedBlock = Rlp.Decode(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; diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs index fdd03858fd8..c344cd773da 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs @@ -24,6 +24,7 @@ public class BlockchainTestJson public TestBlockJson[]? Blocks { get; set; } public TestBlockHeaderJson? GenesisBlockHeader { get; set; } + public TestEngineNewPayloadsJson[]? EngineNewPayloads { get; set; } public Dictionary? Pre { get; set; } public Dictionary? PostState { get; set; } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index abed2616a10..1fd3471c741 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -14,6 +15,7 @@ using Nethermind.Crypto; using Nethermind.Evm.EvmObjectFormat; using Nethermind.Int256; +using Nethermind.Merge.Plugin.Data; using Nethermind.Serialization.Json; using Nethermind.Serialization.Rlp; using Nethermind.Specs; @@ -78,6 +80,42 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) return header; } + public static IEnumerable Convert(TestEngineNewPayloadsJson[]? executionPayloadsJson) + { + if (executionPayloadsJson is null) + { + throw new InvalidDataException("Execution payloads JSON was null when constructing test."); + } + + foreach (TestEngineNewPayloadsJson engineNewPayload in executionPayloadsJson) + { + TestEngineNewPayloadsJson.ParamsExecutionPayload executionPayload = engineNewPayload.Params[0].Deserialize(EthereumJsonSerializer.JsonOptions); + string[]? blobVersionedHashes = engineNewPayload.Params[1].Deserialize(EthereumJsonSerializer.JsonOptions); + string? parentBeaconBlockRoot = engineNewPayload.Params[2].Deserialize(EthereumJsonSerializer.JsonOptions); + string[]? validationError = engineNewPayload.Params[3].Deserialize(EthereumJsonSerializer.JsonOptions); + yield return new ExecutionPayloadV3() + { + + BaseFeePerGas = (ulong)Bytes.FromHexString(executionPayload.BaseFeePerGas).ToUnsignedBigInteger(), + BlockHash = new(executionPayload.BlockHash), + BlockNumber = (long)Bytes.FromHexString(executionPayload.BaseFeePerGas).ToUnsignedBigInteger(), + ExtraData = Bytes.FromHexString(executionPayload.ExtraData), + FeeRecipient = new(executionPayload.FeeRecipient), + GasLimit = (long)Bytes.FromHexString(executionPayload.GasLimit).ToUnsignedBigInteger(), + GasUsed = (long)Bytes.FromHexString(executionPayload.GasUsed).ToUnsignedBigInteger(), + LogsBloom = new(Bytes.FromHexString(executionPayload.LogsBloom)), + ParentHash = new(executionPayload.ParentHash), + PrevRandao = new(executionPayload.PrevRandao), + ReceiptsRoot = new(executionPayload.ReceiptsRoot), + StateRoot = new(executionPayload.StateRoot), + Timestamp = (ulong)Bytes.FromHexString(executionPayload.Timestamp).ToUnsignedBigInteger(), + BlockAccessList = Bytes.FromHexString(executionPayload.BlockAccessList), + BlobGasUsed = (ulong)Bytes.FromHexString(executionPayload.BlobGasUsed).ToUnsignedBigInteger(), + ParentBeaconBlockRoot = parentBeaconBlockRoot is null ? null : new(parentBeaconBlockRoot), + }; + } + } + public static Transaction Convert(PostStateJson postStateJson, TransactionJson transactionJson) { Transaction transaction = new() @@ -264,6 +302,7 @@ public static BlockchainTest Convert(string name, string category, BlockchainTes GenesisRlp = testJson.GenesisRlp is null ? null : new Rlp(Bytes.FromHexString(testJson.GenesisRlp)), GenesisBlockHeader = testJson.GenesisBlockHeader, Blocks = testJson.Blocks, + EngineNewPayloads = testJson.EngineNewPayloads, Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value) }; @@ -321,7 +360,7 @@ public static IEnumerable ConvertToEofTests(string json) return tests; static ValidationStrategy ParseContainerKind(string containerKind) - => ("INITCODE".Equals(containerKind) ? ValidationStrategy.ValidateInitCodeMode : ValidationStrategy.ValidateRuntimeMode); + => "INITCODE".Equals(containerKind) ? ValidationStrategy.ValidateInitCodeMode : ValidationStrategy.ValidateRuntimeMode; static void GetTestMetaData(KeyValuePair namedTest, out string? description, out string? url, out string? spec) { @@ -365,8 +404,9 @@ public static IEnumerable ConvertToBlockchainTests(string json) { testsInFile = _serializer.Deserialize>(json); } - catch (Exception) + catch (Exception e) { + Console.WriteLine(e); Dictionary half = _serializer.Deserialize>(json); testsInFile = []; diff --git a/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs b/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs new file mode 100644 index 00000000000..14e3a8c9a41 --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Text.Json; + +namespace Ethereum.Test.Base +{ + public class TestEngineNewPayloadsJson + { + public JsonElement[] Params { get; set; } + // public EngineNewPayloadParams Params { get; set; } + public string? NewPayloadVersion { get; set; } + public string? ForkChoiceUpdatedVersion { get; set; } + + // public class EngineNewPayloadParams + // { + // public ParamsExecutionPayload ExecutionPayload; + // public string[] BlobVersionedHashes; + // public string ParentBeaconBlockRoot; + // public string ValidationError; + // } + + public class ParamsExecutionPayload + { + public string ParentHash { get; set; } + public string FeeRecipient { get; set; } + public string StateRoot { get; set; } + public string ReceiptsRoot { get; set; } + public string LogsBloom { get; set; } + public string BlockNumber { get; set; } + public string GasLimit { get; set; } + public string GasUsed { get; set; } + public string Timestamp { get; set; } + public string ExtraData { get; set; } + public string PrevRandao { get; set; } + public string BaseFeePerGas { get; set; } + public string BlobGasUsed { get; set; } + public string ExcessBlobGas { get; set; } + public string BlockHash { get; set; } + public string[] Transactions { get; set; } + public string[] Withdrawals { get; set; } + public string BlockAccessList { get; set; } + } + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 92d6a2961e0..f5d933da50d 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -22,7 +22,6 @@ using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Exceptions; -using Nethermind.Core.Timers; using Nethermind.Db; using Nethermind.Facade.Proxy; using Nethermind.HealthChecks; @@ -329,6 +328,7 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton>, GetBlobsHandler>() .AddSingleton?>, GetBlobsHandlerV2>() + .AddSingleton() .AddSingleton() .AddSingleton((ctx) => diff --git a/src/Nethermind/Nethermind.Merge.Plugin/NoEngineRequestTracker.cs b/src/Nethermind/Nethermind.Merge.Plugin/NoEngineRequestTracker.cs new file mode 100644 index 00000000000..e0d1b606bd3 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/NoEngineRequestTracker.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading.Tasks; +using Nethermind.Api; + +namespace Nethermind.Merge.Plugin; + +public class NoEngineRequestsTracker : IEngineRequestsTracker +{ + public void OnForkchoiceUpdatedCalled() { } + + public void OnNewPayloadCalled() { } + + public Task StartAsync() + => Task.CompletedTask; +} From 2665f0e29aa2629b5372ea7dbbc17634eb2c3f68 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:20:41 +0100 Subject: [PATCH 106/215] tidy test base --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 3aef7f794a4..056f5a7e6fb 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -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; @@ -30,7 +29,6 @@ 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; @@ -42,16 +40,13 @@ 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(); } @@ -202,11 +197,9 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? stopwatch?.Stop(); IBlockCachePreWarmer? preWarmer = container.Resolve().LifetimeScope.ResolveOptional(); - 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(); - } + + // 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 differences; @@ -215,7 +208,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider); } - Assert.That(differences.Count, Is.Zero, "differences"); + Assert.That(differences, Is.Empty, "differences"); return new EthereumTestResult ( @@ -225,7 +218,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? ); } - private BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IBlockValidator blockValidator, IBlockTree blockTree, BlockHeader parentHeader) + 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++) @@ -269,15 +262,15 @@ private BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IB 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(rlpContext); // Console.WriteLine("suggested block:"); // Console.WriteLine(suggestedBlock.BlockAccessList); @@ -322,17 +315,20 @@ private BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalidRlp, IB 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 accountState in - ((IEnumerable>)test.Pre ?? Array.Empty>())) + (IEnumerable>)test.Pre ?? Array.Empty>()) { foreach (KeyValuePair storageItem in accountState.Value.Storage) { @@ -350,18 +346,18 @@ private void InitializeTestState(BlockchainTest test, IWorldState stateProvider, stateProvider.Reset(); } - private List RunAssertions(BlockchainTest test, Block headBlock, IWorldState stateProvider) + private static List RunAssertions(BlockchainTest test, Block headBlock, IWorldState stateProvider) { if (test.PostStateRoot is not null) { - return test.PostStateRoot != stateProvider.StateRoot ? new List { "state root mismatch" } : Enumerable.Empty().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 differences = new(); + List differences = []; IEnumerable> deletedAccounts = test.Pre? .Where(pre => !(test.PostState?.ContainsKey(pre.Key) ?? false)) ?? Array.Empty>(); @@ -385,8 +381,8 @@ private List 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) { @@ -398,7 +394,7 @@ private List 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}"); @@ -411,10 +407,10 @@ private List RunAssertions(BlockchainTest test, Block headBlock, IWorldS differencesBefore = differences.Count; - KeyValuePair[] clearedStorages = new KeyValuePair[0]; + KeyValuePair[] 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 clearedStorage in clearedStorages) From 19694c20852eec47e1c6c38161238abaaf3d970d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:17:46 +0100 Subject: [PATCH 107/215] fix engine test loading, different versions --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 28 ++++++++++++++++--- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 13 +++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 056f5a7e6fb..18eafc788ea 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -35,6 +35,7 @@ using NUnit.Framework; using Nethermind.Merge.Plugin.Data; using Nethermind.Merge.Plugin; +using Nethermind.JsonRpc; namespace Ethereum.Test.Base; @@ -143,7 +144,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? blockchainProcessor.Start(); BlockHeader parentHeader; - ExecutionPayload[] payloads; + (ExecutionPayload, string[]?, string[]?, string?)[] payloads; // Genesis processing using (stateProvider.BeginScope(null)) { @@ -165,7 +166,7 @@ protected async Task 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(); } }; @@ -183,9 +184,28 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? else if (test.EngineNewPayloads is not null) { // blockchain test engine - foreach (ExecutionPayload executionPayload in payloads) + foreach ((ExecutionPayload executionPayload, string[]? blobVersionedHashes, string[]? validationError, string? newPayloadVersion) in payloads) { - await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, [], executionPayload.ParentBeaconBlockRoot, []); + ResultWrapper res; + switch (newPayloadVersion ?? "4") + { + 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": + ExecutionPayloadV3 payload = (ExecutionPayloadV3)executionPayload; + res = await engineRpcModule.engine_newPayloadV4(payload, [], executionPayload.ParentBeaconBlockRoot, []); + break; + default: + Assert.Fail("Invalid blockchain engine test, version not recognised."); + break; + } } } else diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 1fd3471c741..b2d6759c17b 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -80,7 +80,7 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) return header; } - public static IEnumerable Convert(TestEngineNewPayloadsJson[]? executionPayloadsJson) + public static IEnumerable<(ExecutionPayload, string[]?, string[]?, string?)> Convert(TestEngineNewPayloadsJson[]? executionPayloadsJson) { if (executionPayloadsJson is null) { @@ -93,12 +93,11 @@ public static IEnumerable Convert(TestEngineNewPayloadsJson[]? string[]? blobVersionedHashes = engineNewPayload.Params[1].Deserialize(EthereumJsonSerializer.JsonOptions); string? parentBeaconBlockRoot = engineNewPayload.Params[2].Deserialize(EthereumJsonSerializer.JsonOptions); string[]? validationError = engineNewPayload.Params[3].Deserialize(EthereumJsonSerializer.JsonOptions); - yield return new ExecutionPayloadV3() + yield return (new ExecutionPayloadV3() { - BaseFeePerGas = (ulong)Bytes.FromHexString(executionPayload.BaseFeePerGas).ToUnsignedBigInteger(), BlockHash = new(executionPayload.BlockHash), - BlockNumber = (long)Bytes.FromHexString(executionPayload.BaseFeePerGas).ToUnsignedBigInteger(), + BlockNumber = (long)Bytes.FromHexString(executionPayload.BlockNumber).ToUnsignedBigInteger(), ExtraData = Bytes.FromHexString(executionPayload.ExtraData), FeeRecipient = new(executionPayload.FeeRecipient), GasLimit = (long)Bytes.FromHexString(executionPayload.GasLimit).ToUnsignedBigInteger(), @@ -111,8 +110,12 @@ public static IEnumerable Convert(TestEngineNewPayloadsJson[]? Timestamp = (ulong)Bytes.FromHexString(executionPayload.Timestamp).ToUnsignedBigInteger(), BlockAccessList = Bytes.FromHexString(executionPayload.BlockAccessList), BlobGasUsed = (ulong)Bytes.FromHexString(executionPayload.BlobGasUsed).ToUnsignedBigInteger(), + ExcessBlobGas = (ulong)Bytes.FromHexString(executionPayload.ExcessBlobGas).ToUnsignedBigInteger(), ParentBeaconBlockRoot = parentBeaconBlockRoot is null ? null : new(parentBeaconBlockRoot), - }; + Withdrawals = [.. executionPayload.Withdrawals.Select(x => Rlp.Decode(Bytes.FromHexString(x)))], + Transactions = [.. executionPayload.Transactions.Select(x => Bytes.FromHexString(x))], + ExecutionRequests = [] + }, blobVersionedHashes, validationError, engineNewPayload.NewPayloadVersion); } } From d6fc96db0d49bacc538c6fca800451be9fa1ba08 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:24:01 +0100 Subject: [PATCH 108/215] only set hash if bals enabled --- .../Processing/BlockProcessor.cs | 17 ++++++++++------- .../Validators/BlockValidator.cs | 5 ++++- .../Data/ExecutionPayload.cs | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 82eff985a3e..99e3f324797 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,14 +178,17 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + { + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + } } header.Hash = header.CalculateHash(); diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index f7c54b188ff..a21e5277cf3 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -3,6 +3,7 @@ using System; using System.Text; +using System.Text.Json; using Nethermind.Blockchain; using Nethermind.Core; using Nethermind.Core.BlockAccessLists; @@ -135,7 +136,7 @@ public bool ValidateSuggestedBlock(Block block, BlockHeader? parent, out string? if (spec.BlockLevelAccessListsEnabled) { - if (block.BlockAccessList is null) + if (block.BlockAccessList is null || block.BlockAccessListHash is null) { if (_logger.IsDebug) _logger.Debug($"{Invalid(block)} Block-level access list was missing or empty"); errorMessage = BlockErrorMessages.InvalidBlockLevelAccessList; @@ -244,6 +245,8 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B if (processedBlock.Header.BlockAccessListHash != suggestedBlock.Header.BlockAccessListHash) { + // Console.WriteLine("processed block:"); + // Console.WriteLine(processedBlock.BlockAccessList.ToString()); if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 9425168c84f..6ddc82edbdf 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -52,7 +52,7 @@ public class ExecutionPayload : IForkValidator, IExecutionPayloadParams, IExecut public ulong Timestamp { get; set; } - public byte[] BlockAccessList { get; set; } = []; + public byte[]? BlockAccessList { get; set; } = []; protected byte[][] _encodedTransactions = []; @@ -170,7 +170,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) TotalDifficulty = totalDifficulty, TxRoot = TxTrie.CalculateRoot(transactions.Transactions), WithdrawalsRoot = BuildWithdrawalsRoot(), - BlockAccessListHash = BlockAccessList.Length == 0 ? null : new(ValueKeccak.Compute(BlockAccessList).Bytes) + BlockAccessListHash = BlockAccessList is null || BlockAccessList.Length == 0 ? null : new(ValueKeccak.Compute(BlockAccessList).Bytes) }; return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, Rlp.Decode(BlockAccessList))); From 93e823fbcd56aa8e11016d4682afa83a435b7c75 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 30 Sep 2025 17:24:21 +0100 Subject: [PATCH 109/215] fix loading older engine tests --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 3 ++- .../Ethereum.Test.Base/JsonToEthereumTest.cs | 26 +++++++++---------- .../Ethereum.Test.Base/TestBlockHeaderJson.cs | 14 +++++----- .../TestEngineNewPayloadsJson.cs | 4 +-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 18eafc788ea..e34405e65e6 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -200,7 +200,8 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? break; case "4": ExecutionPayloadV3 payload = (ExecutionPayloadV3)executionPayload; - res = await engineRpcModule.engine_newPayloadV4(payload, [], executionPayload.ParentBeaconBlockRoot, []); + byte[]?[] hashes = blobVersionedHashes is null ? null : [.. blobVersionedHashes.Select(x => Bytes.FromHexString(x))]; + res = await engineRpcModule.engine_newPayloadV4(payload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; default: Assert.Fail("Invalid blockchain engine test, version not recognised."); diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index b2d6759c17b..432156d5dcc 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -59,10 +59,10 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) (long)Bytes.FromHexString(headerJson.GasLimit).ToUnsignedBigInteger(), (ulong)Bytes.FromHexString(headerJson.Timestamp).ToUnsignedBigInteger(), Bytes.FromHexString(headerJson.ExtraData), - (ulong)Bytes.FromHexString(headerJson.BlobGasUsed).ToUnsignedBigInteger(), - (ulong)Bytes.FromHexString(headerJson.ExcessBlobGas).ToUnsignedBigInteger(), - new Hash256(headerJson.ParentBeaconBlockRoot), - new Hash256(headerJson.RequestsHash) + headerJson.BlobGasUsed is null ? null : (ulong)Bytes.FromHexString(headerJson.BlobGasUsed).ToUnsignedBigInteger(), + headerJson.ExcessBlobGas is null ? null : (ulong)Bytes.FromHexString(headerJson.ExcessBlobGas).ToUnsignedBigInteger(), + headerJson.ParentBeaconBlockRoot is null ? null : new Hash256(headerJson.ParentBeaconBlockRoot), + headerJson.RequestsHash is null ? null : new Hash256(headerJson.RequestsHash) ) { Bloom = new Bloom(Bytes.FromHexString(headerJson.Bloom)), @@ -73,8 +73,8 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) ReceiptsRoot = new Hash256(headerJson.ReceiptTrie), StateRoot = new Hash256(headerJson.StateRoot), TxRoot = new Hash256(headerJson.TransactionsTrie), - WithdrawalsRoot = new Hash256(headerJson.WithdrawalsRoot), - BlockAccessListHash = new Hash256(headerJson.BlockAccessListHash), + WithdrawalsRoot = headerJson.WithdrawalsRoot is null ? null : new Hash256(headerJson.WithdrawalsRoot), + BlockAccessListHash = headerJson.BlockAccessListHash is null ? null : new Hash256(headerJson.BlockAccessListHash), BaseFeePerGas = (ulong)Bytes.FromHexString(headerJson.BaseFeePerGas).ToUnsignedBigInteger() }; return header; @@ -90,9 +90,9 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) foreach (TestEngineNewPayloadsJson engineNewPayload in executionPayloadsJson) { TestEngineNewPayloadsJson.ParamsExecutionPayload executionPayload = engineNewPayload.Params[0].Deserialize(EthereumJsonSerializer.JsonOptions); - string[]? blobVersionedHashes = engineNewPayload.Params[1].Deserialize(EthereumJsonSerializer.JsonOptions); - string? parentBeaconBlockRoot = engineNewPayload.Params[2].Deserialize(EthereumJsonSerializer.JsonOptions); - string[]? validationError = engineNewPayload.Params[3].Deserialize(EthereumJsonSerializer.JsonOptions); + string[]? blobVersionedHashes = engineNewPayload.Params.Length > 1 ? engineNewPayload.Params[1].Deserialize(EthereumJsonSerializer.JsonOptions) : null; + string? parentBeaconBlockRoot = engineNewPayload.Params.Length > 2 ? engineNewPayload.Params[2].Deserialize(EthereumJsonSerializer.JsonOptions) : null; + string[]? validationError = engineNewPayload.Params.Length > 3 ? engineNewPayload.Params[3].Deserialize(EthereumJsonSerializer.JsonOptions) : null; yield return (new ExecutionPayloadV3() { BaseFeePerGas = (ulong)Bytes.FromHexString(executionPayload.BaseFeePerGas).ToUnsignedBigInteger(), @@ -108,11 +108,11 @@ public static BlockHeader Convert(TestBlockHeaderJson? headerJson) ReceiptsRoot = new(executionPayload.ReceiptsRoot), StateRoot = new(executionPayload.StateRoot), Timestamp = (ulong)Bytes.FromHexString(executionPayload.Timestamp).ToUnsignedBigInteger(), - BlockAccessList = Bytes.FromHexString(executionPayload.BlockAccessList), - BlobGasUsed = (ulong)Bytes.FromHexString(executionPayload.BlobGasUsed).ToUnsignedBigInteger(), - ExcessBlobGas = (ulong)Bytes.FromHexString(executionPayload.ExcessBlobGas).ToUnsignedBigInteger(), + BlockAccessList = executionPayload.BlockAccessList is null ? null : Bytes.FromHexString(executionPayload.BlockAccessList), + BlobGasUsed = executionPayload.BlobGasUsed is null ? null : (ulong)Bytes.FromHexString(executionPayload.BlobGasUsed).ToUnsignedBigInteger(), + ExcessBlobGas = executionPayload.ExcessBlobGas is null ? null : (ulong)Bytes.FromHexString(executionPayload.ExcessBlobGas).ToUnsignedBigInteger(), ParentBeaconBlockRoot = parentBeaconBlockRoot is null ? null : new(parentBeaconBlockRoot), - Withdrawals = [.. executionPayload.Withdrawals.Select(x => Rlp.Decode(Bytes.FromHexString(x)))], + Withdrawals = executionPayload.Withdrawals is null ? null : [.. executionPayload.Withdrawals.Select(x => Rlp.Decode(Bytes.FromHexString(x)))], Transactions = [.. executionPayload.Transactions.Select(x => Bytes.FromHexString(x))], ExecutionRequests = [] }, blobVersionedHashes, validationError, engineNewPayload.NewPayloadVersion); diff --git a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs index d60fe9348fd..39c11de9c42 100644 --- a/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/TestBlockHeaderJson.cs @@ -21,12 +21,12 @@ public class TestBlockHeaderJson public string Timestamp { get; set; } public string TransactionsTrie { get; set; } public string UncleHash { get; set; } - public string WithdrawalsRoot { get; set; } - public string ParentBeaconBlockRoot { get; set; } - public string RequestsHash { get; set; } - public string BlockAccessListHash { get; set; } - public string BlobGasUsed { get; set; } - public string ExcessBlobGas { get; set; } - public string BaseFeePerGas { get; set; } + public string? WithdrawalsRoot { get; set; } + public string? ParentBeaconBlockRoot { get; set; } + public string? RequestsHash { get; set; } + public string? BlockAccessListHash { get; set; } + public string? BlobGasUsed { get; set; } + public string? ExcessBlobGas { get; set; } + public string? BaseFeePerGas { get; set; } } } diff --git a/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs b/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs index 14e3a8c9a41..a293b5175ac 100644 --- a/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/TestEngineNewPayloadsJson.cs @@ -38,8 +38,8 @@ public class ParamsExecutionPayload public string ExcessBlobGas { get; set; } public string BlockHash { get; set; } public string[] Transactions { get; set; } - public string[] Withdrawals { get; set; } - public string BlockAccessList { get; set; } + public string[]? Withdrawals { get; set; } + public string? BlockAccessList { get; set; } } } } From ce700c26bd0213fbc9696282c01d8076f0a43c77 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:53:16 +0100 Subject: [PATCH 110/215] fix BAL decoding in execution payload --- .../Data/ExecutionPayload.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 6ddc82edbdf..11964709978 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -144,7 +144,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) TransactionDecodingResult transactions = TryGetTransactions(); if (transactions.Error is not null) { - return new BlockDecodingResult(transactions.Error); + return new(transactions.Error); } BlockHeader header = new( @@ -173,7 +173,22 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) BlockAccessListHash = BlockAccessList is null || BlockAccessList.Length == 0 ? null : new(ValueKeccak.Compute(BlockAccessList).Bytes) }; - return new BlockDecodingResult(new Block(header, transactions.Transactions, Array.Empty(), Withdrawals, Rlp.Decode(BlockAccessList))); + BlockAccessList? blockAccessList = null; + + if (BlockAccessList is not null) + { + try + { + blockAccessList = Rlp.Decode(BlockAccessList); + } + catch (RlpException) + { + return new("Could not decode block access list."); + } + } + + Block block = new(header, transactions.Transactions, Array.Empty(), Withdrawals, blockAccessList); + return new(block); } protected virtual Hash256? BuildWithdrawalsRoot() From 94cda6a76f74fa03458a8ea7097b6079605e443f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:24:33 +0100 Subject: [PATCH 111/215] formatting --- src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index e34405e65e6..129fef10d8c 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -199,9 +199,8 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? res = await engineRpcModule.engine_newPayloadV3((ExecutionPayloadV3)executionPayload, [], executionPayload.ParentBeaconBlockRoot); break; case "4": - ExecutionPayloadV3 payload = (ExecutionPayloadV3)executionPayload; byte[]?[] hashes = blobVersionedHashes is null ? null : [.. blobVersionedHashes.Select(x => Bytes.FromHexString(x))]; - res = await engineRpcModule.engine_newPayloadV4(payload, hashes, executionPayload.ParentBeaconBlockRoot, []); + res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; default: Assert.Fail("Invalid blockchain engine test, version not recognised."); From 57437f6557c934d894467321e34b90b392df8fcf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 1 Oct 2025 21:34:09 +0100 Subject: [PATCH 112/215] fix normal blockchain tests --- src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 129fef10d8c..49eb21a5888 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -144,7 +144,6 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? blockchainProcessor.Start(); BlockHeader parentHeader; - (ExecutionPayload, string[]?, string[]?, string?)[] payloads; // Genesis processing using (stateProvider.BeginScope(null)) { @@ -158,8 +157,6 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? // Console.WriteLine(genesisBlock.ToString(Block.Format.Full)); Assert.That(genesisBlock.Header.Hash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash))); - payloads = [.. JsonToEthereumTest.Convert(test.EngineNewPayloads)]; - ManualResetEvent genesisProcessed = new(false); blockTree.NewHeadBlock += (_, args) => @@ -183,6 +180,8 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? } 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) { From b3725c3fdaa1a066c7759c733e7a512f41099fb2 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 1 Oct 2025 21:39:53 +0100 Subject: [PATCH 113/215] set to empty block access list hash if not enabled --- .../Processing/BlockProcessor.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 99e3f324797..82eff985a3e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,17 +178,14 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) - { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); - } + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); } header.Hash = header.CalculateHash(); From 31b2424aa05e218a6f211e6dac81b37445589da7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:48:38 +0100 Subject: [PATCH 114/215] move to newpayloadv5 --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 8 ++++++-- .../Nethermind.Consensus/EngineApiVersions.cs | 1 + .../EngineRpcModule.Amsterdam.cs | 16 +++++++++++++++ .../IEngineRpcModule.Amsterdam.cs | 20 +++++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs create mode 100644 src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 49eb21a5888..e3be2767047 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -186,7 +186,9 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? foreach ((ExecutionPayload executionPayload, string[]? blobVersionedHashes, string[]? validationError, string? newPayloadVersion) in payloads) { ResultWrapper res; - switch (newPayloadVersion ?? "4") + byte[]?[] hashes = blobVersionedHashes is null ? null : [.. blobVersionedHashes.Select(x => Bytes.FromHexString(x))]; + + switch (newPayloadVersion ?? "5") { case "1": res = await engineRpcModule.engine_newPayloadV1(executionPayload); @@ -198,7 +200,9 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? res = await engineRpcModule.engine_newPayloadV3((ExecutionPayloadV3)executionPayload, [], executionPayload.ParentBeaconBlockRoot); break; case "4": - byte[]?[] hashes = blobVersionedHashes is null ? null : [.. blobVersionedHashes.Select(x => Bytes.FromHexString(x))]; + 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: diff --git a/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs b/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs index 145810a7364..c924f581f27 100644 --- a/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs +++ b/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs @@ -9,4 +9,5 @@ public static class EngineApiVersions public const int Shanghai = 2; public const int Cancun = 3; public const int Prague = 4; + public const int Amsterdam = 5; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs new file mode 100644 index 00000000000..ad49280cd17 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading.Tasks; +using Nethermind.Consensus; +using Nethermind.Core.Crypto; +using Nethermind.JsonRpc; +using Nethermind.Merge.Plugin.Data; + +namespace Nethermind.Merge.Plugin; + +public partial class EngineRpcModule : IEngineRpcModule +{ + public Task> engine_newPayloadV5(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot, byte[][]? executionRequests) + => NewPayload(new ExecutionPayloadParams(executionPayload, blobVersionedHashes, parentBeaconBlockRoot, executionRequests), EngineApiVersions.Amsterdam); +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs new file mode 100644 index 00000000000..f480e28e18a --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Threading.Tasks; +using Nethermind.Core.Crypto; +using Nethermind.JsonRpc; +using Nethermind.JsonRpc.Modules; +using Nethermind.Merge.Plugin.Data; + +namespace Nethermind.Merge.Plugin; + +public partial interface IEngineRpcModule : IRpcModule +{ + [JsonRpcMethod( + Description = "Returns the most recent version of an execution payload and fees with respect to the transaction set contained by the mempool.", + IsSharable = true, + IsImplemented = true)] + Task> engine_newPayloadV5(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot, byte[][]? executionRequests); +} From 878b787fb8b542b40dfe6e91ebc4a1c5c31f79dc Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:54:53 +0100 Subject: [PATCH 115/215] trace get account balance --- .../Instructions/EvmInstructions.Environment.cs | 4 ++-- src/Nethermind/Nethermind.Evm/State/IWorldState.cs | 2 +- src/Nethermind/Nethermind.State/TracedAccessWorldState.cs | 6 ++++++ src/Nethermind/Nethermind.State/WorldState.cs | 6 +++--- src/Nethermind/Nethermind.State/WrappedWorldState.cs | 4 ++-- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 38c3a328440..29e1e0e9a88 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -507,7 +507,7 @@ public static EvmExceptionType InstructionBalance(VirtualMachine v // Charge gas for account access. If insufficient gas remains, abort. if (!EvmCalculations.ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; - ref readonly UInt256 result = ref vm.WorldState.GetBalance(address); + UInt256 result = vm.WorldState.GetBalance(address); stack.PushUInt256(in result); return EvmExceptionType.None; @@ -535,7 +535,7 @@ public static EvmExceptionType InstructionSelfBalance(VirtualMachi gasAvailable -= GasCostOf.SelfBalance; // Get balance for currently executing account. - ref readonly UInt256 result = ref vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); + UInt256 result = vm.WorldState.GetBalance(vm.EvmState.Env.ExecutingAccount); stack.PushUInt256(in result); return EvmExceptionType.None; diff --git a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs index 7f7db0c24f2..ff61cd15689 100644 --- a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs @@ -24,7 +24,7 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider IDisposable BeginScope(BlockHeader? baseBlock); bool IsInScope { get; } - new ref readonly UInt256 GetBalance(Address address); + new UInt256 GetBalance(Address address); new ref readonly ValueHash256 GetCodeHash(Address address); bool HasStateForBlock(BlockHeader? baseBlock); diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 7ae54028420..dffce5afeb2 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -97,6 +97,12 @@ public override void Set(in StorageCell storageCell, byte[] newValue) _innerWorldState.Set(storageCell, newValue); } + public override UInt256 GetBalance(Address address) + { + BlockAccessList.AddAccountRead(address); + return _innerWorldState.GetBalance(address); + } + public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { UInt256 before = _innerWorldState.GetBalance(address); diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 78058655f61..0c530b40130 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -275,16 +275,16 @@ public IDisposable BeginScope(BlockHeader? baseBlock) public bool IsInScope => _isInScope; - public ref readonly UInt256 GetBalance(Address address) + public UInt256 GetBalance(Address address) { DebugGuardInScope(); - return ref _stateProvider.GetBalance(address); + return _stateProvider.GetBalance(address); } public ValueHash256 GetStorageRoot(Address address) { DebugGuardInScope(); - if (address == null) throw new ArgumentNullException(nameof(address)); + ArgumentNullException.ThrowIfNull(address); return _stateProvider.GetStorageRoot(address); } diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index cfbe5872ca1..44722c5fd15 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -71,8 +71,8 @@ public virtual ReadOnlySpan Get(in StorageCell storageCell) public ArrayPoolList? GetAccountChanges() => _innerWorldState.GetAccountChanges(); - public ref readonly UInt256 GetBalance(Address address) - => ref _innerWorldState.GetBalance(address); + public virtual UInt256 GetBalance(Address address) + => _innerWorldState.GetBalance(address); public byte[]? GetCode(Address address) => _innerWorldState.GetCode(address); From 89bf5f92a4782755e90ad6c35f94a9bbc44f3a1e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:06:15 +0100 Subject: [PATCH 116/215] codehash --- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 2 +- .../Instructions/EvmInstructions.Environment.cs | 2 +- src/Nethermind/Nethermind.Evm/State/IWorldState.cs | 2 +- src/Nethermind/Nethermind.State/TracedAccessWorldState.cs | 6 ++++++ src/Nethermind/Nethermind.State/WorldState.cs | 4 ++-- src/Nethermind/Nethermind.State/WrappedWorldState.cs | 4 ++-- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 163029357db..c8dd9d0a207 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -49,7 +49,7 @@ public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IR private ICodeInfo InternalGetCachedCode(Address codeSource, IReleaseSpec vmSpec) { - ref readonly ValueHash256 codeHash = ref _worldState.GetCodeHash(codeSource); + ValueHash256 codeHash = _worldState.GetCodeHash(codeSource); return InternalGetCachedCode(_worldState, in codeHash, vmSpec); } diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs index 29e1e0e9a88..2c79ebbb965 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Environment.cs @@ -575,7 +575,7 @@ public static EvmExceptionType InstructionExtCodeHash(VirtualMachi else { // Otherwise, push the account's code hash. - ref readonly ValueHash256 hash = ref state.GetCodeHash(address); + ValueHash256 hash = state.GetCodeHash(address); stack.Push32Bytes(in hash); } diff --git a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs index ff61cd15689..29f3afb96ab 100644 --- a/src/Nethermind/Nethermind.Evm/State/IWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/IWorldState.cs @@ -25,7 +25,7 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider IDisposable BeginScope(BlockHeader? baseBlock); bool IsInScope { get; } new UInt256 GetBalance(Address address); - new ref readonly ValueHash256 GetCodeHash(Address address); + new ValueHash256 GetCodeHash(Address address); bool HasStateForBlock(BlockHeader? baseBlock); /// diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index dffce5afeb2..abe3ed5e9f9 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -103,6 +103,12 @@ public override UInt256 GetBalance(Address address) return _innerWorldState.GetBalance(address); } + public override ValueHash256 GetCodeHash(Address address) + { + BlockAccessList.AddAccountRead(address); + return _innerWorldState.GetCodeHash(address); + } + public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { UInt256 before = _innerWorldState.GetBalance(address); diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 0c530b40130..84d47ce0652 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -300,10 +300,10 @@ public byte[] GetCode(in ValueHash256 codeHash) return _stateProvider.GetCode(in codeHash); } - public ref readonly ValueHash256 GetCodeHash(Address address) + public ValueHash256 GetCodeHash(Address address) { DebugGuardInScope(); - return ref _stateProvider.GetCodeHash(address); + return _stateProvider.GetCodeHash(address); } ValueHash256 IAccountStateProvider.GetCodeHash(Address address) diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.State/WrappedWorldState.cs index 44722c5fd15..662a26727f4 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.State/WrappedWorldState.cs @@ -80,8 +80,8 @@ public virtual UInt256 GetBalance(Address address) public byte[]? GetCode(in ValueHash256 codeHash) => _innerWorldState.GetCode(codeHash); - public ref readonly ValueHash256 GetCodeHash(Address address) - => ref _innerWorldState.GetCodeHash(address); + public virtual ValueHash256 GetCodeHash(Address address) + => _innerWorldState.GetCodeHash(address); public byte[] GetOriginal(in StorageCell storageCell) => _innerWorldState.GetOriginal(storageCell); From c046b924980cf4c9c240e2cca01f937b69c5cd80 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:46:00 +0100 Subject: [PATCH 117/215] create account & selfdestruct edge cases --- .../BlockAccessLists/BlockAccessList.cs | 78 ++++++++++++++++++- .../TracedAccessWorldState.cs | 24 ++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 1e79ad32215..5bd8bac1986 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Nethermind.Core.Extensions; using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; @@ -50,7 +51,7 @@ public void IncrementBlockAccessIndex() Index++; } - public void AddBalanceChange(Address address, UInt256? before, UInt256? after) + public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { if (address == Address.SystemUser) { @@ -60,7 +61,7 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) BalanceChange balanceChange = new() { BlockAccessIndex = Index, - PostBalance = after!.Value + PostBalance = after }; if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) @@ -70,12 +71,24 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) } // don't add zero balance transfers, but add empty account changes - if ((before ?? 0) == after) + if (before == after) { return; } SortedList balanceChanges = accountChanges.BalanceChanges; + + // selfdestruct edge case + // todo: check this couldn't happen in any other case than selfdestruct + if (after == 0 && !WasFundedPreTx(address, before)) + { + if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) + { + balanceChanges.RemoveAt(balanceChanges.Count - 1); + } + return; + } + if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { _changes.Push(new() @@ -84,6 +97,7 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) Type = ChangeType.BalanceChange, PreviousValue = balanceChanges.Last().Value }); + balanceChanges.RemoveAt(balanceChanges.Count - 1); } else @@ -92,9 +106,11 @@ public void AddBalanceChange(Address address, UInt256? before, UInt256? after) { Address = address, Type = ChangeType.BalanceChange, - PreviousValue = null + PreviousValue = null, + WasPreFunded = before != 0 }); } + balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } @@ -241,6 +257,25 @@ public void AddStorageRead(in StorageCell storageCell) } } + public readonly void DeleteAccount(Address address) + { + if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) + { + accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + return; + } + + foreach (byte[] key in accountChanges.StorageChanges.Keys) + { + accountChanges.StorageReads.Add(new(Bytes32.Wrap(key))); + } + + accountChanges.StorageChanges.Clear(); + accountChanges.NonceChanges.Clear(); + accountChanges.CodeChanges.Clear(); + } + private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) { Span newValue = stackalloc byte[32]; @@ -340,6 +375,40 @@ public readonly void Restore(int snapshot) public override readonly string? ToString() => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; + private readonly bool WasFundedPreTx(Address address, UInt256 before) + { + AccountChanges accountChanges = _accountChanges[address]; + int count = accountChanges.BalanceChanges.Count; + + if (count == 0) + { + // first balance change of block + // return balance prior to this selfdestruct instruction + return before == 0; + } + + foreach (BalanceChange balanceChange in accountChanges.BalanceChanges.Values.Reverse()) + { + if (balanceChange.BlockAccessIndex != Index) + { + // balance changed in previous tx in block + return balanceChange.PostBalance == 0; + } + } + + // balance only changed within this transaction + foreach (Change change in _changes) + { + if (change.Type == ChangeType.BalanceChange && change.Address == address && change.PreviousValue is null) + { + // first change of this transaction & block + return change.WasPreFunded!.Value; + } + } + + throw new Exception("Error calculting pre tx balance"); + } + private enum ChangeType { BalanceChange = 0, @@ -353,5 +422,6 @@ private readonly struct Change public byte[]? Slot { get; init; } public ChangeType Type { get; init; } public IIndexedChange? PreviousValue { get; init; } + public bool? WasPreFunded { get; init; } } } diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index abe3ed5e9f9..3a81f7b47ac 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -121,6 +121,30 @@ public override void SubtractFromBalance(Address address, in UInt256 balanceChan } } + public override void DeleteAccount(Address address) + { + BlockAccessList.DeleteAccount(address); + _innerWorldState.DeleteAccount(address); + } + + public override void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) + { + BlockAccessList.AddAccountRead(address); + if (balance != 0) + { + BlockAccessList.AddBalanceChange(address, 0, balance); + } + _innerWorldState.CreateAccount(address, balance, nonce); + } + + public override void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default) + { + if (!_innerWorldState.AccountExists(address)) + { + CreateAccount(address, balance, nonce); + } + } + public override bool TryGetAccount(Address address, out AccountStruct account) { if (Enabled) From f9365464eb152323c0d476a7e32a16eb9ade4370 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:23:36 +0100 Subject: [PATCH 118/215] try only setting null hash when enabled --- .../Processing/BlockProcessor.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 82eff985a3e..b13323d7425 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using System.Threading; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; @@ -178,16 +179,20 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + if (_tracedAccessWorldState is not null) + { + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + } } + header.Hash = header.CalculateHash(); return receipts; From be3fd21c4380b4cd92a389e457451f75538f8cc1 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:44:23 +0100 Subject: [PATCH 119/215] undo --- .../Processing/BlockProcessor.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index b13323d7425..82eff985a3e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Threading; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; @@ -179,19 +178,15 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - if (_tracedAccessWorldState is not null) - { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); - } + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); } - header.Hash = header.CalculateHash(); From 455efa718c0ceb6b54d15f33e85fa50d1622e149 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:23:36 +0100 Subject: [PATCH 120/215] try only setting null hash when enabled --- .../Processing/BlockProcessor.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 82eff985a3e..b13323d7425 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using System.Threading; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; @@ -178,16 +179,20 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + if (_tracedAccessWorldState is not null) + { + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + } } + header.Hash = header.CalculateHash(); return receipts; From c77d2eee4b051af617ebafd7a5d6d50cff275bc0 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:44:23 +0100 Subject: [PATCH 121/215] undo --- .../Processing/BlockProcessor.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index b13323d7425..82eff985a3e 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Threading; using Nethermind.Blockchain; using Nethermind.Blockchain.BeaconBlockRoot; @@ -179,19 +178,15 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - if (_tracedAccessWorldState is not null) - { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); - } + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); } - header.Hash = header.CalculateHash(); From 29537bb9f7fd456ef91d191b38e675dde2cc5f1f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:18:26 +0100 Subject: [PATCH 122/215] set genesis bal hash in chainspec loader --- .../Processing/BlockProcessor.cs | 17 +++++++------ .../ChainSpecStyle/ChainSpecLoader.cs | 24 +++++++++++++------ 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 82eff985a3e..2567ad405d7 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,14 +178,17 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + if (spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = new(ValueKeccak.Compute([Rlp.NullObjectByte])); + if (_tracedAccessWorldState is not null) + { + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; + } } header.Hash = header.CalculateHash(); diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 9209f1d6522..5926a0fed4c 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -340,16 +340,23 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec genesisHeader.StateRoot = stateRoot; genesisHeader.TxRoot = Keccak.EmptyTreeHash; genesisHeader.BaseFeePerGas = baseFee; + bool withdrawalsEnabled = chainSpecJson.Params.Eip4895TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip4895TransitionTimestamp; bool depositsEnabled = chainSpecJson.Params.Eip6110TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip6110TransitionTimestamp; bool withdrawalRequestsEnabled = chainSpecJson.Params.Eip7002TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip7002TransitionTimestamp; bool consolidationRequestsEnabled = chainSpecJson.Params.Eip7251TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip7251TransitionTimestamp; + bool blockAccessListsEnabled = chainSpecJson.Params.Eip7928TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip7928TransitionTimestamp; + if (withdrawalsEnabled) + { genesisHeader.WithdrawalsRoot = Keccak.EmptyTreeHash; + } var requestsEnabled = depositsEnabled || withdrawalRequestsEnabled || consolidationRequestsEnabled; if (requestsEnabled) + { genesisHeader.RequestsHash = ExecutionRequestExtensions.EmptyRequestsHash; + } bool isEip4844Enabled = chainSpecJson.Params.Eip4844TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip4844TransitionTimestamp; if (isEip4844Enabled) @@ -369,16 +376,19 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec genesisHeader.ReceiptsRoot = Keccak.EmptyTreeHash; } + if (blockAccessListsEnabled) + { + genesisHeader.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; + } + genesisHeader.AuRaStep = step; genesisHeader.AuRaSignature = auRaSignature; - chainSpec.Genesis = !withdrawalsEnabled - ? new Block(genesisHeader) - : new Block( - genesisHeader, - Array.Empty(), - Array.Empty(), - Array.Empty()); + chainSpec.Genesis = !blockAccessListsEnabled ? + (!withdrawalsEnabled + ? new Block(genesisHeader) + : new Block(genesisHeader, [], [], [])) + : new Block(genesisHeader, [], [], [], new()); } private static void LoadAllocations(ChainSpecJson chainSpecJson, ChainSpec chainSpec) From f0863b4137604bd75617b81387b7ec462de57481 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:28:58 +0100 Subject: [PATCH 123/215] always set bal hash --- .../Processing/BlockProcessor.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 2567ad405d7..2464c5686eb 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,17 +178,14 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - if (_tracedAccessWorldState is not null) - { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; - } + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } + else + { + header.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; } header.Hash = header.CalculateHash(); From 6ed885f7b75c0bfaab579986d9d4c6cd39abd496 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:55:44 +0100 Subject: [PATCH 124/215] add to chainspecbasedprovider --- .../ChainSpecBasedSpecProvider.cs | 2 ++ .../ChainSpecStyle/ChainSpecLoader.cs | 23 ++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 0b4fdc3c8fa..f6fe905de0c 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -293,6 +293,8 @@ protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releas releaseSpec.IsRip7728Enabled = (chainSpec.Parameters.Rip7728TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.IsEip7928Enabled = (chainSpec.Parameters.Eip7928TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + foreach (IChainSpecEngineParameters item in _chainSpec.EngineChainSpecParametersProvider .AllChainSpecParameters) { diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 5926a0fed4c..b7512dc20a8 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -329,17 +329,18 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec 0, (long)gasLimit, timestamp, - extraData); - - genesisHeader.Author = beneficiary; - genesisHeader.Hash = Keccak.Zero; // need to run the block to know the actual hash - genesisHeader.Bloom = Bloom.Empty; - genesisHeader.MixHash = mixHash; - genesisHeader.Nonce = (ulong)nonce; - genesisHeader.ReceiptsRoot = Keccak.EmptyTreeHash; - genesisHeader.StateRoot = stateRoot; - genesisHeader.TxRoot = Keccak.EmptyTreeHash; - genesisHeader.BaseFeePerGas = baseFee; + extraData) + { + Author = beneficiary, + Hash = Keccak.Zero, // need to run the block to know the actual hash + Bloom = Bloom.Empty, + MixHash = mixHash, + Nonce = (ulong)nonce, + ReceiptsRoot = Keccak.EmptyTreeHash, + StateRoot = stateRoot, + TxRoot = Keccak.EmptyTreeHash, + BaseFeePerGas = baseFee + }; bool withdrawalsEnabled = chainSpecJson.Params.Eip4895TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip4895TransitionTimestamp; bool depositsEnabled = chainSpecJson.Params.Eip6110TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip6110TransitionTimestamp; From cdcf39bec21924de685692b88c5551a75f19e069 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 6 Oct 2025 16:58:11 +0100 Subject: [PATCH 125/215] set to empty hash if genesis --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 2464c5686eb..69d1bc18248 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,7 +178,7 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled && !block.IsGenesis) { body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); From ccfac269d01049c2d67c19a6b365e704ef461d58 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:32:08 +0100 Subject: [PATCH 126/215] don't record balance change for unchanged balance change in account --- .../BlockAccessLists/BlockAccessList.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 5bd8bac1986..eba9399b243 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Nethermind.Core.Extensions; using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; @@ -78,9 +77,8 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) SortedList balanceChanges = accountChanges.BalanceChanges; - // selfdestruct edge case - // todo: check this couldn't happen in any other case than selfdestruct - if (after == 0 && !WasFundedPreTx(address, before)) + // balance change edge case + if (!HasChangedDuringTx(address, before, after)) { if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { @@ -107,7 +105,7 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) Address = address, Type = ChangeType.BalanceChange, PreviousValue = null, - WasPreFunded = before != 0 + PreTxBalance = before }); } @@ -375,7 +373,7 @@ public readonly void Restore(int snapshot) public override readonly string? ToString() => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; - private readonly bool WasFundedPreTx(Address address, UInt256 before) + private readonly bool HasChangedDuringTx(Address address, UInt256 beforeInstr, UInt256 afterInstr) { AccountChanges accountChanges = _accountChanges[address]; int count = accountChanges.BalanceChanges.Count; @@ -383,8 +381,8 @@ private readonly bool WasFundedPreTx(Address address, UInt256 before) if (count == 0) { // first balance change of block - // return balance prior to this selfdestruct instruction - return before == 0; + // return balance prior to this instruction + return beforeInstr != afterInstr; } foreach (BalanceChange balanceChange in accountChanges.BalanceChanges.Values.Reverse()) @@ -392,7 +390,7 @@ private readonly bool WasFundedPreTx(Address address, UInt256 before) if (balanceChange.BlockAccessIndex != Index) { // balance changed in previous tx in block - return balanceChange.PostBalance == 0; + return balanceChange.PostBalance != afterInstr; } } @@ -402,7 +400,7 @@ private readonly bool WasFundedPreTx(Address address, UInt256 before) if (change.Type == ChangeType.BalanceChange && change.Address == address && change.PreviousValue is null) { // first change of this transaction & block - return change.WasPreFunded!.Value; + return change.PreTxBalance!.Value != afterInstr; } } @@ -422,6 +420,6 @@ private readonly struct Change public byte[]? Slot { get; init; } public ChangeType Type { get; init; } public IIndexedChange? PreviousValue { get; init; } - public bool? WasPreFunded { get; init; } + public UInt256? PreTxBalance { get; init; } } } From 6cd23a52f872b24aa4ac390b8a3a21b437a8d0cd Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 8 Oct 2025 15:37:32 +0100 Subject: [PATCH 127/215] check enabled for traced access world state --- .../TracedAccessWorldState.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs index 3a81f7b47ac..ab442d732d4 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs @@ -99,40 +99,51 @@ public override void Set(in StorageCell storageCell, byte[] newValue) public override UInt256 GetBalance(Address address) { - BlockAccessList.AddAccountRead(address); + if (Enabled) + { + BlockAccessList.AddAccountRead(address); + } return _innerWorldState.GetBalance(address); } public override ValueHash256 GetCodeHash(Address address) { - BlockAccessList.AddAccountRead(address); + if (Enabled) + { + BlockAccessList.AddAccountRead(address); + } return _innerWorldState.GetCodeHash(address); } public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { - UInt256 before = _innerWorldState.GetBalance(address); - UInt256 after = before - balanceChange; - _innerWorldState.SubtractFromBalance(address, balanceChange, spec); - if (Enabled) { + UInt256 before = _innerWorldState.GetBalance(address); + UInt256 after = before - balanceChange; BlockAccessList.AddBalanceChange(address, before, after); } + _innerWorldState.SubtractFromBalance(address, balanceChange, spec); } public override void DeleteAccount(Address address) { - BlockAccessList.DeleteAccount(address); + if (Enabled) + { + BlockAccessList.DeleteAccount(address); + } _innerWorldState.DeleteAccount(address); } public override void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default) { - BlockAccessList.AddAccountRead(address); - if (balance != 0) + if (Enabled) { - BlockAccessList.AddBalanceChange(address, 0, balance); + BlockAccessList.AddAccountRead(address); + if (balance != 0) + { + BlockAccessList.AddBalanceChange(address, 0, balance); + } } _innerWorldState.CreateAccount(address, balance, nonce); } @@ -154,6 +165,14 @@ public override bool TryGetAccount(Address address, out AccountStruct account) return _innerWorldState.TryGetAccount(address, out account); } + public void AddAccountRead(Address address) + { + if (Enabled) + { + BlockAccessList.AddAccountRead(address); + } + } + public override void Restore(Snapshot snapshot) { if (Enabled) From 16638b0a6884896e7d335187ece483e79d752177 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 9 Oct 2025 12:32:43 +0100 Subject: [PATCH 128/215] precompile handling --- .../Nethermind.Blockchain.Test/TransactionsExecutorTests.cs | 3 ++- .../Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs | 3 +-- src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs | 6 ++++++ src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj | 1 + .../State}/IPreBlockCaches.cs | 2 +- .../State}/PreBlockCaches.cs | 3 +-- .../State}/TracedAccessWorldState.cs | 3 +-- .../State}/WrappedWorldState.cs | 2 +- .../Nethermind.State/Healing/HealingWorldState.cs | 1 + 9 files changed, 15 insertions(+), 9 deletions(-) rename src/Nethermind/{Nethermind.State => Nethermind.Evm/State}/IPreBlockCaches.cs (86%) rename src/Nethermind/{Nethermind.State => Nethermind.Evm/State}/PreBlockCaches.cs (97%) rename src/Nethermind/{Nethermind.State => Nethermind.Evm/State}/TracedAccessWorldState.cs (99%) rename src/Nethermind/{Nethermind.State => Nethermind.Evm/State}/WrappedWorldState.cs (99%) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/TransactionsExecutorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/TransactionsExecutorTests.cs index f61ef983831..8e36c8c6f49 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/TransactionsExecutorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/TransactionsExecutorTests.cs @@ -367,6 +367,7 @@ public void BlockProductionTransactionsExecutor_calculates_block_size_using_prop public class WorldStateStab() : WorldState(Substitute.For(), Substitute.For(), LimboLogs.Instance), IWorldState { // we cannot mock ref methods - ref readonly UInt256 IWorldState.GetBalance(Address address) => ref UInt256.MaxValue; + // ref readonly UInt256 IWorldState.GetBalance(Address address) => ref UInt256.MaxValue; + UInt256 IWorldState.GetBalance(Address address) => UInt256.MaxValue; } } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs index a62b840652a..8775f11d72a 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/IBlockCachePreWarmer.cs @@ -5,10 +5,9 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Core; -using Nethermind.Core.Crypto; using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; -using Nethermind.State; +using Nethermind.Evm.State; namespace Nethermind.Consensus.Processing; diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index c8dd9d0a207..8f3a51dbb7d 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -21,11 +21,13 @@ public class CodeInfoRepository : ICodeInfoRepository private static readonly CodeLruCache _codeCache = new(); private readonly FrozenDictionary _localPrecompiles; private readonly IWorldState _worldState; + private readonly TracedAccessWorldState? _tracedAccessWorldState; public CodeInfoRepository(IWorldState worldState, IPrecompileProvider precompileProvider) { _localPrecompiles = precompileProvider.GetPrecompiles(); _worldState = worldState; + _tracedAccessWorldState = _worldState as TracedAccessWorldState; } public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IReleaseSpec vmSpec, out Address? delegationAddress) @@ -33,6 +35,10 @@ public ICodeInfo GetCachedCodeInfo(Address codeSource, bool followDelegation, IR delegationAddress = null; if (vmSpec.IsPrecompile(codeSource)) // _localPrecompiles have to have all precompiles { + if (_tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled) + { + _tracedAccessWorldState.AddAccountRead(codeSource); + } return _localPrecompiles[codeSource]; } diff --git a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj index 968541ac1aa..37f9f5a0895 100644 --- a/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj +++ b/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj @@ -9,5 +9,6 @@ + diff --git a/src/Nethermind/Nethermind.State/IPreBlockCaches.cs b/src/Nethermind/Nethermind.Evm/State/IPreBlockCaches.cs similarity index 86% rename from src/Nethermind/Nethermind.State/IPreBlockCaches.cs rename to src/Nethermind/Nethermind.Evm/State/IPreBlockCaches.cs index 1526663b148..2e29278e9c6 100644 --- a/src/Nethermind/Nethermind.State/IPreBlockCaches.cs +++ b/src/Nethermind/Nethermind.Evm/State/IPreBlockCaches.cs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -namespace Nethermind.State; +namespace Nethermind.Evm.State; public interface IPreBlockCaches { diff --git a/src/Nethermind/Nethermind.State/PreBlockCaches.cs b/src/Nethermind/Nethermind.Evm/State/PreBlockCaches.cs similarity index 97% rename from src/Nethermind/Nethermind.State/PreBlockCaches.cs rename to src/Nethermind/Nethermind.Evm/State/PreBlockCaches.cs index 3da7b0036bc..672b0dfbfe5 100644 --- a/src/Nethermind/Nethermind.State/PreBlockCaches.cs +++ b/src/Nethermind/Nethermind.Evm/State/PreBlockCaches.cs @@ -6,12 +6,11 @@ using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Core.Collections; -using Nethermind.Evm.State; using Nethermind.Trie; using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; -namespace Nethermind.State; +namespace Nethermind.Evm.State; public class PreBlockCaches { diff --git a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs similarity index 99% rename from src/Nethermind/Nethermind.State/TracedAccessWorldState.cs rename to src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index ab442d732d4..dbf7dc0f2ba 100644 --- a/src/Nethermind/Nethermind.State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -6,10 +6,9 @@ using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; -using Nethermind.Evm.State; using Nethermind.Int256; -namespace Nethermind.State; +namespace Nethermind.Evm.State; public class TracedAccessWorldState(IWorldState innerWorldState) : WrappedWorldState(innerWorldState), IPreBlockCaches { diff --git a/src/Nethermind/Nethermind.State/WrappedWorldState.cs b/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs similarity index 99% rename from src/Nethermind/Nethermind.State/WrappedWorldState.cs rename to src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs index 662a26727f4..5977f182de0 100644 --- a/src/Nethermind/Nethermind.State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs @@ -11,7 +11,7 @@ using Nethermind.Evm.Tracing.State; using Nethermind.Int256; -namespace Nethermind.State; +namespace Nethermind.Evm.State; public class WrappedWorldState(IWorldState innerWorldState) : IWorldState { diff --git a/src/Nethermind/Nethermind.State/Healing/HealingWorldState.cs b/src/Nethermind/Nethermind.State/Healing/HealingWorldState.cs index 78c6097e413..b6170591623 100644 --- a/src/Nethermind/Nethermind.State/Healing/HealingWorldState.cs +++ b/src/Nethermind/Nethermind.State/Healing/HealingWorldState.cs @@ -5,6 +5,7 @@ using Nethermind.Core; using Nethermind.Logging; using Nethermind.Trie.Pruning; +using Nethermind.Evm.State; namespace Nethermind.State.Healing; From 0d1d43abbb23d34960a085f7906efef24efe88cb Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 9 Oct 2025 15:50:34 +0100 Subject: [PATCH 129/215] don't add empty bal hash to genesis when disabled --- .../Processing/BlockProcessor.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 69d1bc18248..f45661e52ea 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -178,14 +178,17 @@ protected virtual TxReceipt[] ProcessBlock( header.StateRoot = _stateProvider.StateRoot; } - if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled && !block.IsGenesis) + if (_tracedAccessWorldState is not null && spec.BlockLevelAccessListsEnabled) { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); - } - else - { - header.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; + if (block.IsGenesis) + { + header.BlockAccessListHash = Keccak.OfAnEmptySequenceRlp; + } + else + { + body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + } } header.Hash = header.CalculateHash(); From 9c38e809e2fd93f2848cf319546dfad47420bc96 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 9 Oct 2025 18:20:26 +0100 Subject: [PATCH 130/215] add 7702 authority on failed delegation --- .../TransactionProcessing/TransactionProcessor.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index f1c084fa67b..4dcc4f0bb89 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -40,6 +40,7 @@ public abstract class TransactionProcessorBase : ITransactionProcessor private readonly ICodeInfoRepository _codeInfoRepository; private SystemTransactionProcessor? _systemTransactionProcessor; private readonly ILogManager _logManager; + private readonly TracedAccessWorldState? _tracedAccessWorldState; [Flags] protected enum ExecutionOptions @@ -93,6 +94,7 @@ protected TransactionProcessorBase( WorldState = worldState; VirtualMachine = virtualMachine; _codeInfoRepository = codeInfoRepository; + _tracedAccessWorldState = worldState as TracedAccessWorldState; Ecdsa = new EthereumEcdsa(specProvider.ChainId); _logManager = logManager; @@ -248,11 +250,17 @@ private int ProcessDelegations(Transaction tx, IReleaseSpec spec, in StackAccess int refunds = 0; foreach (AuthorizationTuple authTuple in tx.AuthorizationList) { - Address authority = (authTuple.Authority ??= Ecdsa.RecoverAddress(authTuple)); + authTuple.Authority ??= Ecdsa.RecoverAddress(authTuple); + Address authority = authTuple.Authority; if (!IsValidForExecution(authTuple, accessTracker, out string? error)) { if (Logger.IsDebug) Logger.Debug($"Delegation {authTuple} is invalid with error: {error}"); + + if (_tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled) + { + _tracedAccessWorldState.AddAccountRead(authority); + } } else { From a714e194239f12f4cfa940688b2c6c72ad7bbe04 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 10 Oct 2025 14:55:38 +0100 Subject: [PATCH 131/215] engine rpc amsterdam --- .../EngineRpcModule.Amsterdam.cs | 3 +++ .../Handlers/EngineRpcCapabilitiesProvider.cs | 13 ++++++------- .../Handlers/NewPayloadHandler.cs | 3 +++ .../IEngineRpcModule.Amsterdam.cs | 6 ++++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs index ad49280cd17..1fa595a8c0f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Amsterdam.cs @@ -11,6 +11,9 @@ namespace Nethermind.Merge.Plugin; public partial class EngineRpcModule : IEngineRpcModule { + public Task> engine_getPayloadV6(byte[] payloadId) + => _getPayloadHandlerV5.HandleAsync(payloadId); + public Task> engine_newPayloadV5(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot, byte[][]? executionRequests) => NewPayload(new ExecutionPayloadParams(executionPayload, blobVersionedHashes, parentBeaconBlockRoot, executionRequests), EngineApiVersions.Amsterdam); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs index 123ddd1a6f3..c552b1f80ea 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs @@ -9,21 +9,16 @@ namespace Nethermind.HealthChecks; -public class EngineRpcCapabilitiesProvider : IRpcCapabilitiesProvider +public class EngineRpcCapabilitiesProvider(ISpecProvider specProvider) : IRpcCapabilitiesProvider { private readonly ConcurrentDictionary _capabilities = new(); - private readonly ISpecProvider _specProvider; - public EngineRpcCapabilitiesProvider(ISpecProvider specProvider) - { - _specProvider = specProvider; - } public IReadOnlyDictionary GetEngineCapabilities() { if (_capabilities.IsEmpty) { - IReleaseSpec spec = _specProvider.GetFinalSpec(); + IReleaseSpec spec = specProvider.GetFinalSpec(); // The Merge _capabilities[nameof(IEngineRpcModule.engine_exchangeTransitionConfigurationV1)] = (true, false); @@ -53,6 +48,10 @@ public EngineRpcCapabilitiesProvider(ISpecProvider specProvider) // Osaka _capabilities[nameof(IEngineRpcModule.engine_getPayloadV5)] = (spec.IsEip7594Enabled, spec.IsEip7594Enabled); _capabilities[nameof(IEngineRpcModule.engine_getBlobsV2)] = (spec.IsEip7594Enabled, false); + + // Amsterdam + _capabilities[nameof(IEngineRpcModule.engine_getPayloadV6)] = (spec.IsEip7928Enabled, spec.IsEip7928Enabled); + _capabilities[nameof(IEngineRpcModule.engine_newPayloadV5)] = (spec.IsEip7928Enabled, spec.IsEip7928Enabled); } return _capabilities; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs index 7d32ef7f27a..690d1b4c1f4 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs @@ -121,6 +121,9 @@ public async Task> HandleAsync(ExecutionPayload r return NewPayloadV1Result.Invalid(null, $"Block {request} could not be parsed as a block: {decodingResult.Error}"); } + Console.WriteLine("suggested block:"); + Console.WriteLine(block.BlockAccessList); + string requestStr = $"New Block: {request}"; if (_logger.IsInfo) { diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs index f480e28e18a..3e40d959032 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Amsterdam.cs @@ -16,5 +16,11 @@ public partial interface IEngineRpcModule : IRpcModule Description = "Returns the most recent version of an execution payload and fees with respect to the transaction set contained by the mempool.", IsSharable = true, IsImplemented = true)] + public Task> engine_getPayloadV6(byte[] payloadId); + + [JsonRpcMethod( + Description = "Verifies the payload according to the execution environment rules and returns the verification status and hash of the last valid block.", + IsSharable = true, + IsImplemented = true)] Task> engine_newPayloadV5(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot, byte[][]? executionRequests); } From ba184ffca847fc7998ac0f33e4ef073e748b80ed Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 10 Oct 2025 14:57:06 +0100 Subject: [PATCH 132/215] comment logs --- .../Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs index 690d1b4c1f4..9e1e2000377 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/NewPayloadHandler.cs @@ -121,8 +121,8 @@ public async Task> HandleAsync(ExecutionPayload r return NewPayloadV1Result.Invalid(null, $"Block {request} could not be parsed as a block: {decodingResult.Error}"); } - Console.WriteLine("suggested block:"); - Console.WriteLine(block.BlockAccessList); + // Console.WriteLine("suggested block:"); + // Console.WriteLine(block.BlockAccessList); string requestStr = $"New Block: {request}"; if (_logger.IsInfo) From f07ad62308321ac85186e31e7959089c336ee39f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:09:50 +0100 Subject: [PATCH 133/215] fix 7702 failed delgation edge case --- .../TransactionProcessor.cs | 95 +++++++++++-------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 4dcc4f0bb89..5e25c88ea4f 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -253,11 +253,12 @@ private int ProcessDelegations(Transaction tx, IReleaseSpec spec, in StackAccess authTuple.Authority ??= Ecdsa.RecoverAddress(authTuple); Address authority = authTuple.Authority; - if (!IsValidForExecution(authTuple, accessTracker, out string? error)) + AuthorizationTupleResult res = IsValidForExecution(authTuple, accessTracker, spec, out string? error); + if (res != AuthorizationTupleResult.Valid) { if (Logger.IsDebug) Logger.Debug($"Delegation {authTuple} is invalid with error: {error}"); - if (_tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled) + if (_tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled && IncludeAccountRead(res)) { _tracedAccessWorldState.AddAccountRead(authority); } @@ -279,52 +280,66 @@ private int ProcessDelegations(Transaction tx, IReleaseSpec spec, in StackAccess } return refunds; + } - bool IsValidForExecution( - AuthorizationTuple authorizationTuple, - StackAccessTracker accessTracker, - [NotNullWhen(false)] out string? error) - { - if (authorizationTuple.ChainId != 0 && SpecProvider.ChainId != authorizationTuple.ChainId) - { - error = $"Chain id ({authorizationTuple.ChainId}) does not match."; - return false; - } + private bool IncludeAccountRead(AuthorizationTupleResult res) + => res == AuthorizationTupleResult.IncorrectNonce || res == AuthorizationTupleResult.InvalidAsCodeDeployed; - if (authorizationTuple.Nonce == ulong.MaxValue) - { - error = $"Nonce ({authorizationTuple.Nonce}) must be less than 2**64 - 1."; - return false; - } + private enum AuthorizationTupleResult + { + Valid, + IncorrectNonce, + InvalidNonce, + InvalidChainId, + InvalidSignature, + InvalidAsCodeDeployed + } - UInt256 s = new(authorizationTuple.AuthoritySignature.SAsSpan, isBigEndian: true); - if (authorizationTuple.Authority is null - || s > Secp256K1Curve.HalfN - //V minus the offset can only be 1 or 0 since eip-155 does not apply to Setcode signatures - || authorizationTuple.AuthoritySignature.V - Signature.VOffset > 1) - { - error = "Bad signature."; - return false; - } + private AuthorizationTupleResult IsValidForExecution( + AuthorizationTuple authorizationTuple, + StackAccessTracker accessTracker, + IReleaseSpec spec, + [NotNullWhen(false)] out string? error) + { + if (authorizationTuple.ChainId != 0 && SpecProvider.ChainId != authorizationTuple.ChainId) + { + error = $"Chain id ({authorizationTuple.ChainId}) does not match."; + return AuthorizationTupleResult.InvalidChainId; + } - accessTracker.WarmUp(authorizationTuple.Authority); + if (authorizationTuple.Nonce == ulong.MaxValue) + { + error = $"Nonce ({authorizationTuple.Nonce}) must be less than 2**64 - 1."; + return AuthorizationTupleResult.InvalidNonce; + } - if (WorldState.HasCode(authorizationTuple.Authority) && !_codeInfoRepository.TryGetDelegation(authorizationTuple.Authority, spec, out _)) - { - error = $"Authority ({authorizationTuple.Authority}) has code deployed."; - return false; - } + UInt256 s = new(authorizationTuple.AuthoritySignature.SAsSpan, isBigEndian: true); + if (authorizationTuple.Authority is null + || s > Secp256K1Curve.HalfN + //V minus the offset can only be 1 or 0 since eip-155 does not apply to Setcode signatures + || authorizationTuple.AuthoritySignature.V - Signature.VOffset > 1) + { + error = "Bad signature."; + return AuthorizationTupleResult.InvalidSignature; + } - UInt256 authNonce = WorldState.GetNonce(authorizationTuple.Authority); - if (authNonce != authorizationTuple.Nonce) - { - error = $"Skipping tuple in authorization_list because nonce is set to {authorizationTuple.Nonce}, but authority ({authorizationTuple.Authority}) has {authNonce}."; - return false; - } + accessTracker.WarmUp(authorizationTuple.Authority); + + if (WorldState.HasCode(authorizationTuple.Authority) && !_codeInfoRepository.TryGetDelegation(authorizationTuple.Authority, spec, out _)) + { + error = $"Authority ({authorizationTuple.Authority}) has code deployed."; + return AuthorizationTupleResult.InvalidAsCodeDeployed; + } - error = null; - return true; + UInt256 authNonce = WorldState.GetNonce(authorizationTuple.Authority); + if (authNonce != authorizationTuple.Nonce) + { + error = $"Skipping tuple in authorization_list because nonce is set to {authorizationTuple.Nonce}, but authority ({authorizationTuple.Authority}) has {authNonce}."; + return AuthorizationTupleResult.IncorrectNonce; } + + error = null; + return AuthorizationTupleResult.Valid; } protected virtual IReleaseSpec GetSpec(BlockHeader header) => VirtualMachine.BlockExecutionContext.Spec; From e15eb989e170837cceec628ea687ab574dddac65 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 10 Oct 2025 21:12:54 +0100 Subject: [PATCH 134/215] fix blockchain test runner --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index e3be2767047..eae7bde0148 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -83,8 +83,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? test.Network = ChainUtils.ResolveSpec(test.Network, test.ChainId); test.NetworkAfterTransition = ChainUtils.ResolveSpec(test.NetworkAfterTransition, test.ChainId); - List<(ForkActivation Activation, IReleaseSpec Spec)> transitions = - [((ForkActivation)0, test.GenesisSpec), ((ForkActivation)1, test.Network)]; // TODO: this thing took a lot of time to find after it was removed!, genesis block is always initialized with Frontier + List<(ForkActivation Activation, IReleaseSpec Spec)> transitions = [((ForkActivation)0, test.Network)]; if (test.NetworkAfterTransition is not null) { transitions.Add((test.TransitionForkActivation!.Value, test.NetworkAfterTransition)); @@ -92,11 +91,6 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? ISpecProvider specProvider = new CustomSpecProvider(test.ChainId, test.ChainId, transitions.ToArray()); - if (test.ChainId != GnosisSpecProvider.Instance.ChainId && specProvider.GenesisSpec != Frontier.Instance) - { - Assert.Fail("Expected genesis spec to be Frontier for blockchain tests"); - } - if (test.Network is Cancun || test.NetworkAfterTransition is Cancun) { await KzgPolynomialCommitments.InitializeAsync(); @@ -203,7 +197,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; case "5": - res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); + res = await engineRpcModule.engine_newPayloadV5((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; default: Assert.Fail("Invalid blockchain engine test, version not recognised."); @@ -295,9 +289,9 @@ private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalid { RlpStream rlpContext = Bytes.FromHexString(testBlockJson.Rlp).AsRlpStream(); Block suggestedBlock = Rlp.Decode(rlpContext); - // Console.WriteLine("suggested block:"); - // Console.WriteLine(suggestedBlock.BlockAccessList); - // Hash256 tmp = new(ValueKeccak.Compute(Rlp.Encode(suggestedBlock.BlockAccessList!.Value).Bytes).Bytes); + 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; From 64ca3ee760ee2c5bdadbbe5bde2055120eeeda70 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 13 Oct 2025 11:14:10 +0100 Subject: [PATCH 135/215] allow null BAL in execution payload --- .../Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 11964709978..9791cf294ac 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -52,7 +52,7 @@ public class ExecutionPayload : IForkValidator, IExecutionPayloadParams, IExecut public ulong Timestamp { get; set; } - public byte[]? BlockAccessList { get; set; } = []; + public byte[]? BlockAccessList { get; set; } protected byte[][] _encodedTransactions = []; @@ -127,7 +127,7 @@ public byte[][] Transactions Timestamp = block.Timestamp, BaseFeePerGas = block.BaseFeePerGas, Withdrawals = block.Withdrawals, - BlockAccessList = block.EncodedBlockAccessList!, + BlockAccessList = block.EncodedBlockAccessList, }; executionPayload.SetTransactions(block.Transactions); return executionPayload; From 3bd011966ff346698eed485c4d21cf95265cf859 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:52:28 +0100 Subject: [PATCH 136/215] amsterdam chainspec --- src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs | 1 + .../Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs index 94e47e9ab6f..6d290f008e7 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpec.cs @@ -81,5 +81,6 @@ public class ChainSpec public ulong? PragueTimestamp { get; set; } public ulong? OsakaTimestamp { get; set; } + public ulong? AmsterdamTimestamp { get; set; } } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index b7512dc20a8..33c8b2e7e49 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -258,6 +258,7 @@ chainSpec.Parameters.Eip1283DisableTransition is null chainSpec.CancunTimestamp = chainSpec.Parameters.Eip4844TransitionTimestamp; chainSpec.PragueTimestamp = chainSpec.Parameters.Eip7002TransitionTimestamp; chainSpec.OsakaTimestamp = chainSpec.Parameters.Eip7594TransitionTimestamp; + chainSpec.AmsterdamTimestamp = chainSpec.Parameters.Eip7928TransitionTimestamp; // TheMerge parameters chainSpec.MergeForkIdBlockNumber = chainSpec.Parameters.MergeForkIdTransition; From 7a3c5e59b4e3c00da22263bb86bcccff3dfc24a6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:11:18 +0100 Subject: [PATCH 137/215] encode BAL in payload if not already encoded --- src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 9791cf294ac..20c137da042 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -127,7 +127,7 @@ public byte[][] Transactions Timestamp = block.Timestamp, BaseFeePerGas = block.BaseFeePerGas, Withdrawals = block.Withdrawals, - BlockAccessList = block.EncodedBlockAccessList, + BlockAccessList = block.EncodedBlockAccessList ?? (block.BlockAccessList is null ? null : Rlp.Encode(block.BlockAccessList!.Value).Bytes), }; executionPayload.SetTransactions(block.Transactions); return executionPayload; From bea57aec14c44d1b2b35f59a5839d75b2c1db983 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:24:04 +0100 Subject: [PATCH 138/215] package lock --- src/Nethermind/Nethermind.Runner/packages.lock.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Runner/packages.lock.json b/src/Nethermind/Nethermind.Runner/packages.lock.json index ad240fb0934..633165b2cfb 100644 --- a/src/Nethermind/Nethermind.Runner/packages.lock.json +++ b/src/Nethermind/Nethermind.Runner/packages.lock.json @@ -1472,7 +1472,8 @@ "Nethermind.Core": "[1.35.0-unstable, )", "Nethermind.Crypto": "[1.35.0-unstable, )", "Nethermind.Serialization.Rlp": "[1.35.0-unstable, )", - "Nethermind.Specs": "[1.35.0-unstable, )" + "Nethermind.Specs": "[1.35.0-unstable, )", + "Nethermind.Trie": "[1.35.0-unstable, )" } }, "nethermind.evm.precompiles": { From 5a56c5f6cd2a442b85c26966856413793fa2e114 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 13 Oct 2025 16:01:04 +0100 Subject: [PATCH 139/215] disable warning temporarily --- src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs b/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs index db3c5edb739..3cc3fe059c5 100644 --- a/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs @@ -380,11 +380,12 @@ static bool HasNonWhitespace(ReadOnlySpan span) bool isSuccess = localErrorResponse is null; if (!isSuccess) { - if (localErrorResponse?.Error?.SuppressWarning == false) - { - if (_logger.IsWarn) _logger.Warn($"Error response handling JsonRpc Id:{request.Id} Method:{request.Method} | Code: {localErrorResponse.Error.Code} Message: {localErrorResponse.Error.Message}"); - if (_logger.IsTrace) _logger.Trace($"Error when handling {request} | {JsonSerializer.Serialize(localErrorResponse, EthereumJsonSerializer.JsonOptionsIndented)}"); - } + // tmp + // if (localErrorResponse?.Error?.SuppressWarning == false) + // { + // if (_logger.IsWarn) _logger.Warn($"Error response handling JsonRpc Id:{request.Id} Method:{request.Method} | Code: {localErrorResponse.Error.Code} Message: {localErrorResponse.Error.Message}"); + // if (_logger.IsTrace) _logger.Trace($"Error when handling {request} | {JsonSerializer.Serialize(localErrorResponse, EthereumJsonSerializer.JsonOptionsIndented)}"); + // } Metrics.JsonRpcErrors++; } else From 54a00d5a36dce973c4c831b197ed60281a5686de Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 00:36:05 +0100 Subject: [PATCH 140/215] fix block building --- .../Producers/BlockProducerEnvFactory.cs | 2 +- .../Blockchain/TestBlockchain.cs | 5 ++++ .../ShutterIntegrationTests.cs | 27 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs index 6f592335252..b91bef80ee0 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs @@ -32,7 +32,7 @@ protected virtual ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => public IBlockProducerEnv Create() { - IWorldState worldState = worldStateManager.CreateResettableWorldState(); + IWorldState worldState = new TracedAccessWorldState(worldStateManager.CreateResettableWorldState()); ILifetimeScope lifetimeScope = rootLifetime.BeginLifetimeScope(builder => ConfigureBuilder(builder) .AddScoped(worldState)); diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index a7e4aa5d7d4..c1520b234c4 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -373,6 +373,11 @@ public Block Build() genesisBlockBuilder.WithEmptyRequestsHash(); } + if (specProvider.GenesisSpec.BlockLevelAccessListsEnabled) + { + genesisBlockBuilder.WithBlockAccessListHash(Keccak.OfAnEmptySequenceRlp); + } + Block genesisBlock = genesisBlockBuilder.TestObject; foreach (IGenesisPostProcessor genesisPostProcessor in postProcessors) diff --git a/src/Nethermind/Nethermind.Shutter.Test/ShutterIntegrationTests.cs b/src/Nethermind/Nethermind.Shutter.Test/ShutterIntegrationTests.cs index fede30d6d11..ecd91f16219 100644 --- a/src/Nethermind/Nethermind.Shutter.Test/ShutterIntegrationTests.cs +++ b/src/Nethermind/Nethermind.Shutter.Test/ShutterIntegrationTests.cs @@ -12,6 +12,11 @@ using Nethermind.Core; using System.Threading; using Nethermind.Merge.Plugin.Test; +using Nethermind.Specs.Forks; +using Nethermind.Specs; +using Nethermind.Core.Crypto; +using Nethermind.JsonRpc; +using Nethermind.Core.Extensions; namespace Nethermind.Shutter.Test; @@ -116,4 +121,26 @@ public async Task Can_increment_metric_on_missed_keys() Assert.That(Metrics.ShutterKeysMissed, Is.EqualTo(5)); } + + // todo: move + [Test] + public async Task Can_construct_BAL() + { + using MergeTestBlockchain chain = await new MergeTestBlockchain().Build(new TestSpecProvider(Amsterdam.Instance)); + + Block genesis = chain.BlockFinder.FindGenesisBlock()!; + PayloadAttributes payloadAttributes = + new() { Timestamp = 12, PrevRandao = genesis.Header.Random!, SuggestedFeeRecipient = Address.Zero }; + + // we're using payloadService directly, because we can't use fcU for branch + string payloadId = chain.PayloadPreparationService!.StartPreparingPayload(genesis.Header, payloadAttributes)!; + + await Task.Delay(1000); + + ResultWrapper getPayloadResult = + await chain.EngineRpcModule.engine_getPayloadV6(Bytes.FromHexString(payloadId)); + var res = getPayloadResult.Data!; + Assert.That(res.ExecutionPayload.BlockAccessList, Is.Not.Null); + } + } From 32b91d987f6ca93b513b4becc85fbd89580f2d44 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 12:02:14 +0100 Subject: [PATCH 141/215] add encoding and decoding logs tmp --- .../Data/ExecutionPayload.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 20c137da042..b0794859f3f 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -13,6 +13,7 @@ using System.Text.Json.Serialization; using Nethermind.Core.ExecutionRequest; using Nethermind.Core.BlockAccessLists; +using Nethermind.Core.Extensions; namespace Nethermind.Merge.Plugin.Data; @@ -111,6 +112,9 @@ public byte[][] Transactions protected static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayload, new() { + Console.WriteLine("Creating execution payload, encodedBal=" + + (block.EncodedBlockAccessList is null ? "null" : Bytes.ToHexString(block.EncodedBlockAccessList)) + + " AccountChangesLength=" + (block.BlockAccessList is null ? "null" : block.BlockAccessList.Value.GetAccountChanges().Count())); TExecutionPayload executionPayload = new() { BlockHash = block.Hash!, @@ -177,12 +181,15 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) if (BlockAccessList is not null) { + //tmp + Console.WriteLine("Decoding BAL from execution payload: " + Bytes.ToHexString(BlockAccessList)); try { blockAccessList = Rlp.Decode(BlockAccessList); } - catch (RlpException) + catch (RlpException e) { + Console.Error.Write("Could not decode block access list from execution payload: " + e); return new("Could not decode block access list."); } } @@ -191,10 +198,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) return new(block); } - protected virtual Hash256? BuildWithdrawalsRoot() - { - return Withdrawals is null ? null : new WithdrawalTrie(Withdrawals).RootHash; - } + protected virtual Hash256? BuildWithdrawalsRoot() => Withdrawals is null ? null : new WithdrawalTrie(Withdrawals).RootHash; protected Transaction[]? _transactions = null; From 0c23e49e7b6e88f7fc4e50d7acae602d67f63a26 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:36:16 +0100 Subject: [PATCH 142/215] improve logs for degbugging --- .../Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 6 ++---- .../Eip7928/AccountChangesDecoder.cs | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index b0794859f3f..06f1ebd5947 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -112,9 +112,7 @@ public byte[][] Transactions protected static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayload, new() { - Console.WriteLine("Creating execution payload, encodedBal=" - + (block.EncodedBlockAccessList is null ? "null" : Bytes.ToHexString(block.EncodedBlockAccessList)) - + " AccountChangesLength=" + (block.BlockAccessList is null ? "null" : block.BlockAccessList.Value.GetAccountChanges().Count())); + Console.WriteLine("Created block access list:\n" + (block.BlockAccessList is null ? "null" : block.BlockAccessList.ToString())); TExecutionPayload executionPayload = new() { BlockHash = block.Hash!, @@ -182,7 +180,7 @@ public virtual BlockDecodingResult TryGetBlock(UInt256? totalDifficulty = null) if (BlockAccessList is not null) { //tmp - Console.WriteLine("Decoding BAL from execution payload: " + Bytes.ToHexString(BlockAccessList)); + // Console.WriteLine("Decoding BAL from execution payload:\n" + Bytes.ToHexString(BlockAccessList)); try { blockAccessList = Rlp.Decode(BlockAccessList); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index ea2d5f1a124..c8ce60f4574 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -61,9 +61,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe ushort lastIndex = 0; SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { + Console.WriteLine("Decoding balance change: " + s.ToString()); ushort index = s.BlockAccessIndex; if (index <= lastIndex) { + Console.WriteLine($"Balance changes were in incorrect order. index={index}, lastIndex={lastIndex}"); throw new RlpException("Balance changes were in incorrect order."); } lastIndex = index; From b9fba1fafba2a84b69893cb6f9e916a88db98020 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:02:50 +0100 Subject: [PATCH 143/215] fix bal index for building & decoding index 0 --- ...kProcessor.BlockProductionTransactionsExecutor.cs | 8 +++++--- ...kProcessor.BlockValidationTransactionsExecutor.cs | 8 ++++---- .../Nethermind.Merge.Plugin/Data/ExecutionPayload.cs | 2 +- .../Eip7928/AccountChangesDecoder.cs | 12 ++++++------ 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index 19f15ffb97d..da139fb0dbf 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using Nethermind.Blockchain.Tracing; @@ -31,6 +30,7 @@ public class BlockProductionTransactionsExecutor( ILogManager logManager) : IBlockProductionTransactionsExecutor { + private readonly TracedAccessWorldState? _tracedAccessWorldState = stateProvider as TracedAccessWorldState; private readonly ILogger _logger = logManager.GetClassLogger(); protected EventHandler? _transactionProcessed; @@ -65,6 +65,7 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr // Check if we have gone over time or the payload has been requested if (token.IsCancellationRequested) break; + _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); TxAction action = ProcessTransaction(block, currentTx, i++, receiptsTracer, processingOptions, consideredTx); if (action == TxAction.Stop) break; @@ -78,13 +79,14 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr } } } + _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); block.Header.TxRoot = TxTrie.CalculateRoot(includedTx.AsSpan()); if (blockToProduce is not null) { - blockToProduce.Transactions = includedTx.ToArray(); + blockToProduce.Transactions = [.. includedTx]; } - return receiptsTracer.TxReceipts.ToArray(); + return [.. receiptsTracer.TxReceipts]; } private TxAction ProcessTransaction( diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index 85e694051f8..27361369d63 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; @@ -11,7 +10,6 @@ using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Evm.TransactionProcessing; -using Nethermind.State; using Metrics = Nethermind.Evm.Metrics; namespace Nethermind.Consensus.Processing @@ -24,6 +22,8 @@ public class BlockValidationTransactionsExecutor( BlockValidationTransactionsExecutor.ITransactionProcessedEventHandler? transactionProcessedEventHandler = null) : IBlockProcessor.IBlockTransactionsExecutor { + private readonly TracedAccessWorldState? _tracedAccessWorldState = stateProvider as TracedAccessWorldState; + public void SetBlockExecutionContext(in BlockExecutionContext blockExecutionContext) { transactionProcessor.SetBlockExecutionContext(in blockExecutionContext); @@ -35,11 +35,11 @@ public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processing for (int i = 0; i < block.Transactions.Length; i++) { - (stateProvider as TracedAccessWorldState)?.BlockAccessList.IncrementBlockAccessIndex(); + _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); Transaction currentTx = block.Transactions[i]; ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); } - (stateProvider as TracedAccessWorldState)?.BlockAccessList.IncrementBlockAccessIndex(); + _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); return [.. receiptsTracer.TxReceipts]; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 06f1ebd5947..ff268c8371b 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -112,7 +112,7 @@ public byte[][] Transactions protected static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayload, new() { - Console.WriteLine("Created block access list:\n" + (block.BlockAccessList is null ? "null" : block.BlockAccessList.ToString())); + // Console.WriteLine("Created block access list:\n" + (block.BlockAccessList is null ? "null" : block.BlockAccessList.ToString())); TExecutionPayload executionPayload = new() { BlockHash = block.Hash!, diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index c8ce60f4574..5cd3d8804b9 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -58,12 +58,12 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe } BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); - ushort lastIndex = 0; + ushort? lastIndex = null; SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { Console.WriteLine("Decoding balance change: " + s.ToString()); ushort index = s.BlockAccessIndex; - if (index <= lastIndex) + if (lastIndex is not null && index <= lastIndex) { Console.WriteLine($"Balance changes were in incorrect order. index={index}, lastIndex={lastIndex}"); throw new RlpException("Balance changes were in incorrect order."); @@ -72,12 +72,12 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe return index; }, s => s)); - lastIndex = 0; + lastIndex = null; NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); SortedList nonceChangesList = new(nonceChanges.ToDictionary(s => { ushort index = s.BlockAccessIndex; - if (index <= lastIndex) + if (lastIndex is not null && index <= lastIndex) { throw new RlpException("Nonce changes were in incorrect order."); } @@ -92,11 +92,11 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe throw new RlpException("Number of code changes exceeded maximum."); } - lastIndex = 0; + lastIndex = null; SortedList codeChangesList = new(codeChanges.ToDictionary(s => { ushort index = s.BlockAccessIndex; - if (index <= lastIndex) + if (lastIndex is not null && index <= lastIndex) { throw new RlpException("Code changes were in incorrect order."); } From 8087b864ef9ab02d001679db04a862ff64b0477c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:23:17 +0100 Subject: [PATCH 144/215] reset block access index --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 1 + .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index f45661e52ea..93460cb7cb9 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -134,6 +134,7 @@ protected virtual TxReceipt[] ProcessBlock( if (_tracedAccessWorldState is not null) { _tracedAccessWorldState.Enabled = spec.BlockLevelAccessListsEnabled; + _tracedAccessWorldState.BlockAccessList.ResetBlockAccessIndex(); } StoreBeaconRoot(block, spec); diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index eba9399b243..01a721d9d0f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -50,6 +50,12 @@ public void IncrementBlockAccessIndex() Index++; } + public void ResetBlockAccessIndex() + { + _changes.Clear(); + Index = 0; + } + public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { if (address == Address.SystemUser) From cfad062365e9eb053b2a5d77ee51fa892d7b152f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:42:05 +0100 Subject: [PATCH 145/215] try to fix invalid hash, add logs --- .../BlockProcessor.BlockProductionTransactionsExecutor.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index da139fb0dbf..773ea408062 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using Nethermind.Blockchain.Tracing; @@ -66,6 +67,7 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr if (token.IsCancellationRequested) break; _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); + Console.WriteLine($"Building block {i}, balIndex={(_tracedAccessWorldState is null ? "null" : _tracedAccessWorldState.BlockAccessList.Index)}"); TxAction action = ProcessTransaction(block, currentTx, i++, receiptsTracer, processingOptions, consideredTx); if (action == TxAction.Stop) break; @@ -80,13 +82,14 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr } } _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); + Console.WriteLine($"Built block {i}, balIndex={(_tracedAccessWorldState is null ? "null" : _tracedAccessWorldState.BlockAccessList.Index)}"); block.Header.TxRoot = TxTrie.CalculateRoot(includedTx.AsSpan()); if (blockToProduce is not null) { - blockToProduce.Transactions = [.. includedTx]; + blockToProduce.Transactions = includedTx.ToArray(); } - return [.. receiptsTracer.TxReceipts]; + return receiptsTracer.TxReceipts.ToArray(); } private TxAction ProcessTransaction( From 601db9e58fb8dfab96987d8564e17da4c6febad1 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:57:34 +0100 Subject: [PATCH 146/215] remove log --- .../BlockProcessor.BlockProductionTransactionsExecutor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index 773ea408062..6bddc0cea9f 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -67,7 +67,6 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr if (token.IsCancellationRequested) break; _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); - Console.WriteLine($"Building block {i}, balIndex={(_tracedAccessWorldState is null ? "null" : _tracedAccessWorldState.BlockAccessList.Index)}"); TxAction action = ProcessTransaction(block, currentTx, i++, receiptsTracer, processingOptions, consideredTx); if (action == TxAction.Stop) break; From c7744227ed43fe2f061cb3f56df50748d8accc9a Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:24:21 +0100 Subject: [PATCH 147/215] log encoding balance changes --- .../Eip7928/AccountChangesDecoder.cs | 1 + .../Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 5cd3d8804b9..d8c44f28298 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -131,6 +131,7 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); + Console.WriteLine("Encoding bal address " + item.Address); stream.Encode(item.Address); stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index ba570dec95b..06892be8bb3 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -53,6 +53,7 @@ public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); + Console.WriteLine("Encoding balance change: " + item); stream.Encode(item.BlockAccessIndex); stream.Encode(item.PostBalance); } From 2668013f43e159bfbabe404dfa5a966179cea29a Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:26:31 +0100 Subject: [PATCH 148/215] try fix ushort decoding, save encoded bal --- ...sor.BlockProductionTransactionsExecutor.cs | 2 +- .../Processing/BlockProcessor.cs | 4 ++- .../Eip7928/AccountChangesDecoder.cs | 1 - .../Eip7928/BalanceChangeDecoder.cs | 1 - .../Nethermind.Serialization.Rlp/Rlp.cs | 26 ++++++++++--------- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index 6bddc0cea9f..93191900a1f 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -81,7 +81,7 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr } } _tracedAccessWorldState?.BlockAccessList.IncrementBlockAccessIndex(); - Console.WriteLine($"Built block {i}, balIndex={(_tracedAccessWorldState is null ? "null" : _tracedAccessWorldState.BlockAccessList.Index)}"); + // Console.WriteLine($"Built block {i}, balIndex={(_tracedAccessWorldState is null ? "null" : _tracedAccessWorldState.BlockAccessList.Index)}"); block.Header.TxRoot = TxTrie.CalculateRoot(includedTx.AsSpan()); if (blockToProduce is not null) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 93460cb7cb9..e838424390d 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -188,7 +188,9 @@ protected virtual TxReceipt[] ProcessBlock( else { body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - header.BlockAccessListHash = new(ValueKeccak.Compute(Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes).Bytes); + _logger.Info(body.BlockAccessList.ToString()); + block.EncodedBlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; + header.BlockAccessListHash = new(ValueKeccak.Compute(block.EncodedBlockAccessList).Bytes); } } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index d8c44f28298..5cd3d8804b9 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -131,7 +131,6 @@ public AccountChanges Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - Console.WriteLine("Encoding bal address " + item.Address); stream.Encode(item.Address); stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index 06892be8bb3..ba570dec95b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -53,7 +53,6 @@ public BalanceChange Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BalanceChange item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { stream.StartSequence(GetContentLength(item, rlpBehaviors)); - Console.WriteLine("Encoding balance change: " + item); stream.Encode(item.BlockAccessIndex); stream.Encode(item.PostBalance); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index 032e4e42c83..a883926b7b8 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -1724,18 +1724,20 @@ public static int LengthOf(long value) public static int LengthOf(int value) => LengthOf((long)value); - public static int LengthOf(ushort value) - { - if (value < 128) - { - return 1; - } - else - { - // everything has a length prefix - return 1 + sizeof(ushort) - (BitOperations.LeadingZeroCount(value) / 2); - } - } + // public static int LengthOf(ushort value) + // { + // if (value < 128) + // { + // return 1; + // } + // else + // { + // // everything has a length prefix + // return 1 + sizeof(ushort) - (BitOperations.LeadingZeroCount(value) / 2); + // } + // } + + public static int LengthOf(ushort value) => LengthOf((long)value); public static int LengthOf(Hash256? item) => item is null ? 1 : 33; From 30b6f29dfceeb615dd872651c6d5d9574cb6e8a7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 14 Oct 2025 17:47:52 +0100 Subject: [PATCH 149/215] try fix account changes in wrong order error --- .../Processing/BlockProcessor.cs | 1 - .../Eip7928/AccountChangesDecoder.cs | 1 - .../Eip7928/BlockAccessListDecoder.cs | 16 ++++++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index e838424390d..d77fed9709a 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -188,7 +188,6 @@ protected virtual TxReceipt[] ProcessBlock( else { body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; - _logger.Info(body.BlockAccessList.ToString()); block.EncodedBlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; header.BlockAccessListHash = new(ValueKeccak.Compute(block.EncodedBlockAccessList).Bytes); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 5cd3d8804b9..b791ca00195 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -61,7 +61,6 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe ushort? lastIndex = null; SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { - Console.WriteLine("Decoding balance change: " + s.ToString()); ushort index = s.BlockAccessIndex; if (lastIndex is not null && index <= lastIndex) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index cac12e0dea7..6451e625df6 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -19,17 +19,21 @@ public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { + // var tmp = ctx.Data[ctx.Position..].ToArray(); + // Console.WriteLine("bal:"); + // Console.WriteLine(Bytes.ToHexString(tmp)); + AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); if (accountChanges.Length > Eip7928Constants.MaxAccounts) { throw new RlpException("Number of accounts exceeded maximum."); } - Address lastAddress = Address.Zero; + Address? lastAddress = null; SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => { Address address = a.Address; - if (address.CompareTo(lastAddress) <= 0) + if (lastAddress is not null && address.CompareTo(lastAddress) <= 0) { throw new RlpException("Account changes were in incorrect order."); } @@ -38,10 +42,10 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB }, a => a)); BlockAccessList blockAccessList = new(accountChangesMap); - if (!accountChanges.SequenceEqual(accountChangesMap.Values)) - { - throw new RlpException("Accounts were in incorrect order."); - } + // if (!accountChanges.SequenceEqual(accountChangesMap.Values)) + // { + // throw new RlpException("Accounts were in incorrect order."); + // } return blockAccessList; } From 96bdd95530a0989bd46fa18b30bce200015fed1f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:18:22 +0100 Subject: [PATCH 150/215] coinbase zero transfer edge case --- .../TransactionProcessing/TransactionProcessor.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 1e56cac5fc9..23e871f018b 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -856,7 +856,19 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec bool gasBeneficiaryNotDestroyed = !substate.DestroyList.Contains(header.GasBeneficiary); if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) { + bool balTracingEnabled = _tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled; + if (balTracingEnabled && fees == 0) + { + // disable BAL tracing for special case of zero value transfer to coinbase + _tracedAccessWorldState.Enabled = false; + } + WorldState.AddToBalanceAndCreateIfNotExists(header.GasBeneficiary!, fees, spec); + + if (balTracingEnabled) + { + _tracedAccessWorldState.Enabled = true; + } } UInt256 eip1559Fees = !tx.IsFree() ? header.BaseFeePerGas * (ulong)spentGas : UInt256.Zero; From e461a6c92c6401a53c622b0185309a05892872ee Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:18:23 +0100 Subject: [PATCH 151/215] code change max increase --- src/Nethermind/Nethermind.Core/Eip7928Constants.cs | 1 - .../Eip7928/AccountChangesDecoder.cs | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Eip7928Constants.cs b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs index cafa85007c4..682cc3dad31 100644 --- a/src/Nethermind/Nethermind.Core/Eip7928Constants.cs +++ b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs @@ -9,5 +9,4 @@ public static class Eip7928Constants public const int MaxSlots = 300_000; public const int MaxAccounts = 300_000; public const int MaxCodeSize = 24_576; - public const int MaxCodeChanges = 1; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index b791ca00195..13d10d5f4ef 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -45,7 +45,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe }, s => s), Bytes.Comparer); StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); - SortedSet storareReadsList = []; + SortedSet storageReadsList = []; StorageRead? lastRead = null; foreach (StorageRead storageRead in storageReads) { @@ -53,7 +53,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe { throw new RlpException("Storage reads were in incorrect order."); } - storareReadsList.Add(storageRead); + storageReadsList.Add(storageRead); lastRead = storageRead; } @@ -86,7 +86,8 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); - if (codeChanges.Length > Eip7928Constants.MaxCodeChanges) + // max one code change per tx + if (codeChanges.Length > Eip7928Constants.MaxTxs) { throw new RlpException("Number of code changes exceeded maximum."); } @@ -107,7 +108,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe { Address = address, StorageChanges = slotChangesMap, - StorageReads = storareReadsList, + StorageReads = storageReadsList, BalanceChanges = balanceChangesList, NonceChanges = nonceChangesList, CodeChanges = codeChangesList From cc7e07c6d7f9634f55200da0254df5f8d3ce3120 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:17:06 +0100 Subject: [PATCH 152/215] enforce rlp buffer size limits --- .../Nethermind.Core/Eip7928Constants.cs | 17 ++++++++--- .../Eip7928/AccountChangesDecoder.cs | 28 ++++++------------- .../Eip7928/BalanceChangeDecoder.cs | 6 ---- .../Eip7928/BlockAccessListDecoder.cs | 17 ++--------- .../Eip7928/CodeChangeDecoder.cs | 13 ++------- .../Eip7928/NonceChangeDecoder.cs | 7 ----- .../Eip7928/SlotChangesDecoder.cs | 19 ++----------- .../Eip7928/StorageChangeDecoder.cs | 9 +----- .../Eip7928/StorageReadDecoder.cs | 2 +- 9 files changed, 31 insertions(+), 87 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Eip7928Constants.cs b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs index 682cc3dad31..9ea4fc2787e 100644 --- a/src/Nethermind/Nethermind.Core/Eip7928Constants.cs +++ b/src/Nethermind/Nethermind.Core/Eip7928Constants.cs @@ -5,8 +5,17 @@ namespace Nethermind.Core; public static class Eip7928Constants { - public const int MaxTxs = 30_000; - public const int MaxSlots = 300_000; - public const int MaxAccounts = 300_000; - public const int MaxCodeSize = 24_576; + // Max buffer lengths for RLP decoding + + // max number of transactions per block + public const int MaxTxs = 100_000; + + // max number of slots changed / read in one account + public const int MaxSlots = 1_000_000; + + // max number of accounts accessed per block + public const int MaxAccounts = 1_000_000; + + // max code size in bytes + public const int MaxCodeSize = 1_000_000; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index 13d10d5f4ef..d233bd8f29e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -15,23 +15,17 @@ public class AccountChangesDecoder : IRlpValueDecoder, IRlpStrea private static AccountChangesDecoder? _instance = null; public static AccountChangesDecoder Instance => _instance ??= new(); + private static readonly RlpLimit _slotsLimit = new(Eip7928Constants.MaxSlots, "", ReadOnlyMemory.Empty); + private static readonly RlpLimit _txLimit = new(Eip7928Constants.MaxTxs, "", ReadOnlyMemory.Empty); + public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - - // Console.WriteLine("account change uncut:"); - // Console.WriteLine(Bytes.ToHexString(tmp)); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 1)]; - // Console.WriteLine("account change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - Address address = ctx.DecodeAddress(); - SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance); + SlotChanges[] slotChanges = ctx.DecodeArray(SlotChangesDecoder.Instance, true, default, _slotsLimit); byte[]? lastSlot = null; SortedDictionary slotChangesMap = new(slotChanges.ToDictionary(s => { @@ -44,7 +38,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe return slot; }, s => s), Bytes.Comparer); - StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance); + StorageRead[] storageReads = ctx.DecodeArray(StorageReadDecoder.Instance, true, default, _slotsLimit); SortedSet storageReadsList = []; StorageRead? lastRead = null; foreach (StorageRead storageRead in storageReads) @@ -57,7 +51,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe lastRead = storageRead; } - BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance); + BalanceChange[] balanceChanges = ctx.DecodeArray(BalanceChangeDecoder.Instance, true, default, _txLimit); ushort? lastIndex = null; SortedList balanceChangesList = new(balanceChanges.ToDictionary(s => { @@ -72,7 +66,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe }, s => s)); lastIndex = null; - NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance); + NonceChange[] nonceChanges = ctx.DecodeArray(NonceChangeDecoder.Instance, true, default, _txLimit); SortedList nonceChangesList = new(nonceChanges.ToDictionary(s => { ushort index = s.BlockAccessIndex; @@ -84,13 +78,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe return index; }, s => s)); - CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance); - - // max one code change per tx - if (codeChanges.Length > Eip7928Constants.MaxTxs) - { - throw new RlpException("Number of code changes exceeded maximum."); - } + CodeChange[] codeChanges = ctx.DecodeArray(CodeChangeDecoder.Instance, true, default, _txLimit); lastIndex = null; SortedList codeChangesList = new(codeChanges.ToDictionary(s => diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs index ba570dec95b..46b699292c8 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BalanceChangeDecoder.cs @@ -17,15 +17,9 @@ public int GetLength(BalanceChange item, RlpBehaviors rlpBehaviors) public BalanceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 1)]; - // Console.WriteLine("balance change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - BalanceChange balanceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 6451e625df6..15704a37ccc 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -14,20 +14,14 @@ public class BlockAccessListDecoder : IRlpValueDecoder, IRlpStr private static BlockAccessListDecoder? _instance = null; public static BlockAccessListDecoder Instance => _instance ??= new(); + private static readonly RlpLimit _accountsLimit = new(Eip7928Constants.MaxAccounts, "", ReadOnlyMemory.Empty); + public int GetLength(BlockAccessList item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - // Console.WriteLine("bal:"); - // Console.WriteLine(Bytes.ToHexString(tmp)); - - AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance); - if (accountChanges.Length > Eip7928Constants.MaxAccounts) - { - throw new RlpException("Number of accounts exceeded maximum."); - } + AccountChanges[] accountChanges = ctx.DecodeArray(AccountChangesDecoder.Instance, true, default, _accountsLimit); Address? lastAddress = null; SortedDictionary accountChangesMap = new(accountChanges.ToDictionary(a => @@ -42,11 +36,6 @@ public BlockAccessList Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpB }, a => a)); BlockAccessList blockAccessList = new(accountChangesMap); - // if (!accountChanges.SequenceEqual(accountChangesMap.Values)) - // { - // throw new RlpException("Accounts were in incorrect order."); - // } - return blockAccessList; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs index 1dfad9d7532..a6ac2c2de6f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/CodeChangeDecoder.cs @@ -12,24 +12,15 @@ public class CodeChangeDecoder : IRlpValueDecoder, IRlpStreamDecoder { private static CodeChangeDecoder? _instance = null; public static CodeChangeDecoder Instance => _instance ??= new(); + private static readonly RlpLimit _codeLimit = new(Eip7928Constants.MaxCodeSize, "", ReadOnlyMemory.Empty); public CodeChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 1)]; - // Console.WriteLine("code change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - ushort blockAccessIndex = ctx.DecodeUShort(); - byte[] newCode = ctx.DecodeByteArray(); - if (newCode.Length > Eip7928Constants.MaxCodeSize) - { - throw new RlpException("New code exceeded maxium length."); - } + byte[] newCode = ctx.DecodeByteArray(_codeLimit); CodeChange codeChange = new() { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs index 468fd1488d2..352bd7ccedd 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/NonceChangeDecoder.cs @@ -3,7 +3,6 @@ using System; using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -14,15 +13,9 @@ public class NonceChangeDecoder : IRlpValueDecoder, IRlpStreamDecod public NonceChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 1)]; - // Console.WriteLine("nonce change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - NonceChange nonceChange = new() { BlockAccessIndex = ctx.DecodeUShort(), diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index 471578f153b..c8d1cc5391b 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -12,33 +12,20 @@ public class SlotChangesDecoder : IRlpValueDecoder, IRlpStreamDecod private static SlotChangesDecoder? _instance = null; public static SlotChangesDecoder Instance => _instance ??= new(); + private static readonly RlpLimit _codeLimit = new(Eip7928Constants.MaxCodeSize, "", ReadOnlyMemory.Empty); public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - - // Console.WriteLine("slot change uncut:"); - // Console.WriteLine(Bytes.ToHexString(tmp)); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 2)]; - // Console.WriteLine("slot change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - - byte[] slot = ctx.DecodeByteArray(); + byte[] slot = ctx.DecodeByteArray(RlpLimit.L32); if (slot.Length != 32) { throw new RlpException("Invalid storage key, should be 32 bytes."); } - StorageChange[] changes = ctx.DecodeArray(StorageChangeDecoder.Instance); - if (changes.Length > Eip7928Constants.MaxSlots) - { - throw new RlpException("Number of slot changes exceeded maximum."); - } - + StorageChange[] changes = ctx.DecodeArray(StorageChangeDecoder.Instance, true, default, _codeLimit); SlotChanges slotChanges = new() { Slot = slot, diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 8516d3f1415..730e6037d21 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -3,7 +3,6 @@ using System; using Nethermind.Core.BlockAccessLists; -using Nethermind.Core.Extensions; namespace Nethermind.Serialization.Rlp.Eip7928; @@ -14,17 +13,11 @@ public class StorageChangeDecoder : IRlpValueDecoder, IRlpStreamD public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - // var tmp = ctx.Data[ctx.Position..].ToArray(); - int length = ctx.ReadSequenceLength(); int check = length + ctx.Position; - // tmp = tmp[..(length + 1)]; - // Console.WriteLine("storage change:" + length); - // Console.WriteLine(Bytes.ToHexString(tmp)); - ushort blockAccessIndex = ctx.DecodeUShort(); - byte[] newValue = ctx.DecodeByteArray(); + byte[] newValue = ctx.DecodeByteArray(RlpLimit.L32); if (newValue.Length != 32) { throw new RlpException("Invalid storage value, should be 32 bytes."); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index 6574f9c56e0..a9b8e662071 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -16,7 +16,7 @@ public int GetLength(StorageRead item, RlpBehaviors rlpBehaviors) public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors) { - byte[] key = ctx.DecodeByteArray(); + byte[] key = ctx.DecodeByteArray(RlpLimit.L32); if (key.Length != 32) { throw new RlpException("Invalid storage key, should be 32 bytes."); From 36937cd4efe71bdfe63e129c3ec072bb5888fdaf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:35:45 +0100 Subject: [PATCH 153/215] fix test runners --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 163 ++++++------------ .../Ethereum.Test.Base/FileTestsSource.cs | 1 + .../Ethereum.Test.Base/JsonToEthereumTest.cs | 4 +- .../BlockchainTestsRunner.cs | 11 +- 4 files changed, 60 insertions(+), 119 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 45290d9538d..b9061992d2f 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -122,7 +122,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? IConfigProvider configProvider = new ConfigProvider(); // configProvider.GetConfig().PreWarmStateOnBlockProcessing = false; await using IContainer container = new ContainerBuilder() - .AddModule(new TestNethermindModule(configProvider)) + .AddModule(new TestNethermindModule(configProvider)) // check if two different modules must be fixed together .AddModule(new TestMergeModule(configProvider)) .AddSingleton(specProvider) .AddSingleton(_logManager) @@ -210,6 +210,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? res = await engineRpcModule.engine_newPayloadV4((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; case "5": + // this block is not found in stateProvider and blocktree res = await engineRpcModule.engine_newPayloadV5((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); break; default: @@ -223,6 +224,8 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? Assert.Fail("Invalid blockchain test, did not contain blocks or new payloads."); } + // NOTE: Tracer removal must happen AFTER StopAsync to ensure all blocks are traced + // Blocks are queued asynchronously, so we need to wait for processing to complete await blockchainProcessor.StopAsync(true); stopwatch?.Stop(); @@ -238,6 +241,16 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider); } + bool testPassed = differences.Count == 0; + + // Write test end marker if using streaming tracer (JSONL format) + // This must be done BEFORE removing tracer and BEFORE Assert to ensure marker is written even on failure + if (tracer is not null) + { + tracer.TestFinished(test.Name, testPassed, test.Network, stopwatch?.Elapsed, headBlock?.StateRoot); + blockchainProcessor.Tracers.Remove(tracer); + } + Assert.That(differences, Is.Empty, "differences"); return new EthereumTestResult @@ -259,122 +272,46 @@ private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalid List<(Block Block, string ExpectedException)> correctRlp = DecodeRlps(test, failOnInvalidRlp); for (int i = 0; i < correctRlp.Count; i++) { - BlockHeader parentHeader; - // Genesis processing - using (stateProvider.BeginScope(null)) + if (correctRlp[i].Block.Hash is null) { - InitializeTestState(test, stateProvider, specProvider); - - stopwatch?.Start(); - - test.GenesisRlp ??= Rlp.Encode(new Block(JsonToEthereumTest.Convert(test.GenesisBlockHeader))); - - Block genesisBlock = Rlp.Decode(test.GenesisRlp.Bytes); - Assert.That(genesisBlock.Header.Hash, Is.EqualTo(new Hash256(test.GenesisBlockHeader.Hash))); - - ManualResetEvent genesisProcessed = new(false); - - blockTree.NewHeadBlock += (_, args) => - { - if (args.Block.Number == 0) - { - Assert.That(stateProvider.HasStateForBlock(genesisBlock.Header), Is.EqualTo(true)); - genesisProcessed.Set(); - } - }; - - blockTree.SuggestBlock(genesisBlock); - genesisProcessed.WaitOne(); - parentHeader = genesisBlock.Header; - - // Dispose genesis block's AccountChanges - genesisBlock.DisposeAccountChanges(); + Assert.Fail($"null hash in {test.Name} block {i}"); } - List<(Block Block, string ExpectedException)> correctRlp = DecodeRlps(test, failOnInvalidRlp); - for (int i = 0; i < correctRlp.Count; i++) + try { - if (correctRlp[i].Block.Hash is null) - { - Assert.Fail($"null hash in {test.Name} block {i}"); - } - - try + // TODO: mimic the actual behaviour where block goes through validating sync manager? + correctRlp[i].Block.Header.IsPostMerge = correctRlp[i].Block.Difficulty == 0; + if (!test.SealEngineUsed || blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out _)) { - // TODO: mimic the actual behaviour where block goes through validating sync manager? - correctRlp[i].Block.Header.IsPostMerge = correctRlp[i].Block.Difficulty == 0; - if (!test.SealEngineUsed || blockValidator.ValidateSuggestedBlock(correctRlp[i].Block, parentHeader, out _)) - { - blockTree.SuggestBlock(correctRlp[i].Block); - } - else - { - if (correctRlp[i].ExpectedException is not null) - { - Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}"); - } - } + blockTree.SuggestBlock(correctRlp[i].Block); } - catch (InvalidBlockException e) + else { if (correctRlp[i].ExpectedException is not null) { - Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}: {e}"); + Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}"); } } - catch (Exception e) - { - Assert.Fail($"Unexpected exception during processing: {e}"); - } - finally - { - // Dispose AccountChanges to prevent memory leaks in tests - correctRlp[i].Block.DisposeAccountChanges(); - } - - parentHeader = correctRlp[i].Block.Header; } - - // NOTE: Tracer removal must happen AFTER StopAsync to ensure all blocks are traced - // Blocks are queued asynchronously, so we need to wait for processing to complete - await blockchainProcessor.StopAsync(true); - - stopwatch?.Stop(); - - IBlockCachePreWarmer? preWarmer = container.Resolve().LifetimeScope.ResolveOptional(); - if (preWarmer is not null) + catch (InvalidBlockException e) { - // Caches are cleared async, which is a problem as read for the MainWorldState with prewarmer is not correct if its not cleared. - preWarmer.ClearCaches(); + if (correctRlp[i].ExpectedException is not null) + { + Assert.Fail($"Unexpected invalid block {correctRlp[i].Block.Hash}: {e}"); + } } - - Block? headBlock = blockTree.RetrieveHeadBlock(); - List differences; - using (stateProvider.BeginScope(headBlock.Header)) + catch (Exception e) { - differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider); + Assert.Fail($"Unexpected exception during processing: {e}"); } - - bool testPassed = differences.Count == 0; - - // Write test end marker if using streaming tracer (JSONL format) - // This must be done BEFORE removing tracer and BEFORE Assert to ensure marker is written even on failure - if (tracer is not null) + finally { - tracer.TestFinished(test.Name, testPassed, test.Network, stopwatch?.Elapsed, headBlock?.StateRoot); - blockchainProcessor.Tracers.Remove(tracer); + // Dispose AccountChanges to prevent memory leaks in tests + correctRlp[i].Block.DisposeAccountChanges(); } - Assert.That(differences.Count, Is.Zero, "differences"); - - return new EthereumTestResult - ( - test.Name, - null, - testPassed - ); + parentHeader = correctRlp[i].Block.Header; } - return parentHeader; } @@ -486,7 +423,7 @@ private static List RunAssertions(BlockchainTest test, Block headBlock, } } - foreach ((Address acountAddress, AccountState accountState) in test.PostState!) + foreach ((Address accountAddress, AccountState accountState) in test.PostState!) { int differencesBefore = differences.Count; @@ -496,60 +433,60 @@ private static List RunAssertions(BlockchainTest test, Block headBlock, break; } - bool accountExists = stateProvider.AccountExists(acountAddress); - UInt256? balance = accountExists ? stateProvider.GetBalance(acountAddress) : null; - UInt256? nonce = accountExists ? stateProvider.GetNonce(acountAddress) : null; + bool accountExists = stateProvider.AccountExists(accountAddress); + UInt256? balance = accountExists ? stateProvider.GetBalance(accountAddress) : null; + UInt256? nonce = accountExists ? stateProvider.GetNonce(accountAddress) : null; if (accountState.Balance != balance) { - differences.Add($"{acountAddress} balance exp: {accountState.Balance}, actual: {balance}, diff: {(balance > accountState.Balance ? balance - accountState.Balance : accountState.Balance - balance)}"); + differences.Add($"{accountAddress} balance exp: {accountState.Balance}, actual: {balance}, diff: {(balance > accountState.Balance ? balance - accountState.Balance : accountState.Balance - balance)}"); } if (accountState.Nonce != nonce) { - differences.Add($"{acountAddress} nonce exp: {accountState.Nonce}, actual: {nonce}"); + differences.Add($"{accountAddress} nonce exp: {accountState.Nonce}, actual: {nonce}"); } - byte[] code = accountExists ? stateProvider.GetCode(acountAddress) : []; + byte[] code = accountExists ? stateProvider.GetCode(accountAddress) : []; if (!Bytes.AreEqual(accountState.Code, code)) { - differences.Add($"{acountAddress} code exp: {accountState.Code?.Length}, actual: {code?.Length}"); + differences.Add($"{accountAddress} code exp: {accountState.Code?.Length}, actual: {code?.Length}"); } if (differences.Count != differencesBefore) { - _logger.Info($"ACCOUNT STATE ({acountAddress}) HAS DIFFERENCES"); + _logger.Info($"ACCOUNT STATE ({accountAddress}) HAS DIFFERENCES"); } differencesBefore = differences.Count; KeyValuePair[] clearedStorages = []; - if (test.Pre.TryGetValue(acountAddress, out AccountState? state)) + if (test.Pre.ContainsKey(accountAddress)) { - clearedStorages = state.Storage.Where(s => !accountState.Storage.ContainsKey(s.Key)).ToArray(); + clearedStorages = [.. test.Pre[accountAddress].Storage.Where(s => !accountState.Storage.ContainsKey(s.Key))]; } foreach (KeyValuePair clearedStorage in clearedStorages) { - ReadOnlySpan value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, clearedStorage.Key)); + ReadOnlySpan value = !stateProvider.AccountExists(accountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(accountAddress, clearedStorage.Key)); if (!value.IsZero()) { - differences.Add($"{acountAddress} storage[{clearedStorage.Key}] exp: 0x00, actual: {value.ToHexString(true)}"); + differences.Add($"{accountAddress} storage[{clearedStorage.Key}] exp: 0x00, actual: {value.ToHexString(true)}"); } } foreach (KeyValuePair storageItem in accountState.Storage) { - ReadOnlySpan value = !stateProvider.AccountExists(acountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(acountAddress, storageItem.Key)); + ReadOnlySpan value = !stateProvider.AccountExists(accountAddress) ? Bytes.Empty : stateProvider.Get(new StorageCell(accountAddress, storageItem.Key)); if (!Bytes.AreEqual(storageItem.Value, value)) { - differences.Add($"{acountAddress} storage[{storageItem.Key}] exp: {storageItem.Value.ToHexString(true)}, actual: {value.ToHexString(true)}"); + differences.Add($"{accountAddress} storage[{storageItem.Key}] exp: {storageItem.Value.ToHexString(true)}, actual: {value.ToHexString(true)}"); } } if (differences.Count != differencesBefore) { - _logger.Info($"ACCOUNT STORAGE ({acountAddress}) HAS DIFFERENCES"); + _logger.Info($"ACCOUNT STORAGE ({accountAddress}) HAS DIFFERENCES"); } } diff --git a/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs b/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs index 3e85b70087f..75e626d3272 100644 --- a/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs +++ b/src/Nethermind/Ethereum.Test.Base/FileTestsSource.cs @@ -34,6 +34,7 @@ public IEnumerable LoadTests(TestType testType) return []; } + Console.WriteLine("loaded " + _fileName); string json = File.ReadAllText(_fileName, Encoding.Default); return testType switch diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 432156d5dcc..ac88f76dc23 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -306,7 +306,9 @@ public static BlockchainTest Convert(string name, string category, BlockchainTes GenesisBlockHeader = testJson.GenesisBlockHeader, Blocks = testJson.Blocks, EngineNewPayloads = testJson.EngineNewPayloads, - Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value) + // nullable for engine? + // Pre = testJson.Pre.ToDictionary(p => p.Key, p => p.Value) + Pre = testJson.Pre }; HalfBlockchainTestJson half = testJson as HalfBlockchainTestJson; diff --git a/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs b/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs index 31228fa6f1a..9f055bf779a 100644 --- a/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs +++ b/src/Nethermind/Nethermind.Test.Runner/BlockchainTestsRunner.cs @@ -37,13 +37,14 @@ public async Task> RunTestsAsync() continue; } - // Create a streaming tracer once for all tests if tracing is enabled - using BlockchainTestStreamingTracer? tracer = trace - ? new BlockchainTestStreamingTracer(new() { EnableMemory = traceMemory, DisableStack = traceNoStack }) - : null; + // Create a streaming tracer once for all tests if tracing is enabled + using BlockchainTestStreamingTracer? tracer = trace + ? new BlockchainTestStreamingTracer(new() { EnableMemory = traceMemory, DisableStack = traceNoStack }) + : null; + BlockchainTest test = loadedTest as BlockchainTest; - if (_filter is not null && test.Name is not null && !Regex.Match(test.Name, $"^({_filter})").Success) + if (filter is not null && test.Name is not null && !Regex.Match(test.Name, $"^({filter})").Success) continue; Setup(); From 78d054e26ab76d91ea2b0e8c6747629484edcb2c Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 17 Oct 2025 01:40:42 +0100 Subject: [PATCH 154/215] fix restore --- .../BlockAccessLists/BlockAccessList.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 01a721d9d0f..adb1e939c66 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Data; using System.Linq; using Nethermind.Int256; @@ -99,7 +100,8 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { Address = address, Type = ChangeType.BalanceChange, - PreviousValue = balanceChanges.Last().Value + PreviousValue = balanceChanges.Last().Value, + BlockAccessIndex = Index }); balanceChanges.RemoveAt(balanceChanges.Count - 1); @@ -111,7 +113,8 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) Address = address, Type = ChangeType.BalanceChange, PreviousValue = null, - PreTxBalance = before + PreTxBalance = before, + BlockAccessIndex = Index }); } @@ -325,13 +328,21 @@ public readonly void Restore(int snapshot) while (_changes.Count > snapshot) { Change change = _changes.Pop(); + int count; switch (change.Type) { case ChangeType.BalanceChange: BalanceChange? previousBalance = change.PreviousValue is null ? null : (BalanceChange)change.PreviousValue; SortedList balanceChanges = _accountChanges[change.Address].BalanceChanges; - balanceChanges.RemoveAt(balanceChanges.Count - 1); + // balance could have gone bak to pre-tx value + // so would already be removed + count = balanceChanges.Count; + if (count > 0 && balanceChanges.Last().Key == change.BlockAccessIndex) + { + balanceChanges.RemoveAt(balanceChanges.Count - 1); + } + if (previousBalance is not null) { balanceChanges.Add(Index, previousBalance.Value); @@ -427,5 +438,6 @@ private readonly struct Change public ChangeType Type { get; init; } public IIndexedChange? PreviousValue { get; init; } public UInt256? PreTxBalance { get; init; } + public ushort BlockAccessIndex { get; init; } } } From 074a9e76eb5bb2e06ecaf5a7b1369d877afc0907 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 17 Oct 2025 02:20:30 +0100 Subject: [PATCH 155/215] fix reverting storage change --- .../BlockAccessLists/BlockAccessList.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index adb1e939c66..643b248abce 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -246,11 +246,15 @@ public void AddStorageRead(in StorageCell storageCell) { byte[] key = new byte[32]; storageCell.Index.ToBigEndian(key); + AddStorageRead(storageCell.Address, key); + } + + public void AddStorageRead(Address address, byte[] key) + { StorageRead storageRead = new() { Key = new(key) }; - Address address = storageCell.Address; if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) { @@ -322,7 +326,8 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan public readonly int TakeSnapshot() => _changes.Count; - public readonly void Restore(int snapshot) + // todo: can simplify to wipe all balance, code, nonce changes? + public void Restore(int snapshot) { snapshot = int.Max(0, snapshot); while (_changes.Count > snapshot) @@ -335,7 +340,7 @@ public readonly void Restore(int snapshot) BalanceChange? previousBalance = change.PreviousValue is null ? null : (BalanceChange)change.PreviousValue; SortedList balanceChanges = _accountChanges[change.Address].BalanceChanges; - // balance could have gone bak to pre-tx value + // balance could have gone back to pre-tx value // so would already be removed count = balanceChanges.Count; if (count > 0 && balanceChanges.Last().Key == change.BlockAccessIndex) @@ -372,6 +377,9 @@ public readonly void Restore(int snapshot) StorageChange? previousStorage = change.PreviousValue is null ? null : (StorageChange)change.PreviousValue; SlotChanges storageChanges = _accountChanges[change.Address].StorageChanges[change.Slot!]; + // replace change with read + _accountChanges[change.Address].StorageReads.Add(new(Bytes32.Wrap(change.Slot!))); + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); if (previousStorage is not null) { From 81f3d0f8812df285b5e04d2da6660310423199ba Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 17 Oct 2025 02:48:16 +0100 Subject: [PATCH 156/215] add account read for extcodecopy --- .../Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 74e3ebe8bca..9e155798ed3 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -10,6 +10,7 @@ namespace Nethermind.Evm; using Int256; +using Nethermind.Evm.State; internal static partial class EvmInstructions { @@ -147,6 +148,8 @@ public static EvmExceptionType InstructionExtCodeCopy( gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in result, out bool outOfGas); if (outOfGas) goto OutOfGas; + (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); + // Charge gas for account access (considering hot/cold storage costs). if (!EvmCalculations.ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; From bc43434cf272123b661592c0f5364d4ca42ece79 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 17 Oct 2025 03:05:35 +0100 Subject: [PATCH 157/215] don't add for extcodecopy when oog --- .../Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index 9e155798ed3..c133fc19327 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -148,12 +148,12 @@ public static EvmExceptionType InstructionExtCodeCopy( gasAvailable -= spec.GetExtCodeCost() + GasCostOf.Memory * EvmCalculations.Div32Ceiling(in result, out bool outOfGas); if (outOfGas) goto OutOfGas; - (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); - // Charge gas for account access (considering hot/cold storage costs). if (!EvmCalculations.ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; + (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); + if (!result.IsZero) { // Update memory cost if the destination region requires expansion. From 872ff027ffcc44edf81ac7c829d8af4281563758 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 20 Oct 2025 16:57:27 +0100 Subject: [PATCH 158/215] fix engine test runner --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index b9061992d2f..f4fee75cb71 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -215,7 +215,13 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? break; default: Assert.Fail("Invalid blockchain engine test, version not recognised."); - break; + continue; + } + + if (res.Result.ResultType == ResultType.Success) + { + ForkchoiceStateV1 fcuState = new(executionPayload.BlockHash, executionPayload.BlockHash, executionPayload.BlockHash); + await engineRpcModule.engine_forkchoiceUpdatedV3(fcuState); } } } @@ -238,7 +244,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? List differences; using (stateProvider.BeginScope(headBlock.Header)) { - differences = RunAssertions(test, blockTree.RetrieveHeadBlock(), stateProvider); + differences = RunAssertions(test, headBlock, stateProvider); } bool testPassed = differences.Count == 0; @@ -406,10 +412,6 @@ private static List RunAssertions(BlockchainTest test, Block headBlock, return test.PostStateRoot != stateProvider.StateRoot ? ["state root mismatch"] : Enumerable.Empty().ToList(); } - 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 differences = []; IEnumerable> deletedAccounts = test.Pre? @@ -490,30 +492,39 @@ private static List RunAssertions(BlockchainTest test, Block headBlock, } } - BigInteger gasUsed = headBlock.Header.GasUsed; - if ((testHeader?.GasUsed ?? 0) != gasUsed) - { - differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}"); - } + TestBlockHeaderJson? testHeaderJson = test.Blocks? + .Where(b => b.BlockHeader is not null) + .SingleOrDefault(b => new Hash256(b.BlockHeader.Hash) == headBlock.Hash)?.BlockHeader; - if (headBlock.Transactions.Any() && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString()) + if (testHeaderJson is not null) { - differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}"); - } + BlockHeader testHeader = JsonToEthereumTest.Convert(testHeaderJson); - if (testHeader.StateRoot != stateProvider.StateRoot) - { - differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}"); - } + BigInteger gasUsed = headBlock.Header.GasUsed; + if ((testHeader?.GasUsed ?? 0) != gasUsed) + { + differences.Add($"GAS USED exp: {testHeader?.GasUsed ?? 0}, actual: {gasUsed}"); + } - if (testHeader.TxRoot != headBlock.Header.TxRoot) - { - differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TxRoot}, actual: {headBlock.Header.TxRoot}"); - } + if (headBlock.Transactions.Length != 0 && testHeader.Bloom.ToString() != headBlock.Header.Bloom.ToString()) + { + differences.Add($"BLOOM exp: {testHeader.Bloom}, actual: {headBlock.Header.Bloom}"); + } - if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot) - { - differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}"); + if (testHeader.StateRoot != stateProvider.StateRoot) + { + differences.Add($"STATE ROOT exp: {testHeader.StateRoot}, actual: {stateProvider.StateRoot}"); + } + + if (testHeader.TxRoot != headBlock.Header.TxRoot) + { + differences.Add($"TRANSACTIONS ROOT exp: {testHeader.TxRoot}, actual: {headBlock.Header.TxRoot}"); + } + + if (testHeader.ReceiptsRoot != headBlock.Header.ReceiptsRoot) + { + differences.Add($"RECEIPT ROOT exp: {testHeader.ReceiptsRoot}, actual: {headBlock.Header.ReceiptsRoot}"); + } } if (test.LastBlockHash != headBlock.Hash) From c3b9447ca0236efb70a3ae800d31e2f406e8c74e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:00:39 +0100 Subject: [PATCH 159/215] refactor test runner --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index f4fee75cb71..66f96a5897c 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -187,43 +187,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? } 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 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": - // this block is not found in stateProvider and blocktree - res = await engineRpcModule.engine_newPayloadV5((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); - break; - default: - Assert.Fail("Invalid blockchain engine test, version not recognised."); - continue; - } - - if (res.Result.ResultType == ResultType.Success) - { - ForkchoiceStateV1 fcuState = new(executionPayload.BlockHash, executionPayload.BlockHash, executionPayload.BlockHash); - await engineRpcModule.engine_forkchoiceUpdatedV3(fcuState); - } - } + RunNewPayloads(test.EngineNewPayloads, engineRpcModule); } else { @@ -321,6 +285,47 @@ private static BlockHeader SuggestBlocks(BlockchainTest test, bool failOnInvalid return parentHeader; } + private async static void RunNewPayloads(TestEngineNewPayloadsJson[]? newPayloads, IEngineRpcModule engineRpcModule) + { + (ExecutionPayload, string[]?, string[]?, string?)[] payloads = [.. JsonToEthereumTest.Convert(newPayloads)]; + + // blockchain test engine + foreach ((ExecutionPayload executionPayload, string[]? blobVersionedHashes, string[]? validationError, string? newPayloadVersion) in payloads) + { + ResultWrapper 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": + // this block is not found in stateProvider and blocktree + res = await engineRpcModule.engine_newPayloadV5((ExecutionPayloadV3)executionPayload, hashes, executionPayload.ParentBeaconBlockRoot, []); + break; + default: + Assert.Fail("Invalid blockchain engine test, version not recognised."); + continue; + } + + if (res.Result.ResultType == ResultType.Success) + { + ForkchoiceStateV1 fcuState = new(executionPayload.BlockHash, executionPayload.BlockHash, executionPayload.BlockHash); + await engineRpcModule.engine_forkchoiceUpdatedV3(fcuState); + } + } + } + private static List<(Block Block, string ExpectedException)> DecodeRlps(BlockchainTest test, bool failOnInvalidRlp) { List<(Block Block, string ExpectedException)> correctRlp = new(); From f9ff97af5275bba03b8425203d0fdb42bba2e2a8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 00:41:09 +0100 Subject: [PATCH 160/215] don't record storage change if resets to same value --- .../BlockAccessLists/BlockAccessList.cs | 67 ++++++++++++++++--- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 643b248abce..5f98ba2a69d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -85,7 +85,7 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) SortedList balanceChanges = accountChanges.BalanceChanges; // balance change edge case - if (!HasChangedDuringTx(address, before, after)) + if (!HasBalanceChangedDuringTx(address, before, after)) { if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { @@ -220,7 +220,7 @@ public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan { Span key = new byte[32]; storageIndex.ToBigEndian(key); - StorageChange(accountChanges, key, after); + StorageChange(accountChanges, key, before, after); } } @@ -238,7 +238,7 @@ public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] a { Span key = new byte[32]; storageCell.Index.ToBigEndian(key); - StorageChange(accountChanges, key, after.AsSpan()); + StorageChange(accountChanges, key, before.AsSpan(), after.AsSpan()); } } @@ -287,11 +287,11 @@ public readonly void DeleteAccount(Address address) accountChanges.CodeChanges.Clear(); } - private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan value) + private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan before, in ReadOnlySpan after) { Span newValue = stackalloc byte[32]; newValue.Clear(); - value.CopyTo(newValue[(32 - value.Length)..]); + after.CopyTo(newValue[(32 - after.Length)..]); StorageChange storageChange = new() { BlockAccessIndex = Index, @@ -305,11 +305,24 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan { storageChanges = new(storageKey); } - else if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) + + // storage change edge case + if (!HasStorageChangedDuringTx(accountChanges.Address, [..key], before, after)) + { + if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) + { + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + } + accountChanges.StorageReads.Add(new(Bytes32.Wrap(storageKey))); + return; + } + + if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { previousStorage = storageChanges.Changes[^1]; storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } + storageChanges.Changes.Add(storageChange); accountChanges.StorageChanges[storageKey] = storageChanges; _changes.Push(new() @@ -317,7 +330,8 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan Address = accountChanges.Address, Slot = storageKey, Type = ChangeType.StorageChange, - PreviousValue = previousStorage + PreviousValue = previousStorage, + PreTxStorage = [.. before] }); accountChanges.StorageReads.Remove(new(Bytes32.Wrap(storageKey))); @@ -398,7 +412,7 @@ public void Restore(int snapshot) public override readonly string? ToString() => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; - private readonly bool HasChangedDuringTx(Address address, UInt256 beforeInstr, UInt256 afterInstr) + private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeInstr, UInt256 afterInstr) { AccountChanges accountChanges = _accountChanges[address]; int count = accountChanges.BalanceChanges.Count; @@ -429,7 +443,41 @@ private readonly bool HasChangedDuringTx(Address address, UInt256 beforeInstr, U } } - throw new Exception("Error calculting pre tx balance"); + throw new Exception("Error calculating pre tx balance"); + } + + private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in ReadOnlySpan beforeInstr, in ReadOnlySpan afterInstr) + { + AccountChanges accountChanges = _accountChanges[address]; + int count = accountChanges.StorageChanges.Count; + + if (count == 0) + { + // first storage change of block + // return storage prior to this instruction + return beforeInstr != afterInstr; + } + + foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) + { + if (storageChange.BlockAccessIndex != Index) + { + // storage changed in previous tx in block + return storageChange.NewValue.Unwrap().AsSpan() != afterInstr; + } + } + + // balance only changed within this transaction + foreach (Change change in _changes) + { + if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan() && change.PreviousValue is null) + { + // first change of this transaction & block + return (change.PreTxStorage ?? []).AsSpan() != afterInstr; + } + } + + throw new Exception("Error calculating pre tx balance"); } private enum ChangeType @@ -446,6 +494,7 @@ private readonly struct Change public ChangeType Type { get; init; } public IIndexedChange? PreviousValue { get; init; } public UInt256? PreTxBalance { get; init; } + public byte[]? PreTxStorage { get; init; } public ushort BlockAccessIndex { get; init; } } } From 88ae970aa94b33e3c2fd060f307835ffcdc953cf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 00:59:15 +0100 Subject: [PATCH 161/215] remove storage changes if empty --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 5f98ba2a69d..e736d2d0966 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -313,6 +313,12 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan { storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } + + if (storageChanges.Changes.Count == 0) + { + accountChanges.StorageChanges.Remove([..key]); + } + accountChanges.StorageReads.Add(new(Bytes32.Wrap(storageKey))); return; } From 4023bc2146d24aa658324d53eb8d22f34d8cb473 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 12:03:53 +0100 Subject: [PATCH 162/215] fix storage changes --- .../BlockAccessLists/BlockAccessList.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index e736d2d0966..e8efec3d796 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -303,11 +303,12 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) { - storageChanges = new(storageKey); + accountChanges.StorageChanges.Add(storageKey, new(storageKey)); + storageChanges = accountChanges.StorageChanges[storageKey]; } // storage change edge case - if (!HasStorageChangedDuringTx(accountChanges.Address, [..key], before, after)) + if (!HasStorageChangedDuringTx(accountChanges.Address, storageKey, before, after)) { if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { @@ -330,7 +331,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan } storageChanges.Changes.Add(storageChange); - accountChanges.StorageChanges[storageKey] = storageChanges; + // accountChanges.StorageChanges[storageKey] = storageChanges; _changes.Push(new() { Address = accountChanges.Address, @@ -455,7 +456,7 @@ private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeI private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in ReadOnlySpan beforeInstr, in ReadOnlySpan afterInstr) { AccountChanges accountChanges = _accountChanges[address]; - int count = accountChanges.StorageChanges.Count; + int count = accountChanges.StorageChanges[key].Changes.Count; if (count == 0) { @@ -479,7 +480,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan() && change.PreviousValue is null) { // first change of this transaction & block - return (change.PreTxStorage ?? []).AsSpan() != afterInstr; + return change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr; } } From 77c7130e9a1323f2921b2deae129603e761bb6b7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 12:08:32 +0100 Subject: [PATCH 163/215] only add read if write not present --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index e8efec3d796..3952bda94d2 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -317,10 +317,13 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan if (storageChanges.Changes.Count == 0) { - accountChanges.StorageChanges.Remove([..key]); + accountChanges.StorageChanges.Remove(storageKey); } - accountChanges.StorageReads.Add(new(Bytes32.Wrap(storageKey))); + if (!accountChanges.StorageChanges.ContainsKey(storageKey)) + { + accountChanges.StorageReads.Add(new(Bytes32.Wrap(storageKey))); + } return; } From a6081ae11cdc5040609fe55c106d68cae4e27ed6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:43:05 +0100 Subject: [PATCH 164/215] fix error message --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 3952bda94d2..fdf58ebb10a 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -487,7 +487,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in } } - throw new Exception("Error calculating pre tx balance"); + throw new Exception("Error calculating pre tx storage"); } private enum ChangeType From 41e689d193c67a6d1d20c24dd6dfb8089e4607cd Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:51:17 +0100 Subject: [PATCH 165/215] add changes when resetting to og value --- .../BlockAccessLists/BlockAccessList.cs | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index fdf58ebb10a..1719c1507c6 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using Nethermind.Core.Extensions; using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; @@ -89,6 +90,14 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) { + _changes.Push(new() + { + Address = address, + Type = ChangeType.BalanceChange, + PreviousValue = balanceChanges.Last().Value, + BlockAccessIndex = Index + }); + balanceChanges.RemoveAt(balanceChanges.Count - 1); } return; @@ -312,6 +321,14 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan { if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { + _changes.Push(new() + { + Address = accountChanges.Address, + Type = ChangeType.StorageChange, + PreviousValue = storageChanges.Changes[^1], + BlockAccessIndex = Index + }); + storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } @@ -477,9 +494,14 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in } } - // balance only changed within this transaction + // storage only changed within this transaction foreach (Change change in _changes) { + // tmp + if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan()) + { + Console.WriteLine($"found change with pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={change.PreviousValue}"); + } if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan() && change.PreviousValue is null) { // first change of this transaction & block From bd76f9da3e97be3285a4f5c8e1b64d66b2d78e14 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:15:35 +0100 Subject: [PATCH 166/215] change comparison, add logging --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 1719c1507c6..6d6d855bdb7 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -487,6 +487,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) { + Console.WriteLine("storage change = " + storageChange.ToString()); if (storageChange.BlockAccessIndex != Index) { // storage changed in previous tx in block @@ -498,11 +499,11 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in foreach (Change change in _changes) { // tmp - if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan()) + if (change.Type == ChangeType.StorageChange && change.Address == address) { - Console.WriteLine($"found change with pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={change.PreviousValue}"); + Console.WriteLine($"found change with slot={change.Slot} current={key} pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={change.PreviousValue}"); } - if (change.Type == ChangeType.StorageChange && change.Address == address && change.Slot == key.AsSpan() && change.PreviousValue is null) + if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key) && change.PreviousValue is null) { // first change of this transaction & block return change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr; From eb23d61888a729da128d04b4d6324096f0e63d6f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:34:47 +0100 Subject: [PATCH 167/215] more logging --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 6d6d855bdb7..671cb6b053b 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -487,7 +487,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) { - Console.WriteLine("storage change = " + storageChange.ToString()); + Console.WriteLine($"index={Index} storage change = {storageChange.ToString()}"); if (storageChange.BlockAccessIndex != Index) { // storage changed in previous tx in block @@ -499,9 +499,9 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in foreach (Change change in _changes) { // tmp - if (change.Type == ChangeType.StorageChange && change.Address == address) + if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key)) { - Console.WriteLine($"found change with slot={change.Slot} current={key} pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={change.PreviousValue}"); + Console.WriteLine($"found change with pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={(change.PreviousValue is null ? "null" : change.PreviousValue)}"); } if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key) && change.PreviousValue is null) { From e314c75b2162aac53a282e39e506e5bddece1da3 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:25:46 +0100 Subject: [PATCH 168/215] even more logging --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 671cb6b053b..37195aa43b5 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -319,6 +319,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan // storage change edge case if (!HasStorageChangedDuringTx(accountChanges.Address, storageKey, before, after)) { + Console.WriteLine($"unchanged! {accountChanges.Address} {Bytes.ToHexString(storageKey)}"); if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { _changes.Push(new() @@ -328,12 +329,14 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan PreviousValue = storageChanges.Changes[^1], BlockAccessIndex = Index }); + Console.WriteLine($"removing change: {storageChanges.Changes[^1]}"); storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } if (storageChanges.Changes.Count == 0) { + Console.WriteLine($"changes empty, removing"); accountChanges.StorageChanges.Remove(storageKey); } @@ -505,6 +508,9 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in } if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key) && change.PreviousValue is null) { + Console.WriteLine($"has changed = {change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr}"); + Console.WriteLine($"has changed 2 = {change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray())}"); + // first change of this transaction & block return change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr; } From 8f5604eaa43785c909bcc734d5b80929b7792e94 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:37:31 +0100 Subject: [PATCH 169/215] use sequenceequal --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 37195aa43b5..30072ad5199 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -485,7 +485,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in { // first storage change of block // return storage prior to this instruction - return beforeInstr != afterInstr; + return !Enumerable.SequenceEqual(beforeInstr.ToArray(), afterInstr.ToArray()); } foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) @@ -494,7 +494,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in if (storageChange.BlockAccessIndex != Index) { // storage changed in previous tx in block - return storageChange.NewValue.Unwrap().AsSpan() != afterInstr; + return !Enumerable.SequenceEqual(storageChange.NewValue.Unwrap(), afterInstr.ToArray()); } } @@ -508,11 +508,10 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in } if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key) && change.PreviousValue is null) { - Console.WriteLine($"has changed = {change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr}"); - Console.WriteLine($"has changed 2 = {change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray())}"); + Console.WriteLine($"has changed = {change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray())}"); // first change of this transaction & block - return change.PreTxStorage is null || change.PreTxStorage.AsSpan() != afterInstr; + return change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray()); } } From 689f10c6aaef7f1d32a56ea23da01ff108280a82 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:05:36 +0100 Subject: [PATCH 170/215] check for previous code deployed being the same --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 9 ++++++++- .../Nethermind.Evm/State/TracedAccessWorldState.cs | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 30072ad5199..0b70811fdd3 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -130,7 +130,7 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } - public void AddCodeChange(Address address, byte[] after) + public void AddCodeChange(Address address, byte[] before, byte[] after) { CodeChange codeChange = new() { @@ -144,6 +144,13 @@ public void AddCodeChange(Address address, byte[] after) _accountChanges.Add(address, accountChanges); } + // need more complex logic like for storage & balance changes? + if (Enumerable.SequenceEqual(before, after)) + { + Console.WriteLine("code deployed already the same"); + return; + } + SortedList codeChanges = accountChanges.CodeChanges; if (codeChanges.Count != 0 && codeChanges.Last().Key == Index) { diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index dbf7dc0f2ba..8b465aa7808 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -81,7 +81,8 @@ public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadO { if (Enabled) { - BlockAccessList.AddCodeChange(address, code.ToArray()); + byte[] oldCode = _innerWorldState.GetCode(address) ?? []; + BlockAccessList.AddCodeChange(address, oldCode, code.ToArray()); } return _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); } From 5536c3de141311af83a9d1fa12a3fb662758c6b2 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:36:15 +0100 Subject: [PATCH 171/215] record more account accesses --- .../State/TracedAccessWorldState.cs | 47 ++++++++++++++----- .../Nethermind.Evm/State/WrappedWorldState.cs | 11 ++--- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index 8b465aa7808..b5aed04b4ad 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -99,22 +99,22 @@ public override void Set(in StorageCell storageCell, byte[] newValue) public override UInt256 GetBalance(Address address) { - if (Enabled) - { - BlockAccessList.AddAccountRead(address); - } + AddAccountRead(address); return _innerWorldState.GetBalance(address); } public override ValueHash256 GetCodeHash(Address address) { - if (Enabled) - { - BlockAccessList.AddAccountRead(address); - } + AddAccountRead(address); return _innerWorldState.GetCodeHash(address); } + public override byte[]? GetCode(Address address) + { + AddAccountRead(address); + return _innerWorldState.GetCode(address); + } + public override void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) { if (Enabled) @@ -158,10 +158,7 @@ public override void CreateAccountIfNotExists(Address address, in UInt256 balanc public override bool TryGetAccount(Address address, out AccountStruct account) { - if (Enabled) - { - BlockAccessList.AddAccountRead(address); - } + AddAccountRead(address); return _innerWorldState.TryGetAccount(address, out account); } @@ -188,4 +185,30 @@ public override Snapshot TakeSnapshot(bool newTransactionStart = false) Snapshot snapshot = _innerWorldState.TakeSnapshot(newTransactionStart); return new(snapshot.StorageSnapshot, snapshot.StateSnapshot, blockAccessListSnapshot); } + + public override bool AccountExists(Address address) + { + AddAccountRead(address); + return _innerWorldState.AccountExists(address); + } + + public override bool IsContract(Address address) + { + AddAccountRead(address); + return _innerWorldState.AccountExists(address); + } + + public override bool IsDeadAccount(Address address) + { + AddAccountRead(address); + return _innerWorldState.AccountExists(address); + } + + public override void ClearStorage(Address address) + { + // todo: change all storage slots to nothing + // consensus issue? + AddAccountRead(address); + _innerWorldState.ClearStorage(address); + } } diff --git a/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs b/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs index 5977f182de0..d07e28624e8 100644 --- a/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/WrappedWorldState.cs @@ -7,7 +7,6 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; -using Nethermind.Evm.State; using Nethermind.Evm.Tracing.State; using Nethermind.Int256; @@ -20,7 +19,7 @@ public class WrappedWorldState(IWorldState innerWorldState) : IWorldState public Hash256 StateRoot => _innerWorldState.StateRoot; - public bool AccountExists(Address address) + public virtual bool AccountExists(Address address) => _innerWorldState.AccountExists(address); public virtual void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) @@ -38,7 +37,7 @@ public virtual bool AddToBalanceAndCreateIfNotExists(Address address, in UInt256 public virtual IDisposable BeginScope(BlockHeader? baseBlock) => _innerWorldState.BeginScope(baseBlock); - public void ClearStorage(Address address) + public virtual void ClearStorage(Address address) => _innerWorldState.ClearStorage(address); public virtual void Commit(IReleaseSpec releaseSpec, bool isGenesis = false, bool commitRoots = true) @@ -74,7 +73,7 @@ public virtual ReadOnlySpan Get(in StorageCell storageCell) public virtual UInt256 GetBalance(Address address) => _innerWorldState.GetBalance(address); - public byte[]? GetCode(Address address) + public virtual byte[]? GetCode(Address address) => _innerWorldState.GetCode(address); public byte[]? GetCode(in ValueHash256 codeHash) @@ -101,10 +100,10 @@ public virtual void IncrementNonce(Address address, UInt256 delta, out UInt256 o public virtual bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) => _innerWorldState.InsertCode(address, codeHash, code, spec, isGenesis); - public bool IsContract(Address address) + public virtual bool IsContract(Address address) => _innerWorldState.IsContract(address); - public bool IsDeadAccount(Address address) + public virtual bool IsDeadAccount(Address address) => _innerWorldState.IsDeadAccount(address); public virtual void RecalculateStateRoot() From 8d63f4d2cc9ceab0bbc708af17908a7d96cb0336 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:08:18 +0100 Subject: [PATCH 172/215] extcodesize --- .../Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index c133fc19327..689692755b5 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -234,6 +234,8 @@ public static EvmExceptionType InstructionExtCodeSize( if (!EvmCalculations.ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; + (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); + // Attempt a peephole optimization when tracing is not active and code is available. ReadOnlySpan codeSection = vm.EvmState.Env.CodeInfo.CodeSpan; if (!TTracingInst.IsActive && programCounter < codeSection.Length) From e7b662d5a9b821ad86f96d59efe4b11bf0553ddf Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:09:21 +0100 Subject: [PATCH 173/215] fix tracing --- src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index b5aed04b4ad..55e8f640d5a 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -195,13 +195,13 @@ public override bool AccountExists(Address address) public override bool IsContract(Address address) { AddAccountRead(address); - return _innerWorldState.AccountExists(address); + return _innerWorldState.IsContract(address); } public override bool IsDeadAccount(Address address) { AddAccountRead(address); - return _innerWorldState.AccountExists(address); + return _innerWorldState.IsDeadAccount(address); } public override void ClearStorage(Address address) From 80cadecf66ccceb161f78729f1934a9b93f319c5 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:31:37 +0100 Subject: [PATCH 174/215] remove console logs --- .../BlockAccessLists/BlockAccessList.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 0b70811fdd3..86730959e03 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -147,7 +147,6 @@ public void AddCodeChange(Address address, byte[] before, byte[] after) // need more complex logic like for storage & balance changes? if (Enumerable.SequenceEqual(before, after)) { - Console.WriteLine("code deployed already the same"); return; } @@ -326,7 +325,6 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan // storage change edge case if (!HasStorageChangedDuringTx(accountChanges.Address, storageKey, before, after)) { - Console.WriteLine($"unchanged! {accountChanges.Address} {Bytes.ToHexString(storageKey)}"); if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) { _changes.Push(new() @@ -336,14 +334,12 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan PreviousValue = storageChanges.Changes[^1], BlockAccessIndex = Index }); - Console.WriteLine($"removing change: {storageChanges.Changes[^1]}"); storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); } if (storageChanges.Changes.Count == 0) { - Console.WriteLine($"changes empty, removing"); accountChanges.StorageChanges.Remove(storageKey); } @@ -480,6 +476,7 @@ private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeI } } + // assert error? should never happen throw new Exception("Error calculating pre tx balance"); } @@ -497,7 +494,6 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) { - Console.WriteLine($"index={Index} storage change = {storageChange.ToString()}"); if (storageChange.BlockAccessIndex != Index) { // storage changed in previous tx in block @@ -508,20 +504,14 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in // storage only changed within this transaction foreach (Change change in _changes) { - // tmp - if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key)) - { - Console.WriteLine($"found change with pretx={(change.PreTxStorage is null ? "null" : Bytes.ToHexString(change.PreTxStorage))} afterInstr={Bytes.ToHexString(afterInstr.ToArray())} previousvalue={(change.PreviousValue is null ? "null" : change.PreviousValue)}"); - } if (change.Type == ChangeType.StorageChange && change.Address == address && Enumerable.SequenceEqual(change.Slot!, key) && change.PreviousValue is null) { - Console.WriteLine($"has changed = {change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray())}"); - // first change of this transaction & block return change.PreTxStorage is null || !Enumerable.SequenceEqual(change.PreTxStorage, afterInstr.ToArray()); } } + // assert error? should never happen throw new Exception("Error calculating pre tx storage"); } From 23171a38e64ce25c0114b6caddb93f403928cb11 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 31 Oct 2025 01:19:08 +0000 Subject: [PATCH 175/215] remove coinbase edge case --- .../TransactionProcessing/TransactionProcessor.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 23e871f018b..1e56cac5fc9 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -856,19 +856,7 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec bool gasBeneficiaryNotDestroyed = !substate.DestroyList.Contains(header.GasBeneficiary); if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) { - bool balTracingEnabled = _tracedAccessWorldState is not null && _tracedAccessWorldState.Enabled; - if (balTracingEnabled && fees == 0) - { - // disable BAL tracing for special case of zero value transfer to coinbase - _tracedAccessWorldState.Enabled = false; - } - WorldState.AddToBalanceAndCreateIfNotExists(header.GasBeneficiary!, fees, spec); - - if (balTracingEnabled) - { - _tracedAccessWorldState.Enabled = true; - } } UInt256 eip1559Fees = !tx.IsFree() ? header.BaseFeePerGas * (ulong)spentGas : UInt256.Zero; From fe46806a0ea966aaa63c404dcef89ef89aa78727 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:10:57 +0000 Subject: [PATCH 176/215] bpo forks --- .../Forks/{21_BPO12.cs => 21_BPO2.cs} | 0 .../Nethermind.Specs/Forks/22_BPO3.cs | 24 +++++++++++++++++++ .../Nethermind.Specs/Forks/23_BPO4.cs | 24 +++++++++++++++++++ .../Nethermind.Specs/Forks/24_BPO5.cs | 24 +++++++++++++++++++ .../{20_Amsterdam.cs => 25_Amsterdam.cs} | 2 +- 5 files changed, 73 insertions(+), 1 deletion(-) rename src/Nethermind/Nethermind.Specs/Forks/{21_BPO12.cs => 21_BPO2.cs} (100%) create mode 100644 src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs create mode 100644 src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs create mode 100644 src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs rename src/Nethermind/Nethermind.Specs/Forks/{20_Amsterdam.cs => 25_Amsterdam.cs} (94%) diff --git a/src/Nethermind/Nethermind.Specs/Forks/21_BPO12.cs b/src/Nethermind/Nethermind.Specs/Forks/21_BPO2.cs similarity index 100% rename from src/Nethermind/Nethermind.Specs/Forks/21_BPO12.cs rename to src/Nethermind/Nethermind.Specs/Forks/21_BPO2.cs diff --git a/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs b/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs new file mode 100644 index 00000000000..d0d6d38c4dc --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using Nethermind.Core.Specs; + +namespace Nethermind.Specs.Forks; + +public class BPO3 : BPO2 +{ + private static IReleaseSpec _instance; + + public BPO3() + { + Name = "bpo3"; + MaxBlobCount = 32; + TargetBlobCount = 21; + // change + BlobBaseFeeUpdateFraction = 11684671; + Released = false; + } + + public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new BPO3()); +} diff --git a/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs b/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs new file mode 100644 index 00000000000..c929ef97de5 --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using Nethermind.Core.Specs; + +namespace Nethermind.Specs.Forks; + +public class BPO4 : BPO3 +{ + private static IReleaseSpec _instance; + + public BPO4() + { + Name = "bpo4"; + MaxBlobCount = 48; + TargetBlobCount = 32; + // change + BlobBaseFeeUpdateFraction = 11684671; + Released = false; + } + + public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new BPO4()); +} diff --git a/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs b/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs new file mode 100644 index 00000000000..b966e7b3a83 --- /dev/null +++ b/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading; +using Nethermind.Core.Specs; + +namespace Nethermind.Specs.Forks; + +public class BPO5 : BPO4 +{ + private static IReleaseSpec _instance; + + public BPO5() + { + Name = "bpo5"; + MaxBlobCount = 72; + TargetBlobCount = 48; + // change + BlobBaseFeeUpdateFraction = 11684671; + Released = false; + } + + public new static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, static () => new BPO5()); +} diff --git a/src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs similarity index 94% rename from src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs rename to src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs index ed01d5d6ea3..24d71e16554 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/20_Amsterdam.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs @@ -6,7 +6,7 @@ namespace Nethermind.Specs.Forks; -public class Amsterdam : Osaka +public class Amsterdam : BPO5 { private static IReleaseSpec _instance; From 246ff274411818995ea9e8341da02d4718dcd5fe Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:20:16 +0000 Subject: [PATCH 177/215] base fee update fractions --- src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs | 3 +-- src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs | 3 +-- src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs b/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs index d0d6d38c4dc..4993ba7ca6c 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/22_BPO3.cs @@ -15,8 +15,7 @@ public BPO3() Name = "bpo3"; MaxBlobCount = 32; TargetBlobCount = 21; - // change - BlobBaseFeeUpdateFraction = 11684671; + BlobBaseFeeUpdateFraction = 17805213; Released = false; } diff --git a/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs b/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs index c929ef97de5..4e0b500f294 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/23_BPO4.cs @@ -15,8 +15,7 @@ public BPO4() Name = "bpo4"; MaxBlobCount = 48; TargetBlobCount = 32; - // change - BlobBaseFeeUpdateFraction = 11684671; + BlobBaseFeeUpdateFraction = 26707819; Released = false; } diff --git a/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs b/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs index b966e7b3a83..44dcdc5fcf1 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/24_BPO5.cs @@ -15,8 +15,7 @@ public BPO5() Name = "bpo5"; MaxBlobCount = 72; TargetBlobCount = 48; - // change - BlobBaseFeeUpdateFraction = 11684671; + BlobBaseFeeUpdateFraction = 40061729; Released = false; } From 359df9cdf3f58df6c9e93cc6f4614055b5a16766 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:25:23 +0000 Subject: [PATCH 178/215] add bpos to spec name parser --- src/Nethermind/Nethermind.Specs/SpecNameParser.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs index a3022fe5487..a4030e1274c 100644 --- a/src/Nethermind/Nethermind.Specs/SpecNameParser.cs +++ b/src/Nethermind/Nethermind.Specs/SpecNameParser.cs @@ -54,6 +54,11 @@ public static IReleaseSpec Parse(string specName) "Paris" => Paris.Instance, "Prague" => Prague.Instance, "Osaka" => Osaka.Instance, + "BPO1" => BPO1.Instance, + "BPO2" => BPO2.Instance, + "BPO3" => BPO3.Instance, + "BPO4" => BPO4.Instance, + "BPO5" => BPO5.Instance, "Amsterdam" => Amsterdam.Instance, _ => throw new NotSupportedException() }; From e09553b328c6486eb8f60c2487277fda4f8dca8e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:22:45 +0000 Subject: [PATCH 179/215] change blockchain tests --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 11 +++++------ .../Ethereum.Test.Base/JsonToEthereumTest.cs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index f87aee4623e..ffa1cb8e845 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -86,10 +86,10 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? bool isEngineTest = test.Blocks is null && test.EngineNewPayloads is not null; - List<(ForkActivation Activation, IReleaseSpec Spec)> transitions = - isEngineTest ? - [((ForkActivation)0, test.Network)] : - [((ForkActivation)0, test.GenesisSpec), ((ForkActivation)1, test.Network)]; // TODO: this thing took a lot of time to find after it was removed!, genesis block is always initialized with Frontier + List<(ForkActivation Activation, IReleaseSpec Spec)> transitions = [((ForkActivation)0, test.Network)]; + // isEngineTest ? + // [((ForkActivation)0, test.Network)] : + // [((ForkActivation)0, test.GenesisSpec), ((ForkActivation)1, test.Network)]; // TODO: this thing took a lot of time to find after it was removed!, genesis block is always initialized with Frontier if (test.NetworkAfterTransition is not null) { @@ -98,7 +98,7 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? ISpecProvider specProvider = new CustomSpecProvider(test.ChainId, test.ChainId, transitions.ToArray()); - Assert.That(isEngineTest || test.ChainId == GnosisSpecProvider.Instance.ChainId || specProvider.GenesisSpec == Frontier.Instance, "Expected genesis spec to be Frontier for blockchain tests"); + // Assert.That(isEngineTest || test.ChainId == GnosisSpecProvider.Instance.ChainId || specProvider.GenesisSpec == Frontier.Instance, "Expected genesis spec to be Frontier for blockchain tests"); if (test.Network is Cancun || test.NetworkAfterTransition is Cancun) { @@ -147,7 +147,6 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? BlockchainProcessor blockchainProcessor = (BlockchainProcessor)mainBlockProcessingContext.BlockchainProcessor; IBlockTree blockTree = container.Resolve(); IBlockValidator blockValidator = container.Resolve(); - IEngineRpcModule engineRpcModule = container.Resolve(); blockchainProcessor.Start(); // Register tracer if provided for blocktest tracing diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 0d5f04ae5c8..861b131bb9d 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -414,7 +414,7 @@ public static IEnumerable ConvertToBlockchainTests(string json) } catch (Exception e) { - // Console.WriteLine(e); + Console.WriteLine(e); Dictionary half = _serializer.Deserialize>(json); testsInFile = []; From 8f7c3b1de37653dcf0052c05473141bfe3eba43d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:15:21 +0000 Subject: [PATCH 180/215] fix setnonce tracing --- .../Nethermind.Evm/State/TracedAccessWorldState.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index 55e8f640d5a..7adac90282e 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -77,6 +77,16 @@ public override void IncrementNonce(Address address, UInt256 delta, out UInt256 } } + public override void SetNonce(Address address, in UInt256 nonce) + { + _innerWorldState.SetNonce(address, nonce); + + if (Enabled) + { + BlockAccessList.AddNonceChange(address, (ulong)nonce); + } + } + public override bool InsertCode(Address address, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { if (Enabled) From e4718eb29e09f0017a1a1b1e7a840e0d6e6aaff5 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:29:19 +0000 Subject: [PATCH 181/215] add storage slot to changes in edge case --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 86730959e03..5fcb2c5860f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -331,6 +331,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan { Address = accountChanges.Address, Type = ChangeType.StorageChange, + Slot = storageKey, PreviousValue = storageChanges.Changes[^1], BlockAccessIndex = Index }); From e3d4308791804ac02b1def81373696e142ad1390 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:42:25 +0000 Subject: [PATCH 182/215] generatedblockaccesslist for debugging --- .../Validators/BlockValidator.cs | 1 + src/Nethermind/Nethermind.Core/Block.cs | 4 ++++ .../Nethermind.Facade/Eth/BlockForRpc.cs | 9 ++++++++ .../BlockDecoder.cs | 21 +++++++++++++------ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index ddbc8e1e04e..11f360312bc 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -241,6 +241,7 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B // Console.WriteLine(processedBlock.BlockAccessList.ToString()); if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); + suggestedBlock.GeneratedBlockAccessList = processedBlock.BlockAccessList; } if (receipts.Length != processedBlock.Transactions.Length) diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 78931055c6a..b49989d0db4 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -122,6 +122,10 @@ public Transaction[] Transactions public Hash256? BlockAccessListHash => Header.BlockAccessListHash; // do not add setter here public BlockAccessList? BlockAccessList => Body.BlockAccessList; // do not add setter here + // for debugging by rpc + [JsonIgnore] + public BlockAccessList? GeneratedBlockAccessList { get; set; } + [JsonIgnore] public byte[][]? ExecutionRequests { get; set; } diff --git a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs index 6bafdd3d166..9b906afa334 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs @@ -12,6 +12,7 @@ using System.Text.Json.Serialization; using System.Runtime.CompilerServices; using Nethermind.Facade.Eth.RpcTransaction; +using Nethermind.Core.BlockAccessLists; namespace Nethermind.Facade.Eth; @@ -91,6 +92,8 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s Withdrawals = block.Withdrawals; WithdrawalsRoot = block.Header.WithdrawalsRoot; RequestsHash = block.Header.RequestsHash; + BlockAccessList = block.BlockAccessList; + GeneratedBlockAccessList = block.GeneratedBlockAccessList; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -154,6 +157,12 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Hash256? RequestsHash { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public BlockAccessList? BlockAccessList { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public BlockAccessList? GeneratedBlockAccessList { get; set; } + private static object[] GetTransactionHashes(Transaction[] transactions) { if (transactions.Length == 0) return Array.Empty(); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index faf719c6899..269cf749d01 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -4,13 +4,15 @@ using System; using System.Buffers; using Nethermind.Core; +using Nethermind.Serialization.Rlp.Eip7928; namespace Nethermind.Serialization.Rlp { public sealed class BlockDecoder(IHeaderDecoder headerDecoder) : RlpValueDecoder { private readonly IHeaderDecoder _headerDecoder = headerDecoder ?? throw new ArgumentNullException(nameof(headerDecoder)); - private readonly BlockBodyDecoder _blockBodyDecoder = new BlockBodyDecoder(headerDecoder); + private readonly BlockBodyDecoder _blockBodyDecoder = new(headerDecoder); + private readonly BlockAccessListDecoder _blockAccessListDecoder = new(); public BlockDecoder() : this(new HeaderDecoder()) { } @@ -34,19 +36,20 @@ public BlockDecoder() : this(new HeaderDecoder()) { } return decoded; } - private (int Total, int Txs, int Uncles, int? Withdrawals, int? BlockAccessList) GetContentLength(Block item, RlpBehaviors rlpBehaviors) + private (int Total, int Txs, int Uncles, int? Withdrawals, int? BlockAccessList, int? GeneratedBlockAccessList) GetContentLength(Block item, RlpBehaviors rlpBehaviors) { int headerLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); (int txs, int uncles, int? withdrawals, int? blockAccessList) = _blockBodyDecoder.GetBodyComponentLength(item.Body); - + int? generatedBlockAccessList = item.GeneratedBlockAccessList is null ? null : _blockAccessListDecoder.GetLength(item.GeneratedBlockAccessList.Value, RlpBehaviors.None); int contentLength = headerLength + Rlp.LengthOfSequence(txs) + Rlp.LengthOfSequence(uncles) + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + - (blockAccessList is not null ? Rlp.LengthOfSequence(blockAccessList.Value) : 0); - return (contentLength, txs, uncles, withdrawals, blockAccessList); + (blockAccessList is not null ? Rlp.LengthOfSequence(blockAccessList.Value) : 0) + + (generatedBlockAccessList is not null ? Rlp.LengthOfSequence(generatedBlockAccessList.Value) : 0); + return (contentLength, txs, uncles, withdrawals, blockAccessList, generatedBlockAccessList); } public override int GetLength(Block? item, RlpBehaviors rlpBehaviors) @@ -76,6 +79,7 @@ public override int GetLength(Block? item, RlpBehaviors rlpBehaviors) Block block = new(header, body) { EncodedSize = Rlp.LengthOfSequence(sequenceLength), + GeneratedBlockAccessList = decoderContext.PeekNumberOfItemsRemaining() == 0 ? null : _blockAccessListDecoder.Decode(ref decoderContext, rlpBehaviors), EncodedBlockAccessList = body.BlockAccessList is null ? null : Rlp.Encode(body.BlockAccessList.Value).Bytes // todo: possible without reencoding? }; @@ -102,7 +106,7 @@ public override void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehav return; } - (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength, int? _) = GetContentLength(item, rlpBehaviors); + (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength, int? _, int? _) = GetContentLength(item, rlpBehaviors); stream.StartSequence(contentLength); _headerDecoder.Encode(stream, item.Header); stream.StartSequence(txsLength); @@ -131,6 +135,11 @@ public override void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehav { stream.Encode(item.BlockAccessList.Value); } + + if (item.GeneratedBlockAccessList is not null) + { + stream.Encode(item.GeneratedBlockAccessList.Value); + } } public ReceiptRecoveryBlock? DecodeToReceiptRecoveryBlock(MemoryManager? memoryManager, Memory memory, RlpBehaviors rlpBehaviors) From b0dd675c80c55df5c912959c3ef0b909ca859c64 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:01:52 +0000 Subject: [PATCH 183/215] add testing log for decoding --- src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 269cf749d01..9e9390613e4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -23,6 +23,9 @@ public BlockDecoder() : this(new HeaderDecoder()) { } throw new RlpException($"Received a 0 length stream when decoding a {nameof(Block)}"); } + Console.WriteLine("DECODING BLOCK"); + Console.WriteLine(Convert.ToHexString(rlpStream.Data)); + if (rlpStream.IsNextItemNull()) { rlpStream.ReadByte(); @@ -41,7 +44,7 @@ public BlockDecoder() : this(new HeaderDecoder()) { } int headerLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); (int txs, int uncles, int? withdrawals, int? blockAccessList) = _blockBodyDecoder.GetBodyComponentLength(item.Body); - int? generatedBlockAccessList = item.GeneratedBlockAccessList is null ? null : _blockAccessListDecoder.GetLength(item.GeneratedBlockAccessList.Value, RlpBehaviors.None); + int? generatedBlockAccessList = item.GeneratedBlockAccessList is null ? null : _blockAccessListDecoder.GetLength(item.GeneratedBlockAccessList.Value, rlpBehaviors); int contentLength = headerLength + Rlp.LengthOfSequence(txs) + From 1f064b77b3f1a82f52a62913e023847974a6e7c2 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:11:19 +0000 Subject: [PATCH 184/215] try adding sequence length in block encoding --- src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 9e9390613e4..fd2a164c046 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -109,7 +109,7 @@ public override void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehav return; } - (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength, int? _, int? _) = GetContentLength(item, rlpBehaviors); + (int contentLength, int txsLength, int unclesLength, int? withdrawalsLength, int? balLength, int? genBalLength) = GetContentLength(item, rlpBehaviors); stream.StartSequence(contentLength); _headerDecoder.Encode(stream, item.Header); stream.StartSequence(txsLength); @@ -136,11 +136,13 @@ public override void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehav if (item.BlockAccessList is not null) { + stream.StartSequence(balLength.Value); stream.Encode(item.BlockAccessList.Value); } if (item.GeneratedBlockAccessList is not null) { + stream.StartSequence(genBalLength.Value); stream.Encode(item.GeneratedBlockAccessList.Value); } } From 9da994647dd5e61523f210237ba19db59284b581 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:25:44 +0000 Subject: [PATCH 185/215] try block decoding fix --- .../Nethermind.Serialization.Rlp/BlockDecoder.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index fd2a164c046..1f564a5ca78 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -50,8 +50,8 @@ public BlockDecoder() : this(new HeaderDecoder()) { } Rlp.LengthOfSequence(txs) + Rlp.LengthOfSequence(uncles) + (withdrawals is not null ? Rlp.LengthOfSequence(withdrawals.Value) : 0) + - (blockAccessList is not null ? Rlp.LengthOfSequence(blockAccessList.Value) : 0) + - (generatedBlockAccessList is not null ? Rlp.LengthOfSequence(generatedBlockAccessList.Value) : 0); + (blockAccessList is not null ? blockAccessList.Value : 0) + + (generatedBlockAccessList is not null ? generatedBlockAccessList.Value : 0); return (contentLength, txs, uncles, withdrawals, blockAccessList, generatedBlockAccessList); } @@ -136,13 +136,13 @@ public override void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehav if (item.BlockAccessList is not null) { - stream.StartSequence(balLength.Value); + // stream.StartSequence(balLength.Value); stream.Encode(item.BlockAccessList.Value); } if (item.GeneratedBlockAccessList is not null) { - stream.StartSequence(genBalLength.Value); + // stream.StartSequence(genBalLength.Value); stream.Encode(item.GeneratedBlockAccessList.Value); } } From 0730d447af02461089fd86bff34e8bf33a8de319 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 11 Nov 2025 18:03:30 +0000 Subject: [PATCH 186/215] try fix bal json serialisation --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 5fcb2c5860f..c58edb71fa7 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Text.Json.Serialization; using Nethermind.Core.Extensions; using Nethermind.Int256; @@ -12,7 +13,10 @@ namespace Nethermind.Core.BlockAccessLists; public struct BlockAccessList : IEquatable, IJournal { + [JsonIgnore] public ushort Index = 0; + public readonly IEnumerable AccountChanges => GetAccountChanges(); + private readonly SortedDictionary _accountChanges; private readonly Stack _changes; From d4555b67c048d2d601f8f734aab36a88613987ab Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:53:25 +0000 Subject: [PATCH 187/215] refactor --- .../BlockAccessListTests.cs | 385 +++++++++--------- .../BlockAccessLists/AccountChanges.cs | 199 +++++++-- .../BlockAccessLists/BalanceChange.cs | 4 +- .../BlockAccessLists/BlockAccessList.cs | 332 +++++---------- .../BlockAccessLists/CodeChange.cs | 4 +- .../BlockAccessLists/NonceChange.cs | 4 +- .../BlockAccessLists/SlotChanges.cs | 36 +- .../BlockAccessLists/StorageChange.cs | 11 +- .../BlockAccessLists/StorageRead.cs | 12 +- .../Eip7928/AccountChangesDecoder.cs | 26 +- .../Eip7928/BlockAccessListDecoder.cs | 4 +- .../Eip7928/SlotChangesDecoder.cs | 6 +- .../Eip7928/StorageChangeDecoder.cs | 6 +- .../Eip7928/StorageReadDecoder.cs | 6 +- 14 files changed, 525 insertions(+), 510 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs index 29e04adfcd0..6f97e0f270a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockAccessListTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using Autofac; @@ -25,7 +26,6 @@ using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.Specs.Test; -using Nethermind.State; using NUnit.Framework; //move all to correct folder @@ -92,7 +92,7 @@ public void Can_decode_then_encode_nonce_change() public void Can_decode_then_encode_slot_change() { byte[] slot0 = ToStorageSlot(0); - StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + StorageChange parentHashStorageChange = new(0, Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")); const string rlp = "0xf845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"; Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); @@ -113,7 +113,7 @@ public void Can_decode_then_encode_storage_change() Rlp.ValueDecoderContext ctx = new(Bytes.FromHexString(rlp)); StorageChange storageChange = StorageChangeDecoder.Instance.Decode(ref ctx, RlpBehaviors.None); - StorageChange expected = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); + StorageChange expected = new(0, Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")); Assert.That(storageChange, Is.EqualTo(expected)); string encoded = "0x" + Bytes.ToHexString(Rlp.Encode(storageChange).Bytes); @@ -138,7 +138,6 @@ public void Can_decode_then_encode_code_change() Assert.That(encoded, Is.EqualTo(rlp)); } - [TestCaseSource(nameof(AccountChangesTestSource))] public void Can_decode_then_encode_account_change(string rlp, AccountChanges expected) { @@ -159,23 +158,19 @@ public void Can_encode_then_decode() StorageChange storageChange = new() { BlockAccessIndex = 10, - NewValue = new([.. Enumerable.Repeat(50, 32)]) + NewValue = [.. Enumerable.Repeat(50, 32)] }; byte[] storageChangeBytes = Rlp.Encode(storageChange, RlpBehaviors.None).Bytes; StorageChange storageChangeDecoded = Rlp.Decode(storageChangeBytes, RlpBehaviors.None); Assert.That(storageChange, Is.EqualTo(storageChangeDecoded)); - SlotChanges slotChanges = new() - { - Slot = [.. Enumerable.Repeat(100, 32)], - Changes = [storageChange, storageChange] - }; + SlotChanges slotChanges = new([.. Enumerable.Repeat(100, 32)], [storageChange, storageChange]); byte[] slotChangesBytes = Rlp.Encode(slotChanges, RlpBehaviors.None).Bytes; SlotChanges slotChangesDecoded = Rlp.Decode(slotChangesBytes, RlpBehaviors.None); Assert.That(slotChanges, Is.EqualTo(slotChangesDecoded)); - StorageRead storageRead = new(new Bytes32([.. Enumerable.Repeat(50, 32)])); - StorageRead storageRead2 = new(new Bytes32([.. Enumerable.Repeat(60, 32)])); + StorageRead storageRead = new([.. Enumerable.Repeat(50, 32)]); + StorageRead storageRead2 = new([.. Enumerable.Repeat(60, 32)]); byte[] storageReadBytes = Rlp.Encode(storageRead, RlpBehaviors.None).Bytes; StorageRead storageReadDecoded = Rlp.Decode(storageReadBytes, RlpBehaviors.None); Assert.That(storageRead, Is.EqualTo(storageReadDecoded)); @@ -239,15 +234,14 @@ public void Can_encode_then_decode() { codeChange.BlockAccessIndex, codeChange }, }; - AccountChanges accountChanges = new() - { - Address = TestItem.AddressA, - StorageChanges = storageChangesDict, - StorageReads = [storageRead, storageRead2], - BalanceChanges = balanceChangesList, - NonceChanges = nonceChangesList, - CodeChanges = codeChangesList - }; + AccountChanges accountChanges = new( + TestItem.AddressA, + storageChangesDict, + [storageRead, storageRead2], + balanceChangesList, + nonceChangesList, + codeChangesList + ); byte[] accountChangesBytes = Rlp.Encode(accountChanges, RlpBehaviors.None).Bytes; AccountChanges accountChangesDecoded = Rlp.Decode(accountChangesBytes, RlpBehaviors.None); Assert.That(accountChanges, Is.EqualTo(accountChangesDecoded)); @@ -359,21 +353,21 @@ PUSH1 1 // BlockAccessList blockAccessList = Rlp.Decode(processedBlock.BlockAccessList); BlockAccessList blockAccessList = processedBlock.BlockAccessList!.Value; - Assert.That(blockAccessList.GetAccountChanges().Count, Is.EqualTo(10)); + Assert.That(blockAccessList.AccountChanges.Count, Is.EqualTo(10)); Address newContractAddress = ContractAddress.From(TestItem.AddressA, 1); Address newContractAddress2 = ContractAddress.From(TestItem.AddressA, 2); - AccountChanges addressAChanges = blockAccessList.GetAccountChanges(TestItem.AddressA)!.Value; - AccountChanges addressBChanges = blockAccessList.GetAccountChanges(TestItem.AddressB)!.Value; - AccountChanges addressCChanges = blockAccessList.GetAccountChanges(TestItem.AddressC)!.Value; - AccountChanges addressDChanges = blockAccessList.GetAccountChanges(TestItem.AddressD)!.Value; - AccountChanges newContractChanges = blockAccessList.GetAccountChanges(newContractAddress)!.Value; - AccountChanges newContractChanges2 = blockAccessList.GetAccountChanges(newContractAddress2)!.Value; - AccountChanges eip2935Changes = blockAccessList.GetAccountChanges(Eip2935Constants.BlockHashHistoryAddress)!.Value; - AccountChanges eip4788Changes = blockAccessList.GetAccountChanges(Eip4788Constants.BeaconRootsAddress)!.Value; - AccountChanges eip7002Changes = blockAccessList.GetAccountChanges(Eip7002Constants.WithdrawalRequestPredeployAddress)!.Value; - AccountChanges eip7251Changes = blockAccessList.GetAccountChanges(Eip7251Constants.ConsolidationRequestPredeployAddress)!.Value; + AccountChanges addressAChanges = blockAccessList.GetAccountChanges(TestItem.AddressA)!; + AccountChanges addressBChanges = blockAccessList.GetAccountChanges(TestItem.AddressB)!; + AccountChanges addressCChanges = blockAccessList.GetAccountChanges(TestItem.AddressC)!; + AccountChanges addressDChanges = blockAccessList.GetAccountChanges(TestItem.AddressD)!; + AccountChanges newContractChanges = blockAccessList.GetAccountChanges(newContractAddress)!; + AccountChanges newContractChanges2 = blockAccessList.GetAccountChanges(newContractAddress2)!; + AccountChanges eip2935Changes = blockAccessList.GetAccountChanges(Eip2935Constants.BlockHashHistoryAddress)!; + AccountChanges eip4788Changes = blockAccessList.GetAccountChanges(Eip4788Constants.BeaconRootsAddress)!; + AccountChanges eip7002Changes = blockAccessList.GetAccountChanges(Eip7002Constants.WithdrawalRequestPredeployAddress)!; + AccountChanges eip7251Changes = blockAccessList.GetAccountChanges(Eip7251Constants.ConsolidationRequestPredeployAddress)!; byte[] slot0 = ToStorageSlot(0); byte[] slot1 = ToStorageSlot(1); @@ -381,10 +375,10 @@ PUSH1 1 byte[] slot3 = ToStorageSlot(3); byte[] eip4788Slot1 = ToStorageSlot(timestamp % Eip4788Constants.RingBufferSize); byte[] eip4788Slot2 = ToStorageSlot((timestamp % Eip4788Constants.RingBufferSize) + Eip4788Constants.RingBufferSize); - StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(parentHash.BytesToArray())); - StorageChange calldataStorageChange = new(0, Bytes32.Zero); - StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240"))); - StorageChange zeroStorageChangeEnd = new(3, Bytes32.Zero); + StorageChange parentHashStorageChange = new(0, parentHash.BytesToArray()); + StorageChange calldataStorageChange = new(0, Bytes32.Zero.Unwrap()); + StorageChange timestampStorageChange = new(0, Bytes.FromHexString("0x00000000000000000000000000000000000000000000000000000000000F4240")); + StorageChange zeroStorageChangeEnd = new(3, Bytes32.Zero.Unwrap()); UInt256 addressABalance = _accountBalance - gasPrice * GasCostOf.Transaction; UInt256 addressABalance2 = _accountBalance - gasPrice * gasUsedBeforeFinal; @@ -392,118 +386,108 @@ PUSH1 1 using (Assert.EnterMultipleScope()) { - Assert.That(addressAChanges, Is.EqualTo(new AccountChanges() - { - Address = TestItem.AddressA, - StorageChanges = [], - StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2) }, { 3, new(3, addressABalance3) } }, - NonceChanges = new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) }, { 3, new(3, 3) } }, - CodeChanges = [] - })); - - Assert.That(addressBChanges, Is.EqualTo(new AccountChanges() - { - Address = TestItem.AddressB, - StorageChanges = [], - StorageReads = [], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); - - Assert.That(addressCChanges, Is.EqualTo(new AccountChanges() - { - Address = TestItem.AddressC, - StorageChanges = [], - StorageReads = [], - BalanceChanges = new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsedBeforeFinal)) }, { 3, new(3, new UInt256(gasUsed)) } }, - NonceChanges = [], - CodeChanges = [] - })); - - Assert.That(addressDChanges, Is.EqualTo(new AccountChanges() - { - Address = TestItem.AddressD, - StorageChanges = [], - StorageReads = [], - BalanceChanges = new SortedList { { 4, new(4, 1.GWei()) } }, - NonceChanges = [], - CodeChanges = [] - })); - - Assert.That(newContractChanges, Is.EqualTo(new AccountChanges() - { - Address = newContractAddress, - StorageChanges = [], - StorageReads = [], - BalanceChanges = [], - NonceChanges = new SortedList { { 2, new(2, 1) } }, - CodeChanges = new SortedList { { 2, new(2, Eip2935TestConstants.Code) } } - })); - - Assert.That(newContractChanges2, Is.EqualTo(new AccountChanges() - { - Address = newContractAddress2, - StorageChanges = [], - StorageReads = [ToStorageRead(slot1)], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); - - Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges() - { - Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, - StorageReads = [], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); + Assert.That(addressAChanges, Is.EqualTo(new AccountChanges( + TestItem.AddressA, + [], + [], + new SortedList { { 1, new(1, addressABalance) }, { 2, new(2, addressABalance2) }, { 3, new(3, addressABalance3) } }, + new SortedList { { 1, new(1, 1) }, { 2, new(2, 2) }, { 3, new(3, 3) } }, + [] + ))); + + Assert.That(addressBChanges, Is.EqualTo(new AccountChanges( + TestItem.AddressB, + [], + [], + [], + [], + [] + ))); + + Assert.That(addressCChanges, Is.EqualTo(new AccountChanges( + TestItem.AddressC, + [], + [], + new SortedList { { 1, new(1, new UInt256(GasCostOf.Transaction)) }, { 2, new(2, new UInt256(gasUsedBeforeFinal)) }, { 3, new(3, new UInt256(gasUsed)) } }, + [], + [] + ))); + + Assert.That(addressDChanges, Is.EqualTo(new AccountChanges( + TestItem.AddressD, + [], + [], + new SortedList { { 4, new(4, 1.GWei()) } }, + [], + [] + ))); + + Assert.That(newContractChanges, Is.EqualTo(new AccountChanges( + newContractAddress, + [], + [], + [], + new SortedList { { 2, new(2, 1) } }, + new SortedList { { 2, new(2, Eip2935TestConstants.Code) } } + ))); + + Assert.That(newContractChanges2, Is.EqualTo(new AccountChanges( + newContractAddress2, + [], + new SortedSet { ToStorageRead(slot1) }, + [], + [], + [] + ))); + + Assert.That(eip2935Changes, Is.EqualTo(new AccountChanges( + Eip2935Constants.BlockHashHistoryAddress, + new SortedDictionary(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, + [], + [], + [], + [] + ))); // second storage read is not a change, so not recorded - Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges() - { - Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, - StorageReads = [ToStorageRead(eip4788Slot1), ToStorageRead(eip4788Slot2)], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); + Assert.That(eip4788Changes, Is.EqualTo(new AccountChanges( + Eip4788Constants.BeaconRootsAddress, + new SortedDictionary(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, + new SortedSet { ToStorageRead(eip4788Slot1), ToStorageRead(eip4788Slot2) }, + [], + [], + [] + ))); // storage reads make no changes - Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges() - { - Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageChanges = [], - StorageReads = [ + Assert.That(eip7002Changes, Is.EqualTo(new AccountChanges( + Eip7002Constants.WithdrawalRequestPredeployAddress, + [], + new SortedSet { ToStorageRead(slot0), ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), - ], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); + }, + [], + [], + [] + ))); // storage reads make no changes - Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges() - { - Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageChanges = [], - StorageReads = [ + Assert.That(eip7251Changes, Is.EqualTo(new AccountChanges( + Eip7251Constants.ConsolidationRequestPredeployAddress, + [], + new SortedSet { ToStorageRead(slot0), ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), - ], - BalanceChanges = [], - NonceChanges = [], - CodeChanges = [] - })); + }, + [], + [], + [] + ))); } } @@ -542,7 +526,7 @@ private static StorageRead ToStorageRead(byte[] x) Span newValue = new byte[32]; newValue.Clear(); x.CopyTo(newValue[(32 - x.Length)..]); - return new(Bytes32.Wrap([.. newValue])); + return new([.. newValue]); } private static IEnumerable AccountChangesTestSource @@ -551,25 +535,31 @@ private static IEnumerable AccountChangesTestSource { yield return new TestCaseData( "0xf89f9400000961ef480eb55e80d19ad83579a64c007002c0f884a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003c0c0c0", - new AccountChanges() - { - Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageReads = [ + new AccountChanges( + Eip7002Constants.WithdrawalRequestPredeployAddress, + [], + new SortedSet { ToStorageRead(ToStorageSlot(0)), ToStorageRead(ToStorageSlot(1)), ToStorageRead(ToStorageSlot(2)), ToStorageRead(ToStorageSlot(3)) - ], - }) + }, + [], + [], + [] + )) { TestName = "storage_reads" }; yield return new TestCaseData( "0xf862940000f90827f1c53a10cb7a02335b175320002935f847f845a00000000000000000000000000000000000000000000000000000000000000000e3e280a0c382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fdc0c0c0c0", - new AccountChanges() - { - Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")))]) } }, - }) + new AccountChanges( + Eip2935Constants.BlockHashHistoryAddress, + new SortedDictionary(Bytes.Comparer) { { ToStorageSlot(0), new SlotChanges(ToStorageSlot(0), [new(0, Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))]) } }, + [], + [], + [], + [] + )) { TestName = "storage_changes" }; } } @@ -583,59 +573,78 @@ private static IEnumerable BlockAccessListTestSource byte[] slot2 = ToStorageSlot(2); byte[] slot3 = ToStorageSlot(3); byte[] eip4788Slot1 = ToStorageSlot(0xc); - StorageChange parentHashStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd"))); - StorageChange timestampStorageChange = new(0, Bytes32.Wrap(Bytes.FromHexString("0x000000000000000000000000000000000000000000000000000000000000000c"))); + StorageChange parentHashStorageChange = new(0, Bytes.FromHexString("0xc382836f81d7e4055a0e280268371e17cc69a531efe2abee082e9b922d6050fd")); + StorageChange timestampStorageChange = new(0, Bytes.FromHexString("0x000000000000000000000000000000000000000000000000000000000000000c")); SortedDictionary expectedAccountChanges = new() { - {Eip7002Constants.WithdrawalRequestPredeployAddress, new() - { - Address = Eip7002Constants.WithdrawalRequestPredeployAddress, - StorageReads = [ + {Eip7002Constants.WithdrawalRequestPredeployAddress, new( + Eip7002Constants.WithdrawalRequestPredeployAddress, + [], + new SortedSet { ToStorageRead(slot0), ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), - ], - }}, - {Eip7251Constants.ConsolidationRequestPredeployAddress, new() - { - Address = Eip7251Constants.ConsolidationRequestPredeployAddress, - StorageReads = [ + }, + [], + [], + [] + )}, + {Eip7251Constants.ConsolidationRequestPredeployAddress, new( + Eip7251Constants.ConsolidationRequestPredeployAddress, + [], + new SortedSet { ToStorageRead(slot0), ToStorageRead(slot1), ToStorageRead(slot2), ToStorageRead(slot3), - ], - }}, - {Eip2935Constants.BlockHashHistoryAddress, new() - { - Address = Eip2935Constants.BlockHashHistoryAddress, - StorageChanges = new(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, - }}, - {Eip4788Constants.BeaconRootsAddress, new() - { - Address = Eip4788Constants.BeaconRootsAddress, - StorageChanges = new(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, - StorageReads = [ + }, + [], + [], + [] + )}, + {Eip2935Constants.BlockHashHistoryAddress, new( + Eip2935Constants.BlockHashHistoryAddress, + new SortedDictionary(Bytes.Comparer) { { slot0, new SlotChanges(slot0, [parentHashStorageChange]) } }, + [], + [], + [], + [] + )}, + {Eip4788Constants.BeaconRootsAddress, new( + Eip4788Constants.BeaconRootsAddress, + new SortedDictionary(Bytes.Comparer) { { eip4788Slot1, new SlotChanges(eip4788Slot1, [timestampStorageChange]) } }, + new SortedSet { ToStorageRead([0x20, 0x0b]) - ], - }}, - {new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), new() - { - Address = new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - BalanceChanges = new SortedList { { 1, new(1, 0x1319718811c8) } }, - }}, - {new("0xaccc7d92b051544a255b8a899071040739bada75"), new() - { - Address = new("0xaccc7d92b051544a255b8a899071040739bada75"), - NonceChanges = new SortedList { { 1, new(1, 1) } }, - BalanceChanges = new SortedList { { 1, new(1, new(Bytes.FromHexString("0x3635c99aac6d15af9c"))) } }, - }}, - {new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), new() - { - Address = new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), - BalanceChanges = new SortedList { { 1, new(1, 0x64) } }, - }}, + }, + [], + [], + [] + )}, + {new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), new( + new("0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + [], + [], + new SortedList { { 1, new(1, 0x1319718811c8) } }, + [], + [] + )}, + {new("0xaccc7d92b051544a255b8a899071040739bada75"), new( + new("0xaccc7d92b051544a255b8a899071040739bada75"), + [], + [], + new SortedList { { 1, new(1, new(Bytes.FromHexString("0x3635c99aac6d15af9c"))) } }, + new SortedList { { 1, new(1, 1) } }, + [] + )}, + {new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), new( + new("0xd9c0e57d447779673b236c7423aeab84e931f3ba"), + [], + [], + new SortedList { { 1, new(1, 0x64) } }, + [], + [] + )}, }; BlockAccessList expected = new(expectedAccountChanges); yield return new TestCaseData( diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index edb2e54ba85..77d289a9620 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -4,62 +4,80 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Text.Json.Serialization; using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; -public readonly struct AccountChanges : IEquatable +public class AccountChanges : IEquatable { public Address Address { get; init; } - public SortedDictionary StorageChanges { get; init; } - public SortedSet StorageReads { get; init; } - public SortedList BalanceChanges { get; init; } - public SortedList NonceChanges { get; init; } - public SortedList CodeChanges { get; init; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable StorageChanges => _storageChanges.Values; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable StorageReads => _storageReads; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable BalanceChanges => _balanceChanges.Values; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable NonceChanges => _nonceChanges.Values; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public IEnumerable CodeChanges => _codeChanges.Values; + + private readonly SortedDictionary _storageChanges; + private readonly SortedSet _storageReads; + private readonly SortedList _balanceChanges; + private readonly SortedList _nonceChanges; + private readonly SortedList _codeChanges; public AccountChanges() { Address = Address.Zero; - StorageChanges = new(Bytes.Comparer); - StorageReads = []; - BalanceChanges = []; - NonceChanges = []; - CodeChanges = []; + _storageChanges = new(Bytes.Comparer); + _storageReads = []; + _balanceChanges = []; + _nonceChanges = []; + _codeChanges = []; } public AccountChanges(Address address) { Address = address; - StorageChanges = new(Bytes.Comparer); - StorageReads = []; - BalanceChanges = []; - NonceChanges = []; - CodeChanges = []; + _storageChanges = new(Bytes.Comparer); + _storageReads = []; + _balanceChanges = []; + _nonceChanges = []; + _codeChanges = []; } public AccountChanges(Address address, SortedDictionary storageChanges, SortedSet storageReads, SortedList balanceChanges, SortedList nonceChanges, SortedList codeChanges) { Address = address; - StorageChanges = storageChanges; - StorageReads = storageReads; - BalanceChanges = balanceChanges; - NonceChanges = nonceChanges; - CodeChanges = codeChanges; + _storageChanges = storageChanges; + _storageReads = storageReads; + _balanceChanges = balanceChanges; + _nonceChanges = nonceChanges; + _codeChanges = codeChanges; } - public readonly bool Equals(AccountChanges other) => + public bool Equals(AccountChanges? other) => + other is not null && Address == other.Address && - StorageChanges.Values.SequenceEqual(other.StorageChanges.Values) && + StorageChanges.SequenceEqual(other.StorageChanges) && StorageReads.SequenceEqual(other.StorageReads) && BalanceChanges.SequenceEqual(other.BalanceChanges) && NonceChanges.SequenceEqual(other.NonceChanges) && CodeChanges.SequenceEqual(other.CodeChanges); - public override readonly bool Equals(object? obj) => + public override bool Equals(object? obj) => obj is AccountChanges other && Equals(other); - - public override readonly int GetHashCode() => + public override int GetHashCode() => Address.GetHashCode(); public static bool operator ==(AccountChanges left, AccountChanges right) => @@ -68,18 +86,123 @@ public override readonly int GetHashCode() => public static bool operator !=(AccountChanges left, AccountChanges right) => !(left == right); - public override readonly string? ToString() + // n.b. implies that length of changes is zero + public bool HasStorageChange(byte[] key) + => _storageChanges.ContainsKey(key); + + public bool TryGetSlotChanges(byte[] key, [NotNullWhen(true)] out SlotChanges? slotChanges) + => _storageChanges.TryGetValue(key, out slotChanges); + + public void ClearSlotChangesIfEmpty(byte[] key) { - string storageChangesList = string.Join(",\n\t\t\t", [.. StorageChanges.Values.Select(s => s.ToString())]); - string storageChanges = StorageChanges.Count == 0 ? "[] #storage_changes" : $"[ #storage_changes\n\t\t\t{storageChangesList}\n\t\t]"; - string storageReadsList = string.Join(",\n\t\t\t", [.. StorageReads.Select(s => s.ToString())]); - string storageReads = StorageReads.Count == 0 ? "[] #storage_reads" : $"[ #storage_reads\n\t\t\t{storageReadsList}\n\t\t]"; - string balanceChangesList = string.Join(",\n\t\t\t", [.. BalanceChanges.Values.Select(s => s.ToString())]); - string balanceChanges = BalanceChanges.Count == 0 ? "[] #balance_changes" : $"[ #balance_changes\n\t\t\t{balanceChangesList}\n\t\t]"; - string nonceChangesList = string.Join(",\n\t\t\t", [.. NonceChanges.Values.Select(s => s.ToString())]); - string nonceChanges = NonceChanges.Count == 0 ? "[] #nonce_changes" : $"[ #nonce_changes\n\t\t\t{nonceChangesList}\n\t\t]"; - string codeChangesList = string.Join(",\n\t\t\t", [.. CodeChanges.Values.Select(s => s.ToString())]); - string codeChanges = CodeChanges.Count == 0 ? "[] #code_changes" : $"[ #code_changes\n\t\t\t{codeChangesList}\n\t\t]"; - return $"\t[\n\t\t{Address},\n\t\t{storageChanges},\n\t\t{storageReads},\n\t\t{balanceChanges},\n\t\t{nonceChanges},\n\t\t{codeChanges}\n\t]"; + if (TryGetSlotChanges(key, out SlotChanges? slotChanges) && slotChanges.Changes.Count == 0) + { + _storageChanges.Remove(key); + } } + + public SlotChanges GetOrAddSlotChanges(byte[] key) + { + if (!_storageChanges.TryGetValue(key, out SlotChanges? existing)) + { + SlotChanges slotChanges = new(key); + _storageChanges.Add(key, slotChanges); + return slotChanges; + } + return existing; + } + + public void AddStorageRead(byte[] key) + => _storageReads.Add(new(key)); + + public void RemoveStorageRead(byte[] key) + => _storageReads.Remove(new(key)); + + public void SelfDestruct() + { + foreach (byte[] key in _storageChanges.Keys) + { + AddStorageRead(key); + } + + _storageChanges.Clear(); + _nonceChanges.Clear(); + _codeChanges.Clear(); + } + + public void AddBalanceChange(BalanceChange balanceChange) + => _balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); + + public bool PopBalanceChange(ushort index, [NotNullWhen(true)] out BalanceChange? balanceChange) + { + balanceChange = null; + if (PopChange(_balanceChanges, index, out BalanceChange change)) + { + balanceChange = change; + return true; + } + return false; + } + + public void AddNonceChange(NonceChange nonceChange) + => _nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); + + public bool PopNonceChange(ushort index, [NotNullWhen(true)] out NonceChange? nonceChange) + { + nonceChange = null; + if (PopChange(_nonceChanges, index, out NonceChange change)) + { + nonceChange = change; + return true; + } + return false; + } + + public void AddCodeChange(CodeChange codeChange) + => _codeChanges.Add(codeChange.BlockAccessIndex, codeChange); + + public bool PopCodeChange(ushort index, [NotNullWhen(true)] out CodeChange? codeChange) + { + codeChange = null; + if (PopChange(_codeChanges, index, out CodeChange change)) + { + codeChange = change; + return true; + } + return false; + } + + private static bool PopChange(SortedList changes, ushort index, [NotNullWhen(true)] out T? change) where T : IIndexedChange + { + change = default; + + if (changes.Count == 0) + return false; + + KeyValuePair lastChange = changes.Last(); + + if (lastChange.Key == index) + { + changes.RemoveAt(changes.Count - 1); + change = lastChange.Value; + return true; + } + + return false; + } + + // public override string? ToString() + // { + // string storageChangesList = string.Join(",\n\t\t\t", [.. StorageChanges.Values.Select(s => s.ToString())]); + // string storageChanges = StorageChanges.Count == 0 ? "[] #storage_changes" : $"[ #storage_changes\n\t\t\t{storageChangesList}\n\t\t]"; + // string storageReadsList = string.Join(",\n\t\t\t", [.. StorageReads.Select(s => s.ToString())]); + // string storageReads = StorageReads.Count == 0 ? "[] #storage_reads" : $"[ #storage_reads\n\t\t\t{storageReadsList}\n\t\t]"; + // string balanceChangesList = string.Join(",\n\t\t\t", [.. BalanceChanges.Values.Select(s => s.ToString())]); + // string balanceChanges = BalanceChanges.Count == 0 ? "[] #balance_changes" : $"[ #balance_changes\n\t\t\t{balanceChangesList}\n\t\t]"; + // string nonceChangesList = string.Join(",\n\t\t\t", [.. NonceChanges.Values.Select(s => s.ToString())]); + // string nonceChanges = NonceChanges.Count == 0 ? "[] #nonce_changes" : $"[ #nonce_changes\n\t\t\t{nonceChangesList}\n\t\t]"; + // string codeChangesList = string.Join(",\n\t\t\t", [.. CodeChanges.Values.Select(s => s.ToString())]); + // string codeChanges = CodeChanges.Count == 0 ? "[] #code_changes" : $"[ #code_changes\n\t\t\t{codeChangesList}\n\t\t]"; + // return $"\t[\n\t\t{Address},\n\t\t{storageChanges},\n\t\t{storageReads},\n\t\t{balanceChanges},\n\t\t{nonceChanges},\n\t\t{codeChanges}\n\t]"; + // } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index 67408d2a81d..032159f5290 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -27,6 +27,6 @@ public override readonly int GetHashCode() => public static bool operator !=(BalanceChange left, BalanceChange right) => !(left == right); - public override readonly string? ToString() - => $"{BlockAccessIndex}, {PostBalance}"; + // public override readonly string? ToString() + // => $"{BlockAccessIndex}, {PostBalance}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index c58edb71fa7..2db0c1651ac 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -6,7 +6,6 @@ using System.Data; using System.Linq; using System.Text.Json.Serialization; -using Nethermind.Core.Extensions; using Nethermind.Int256; namespace Nethermind.Core.BlockAccessLists; @@ -15,7 +14,7 @@ public struct BlockAccessList : IEquatable, IJournal { [JsonIgnore] public ushort Index = 0; - public readonly IEnumerable AccountChanges => GetAccountChanges(); + public readonly IEnumerable AccountChanges => _accountChanges.Values; private readonly SortedDictionary _accountChanges; private readonly Stack _changes; @@ -47,8 +46,7 @@ public override readonly int GetHashCode() => public static bool operator !=(BlockAccessList left, BlockAccessList right) => !(left == right); - public readonly IEnumerable GetAccountChanges() => _accountChanges.Values; - public readonly AccountChanges? GetAccountChanges(Address address) => _accountChanges.TryGetValue(address, out AccountChanges value) ? value : null; + public readonly AccountChanges? GetAccountChanges(Address address) => _accountChanges.TryGetValue(address, out AccountChanges? value) ? value : null; public void IncrementBlockAccessIndex() { @@ -75,11 +73,7 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) PostBalance = after }; - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); // don't add zero balance transfers, but add empty account changes if (before == after) @@ -87,51 +81,22 @@ public void AddBalanceChange(Address address, UInt256 before, UInt256 after) return; } - SortedList balanceChanges = accountChanges.BalanceChanges; + bool changedDuringTx = HasBalanceChangedDuringTx(address, before, after); + accountChanges.PopBalanceChange(Index, out BalanceChange? oldBalanceChange); - // balance change edge case - if (!HasBalanceChangedDuringTx(address, before, after)) + _changes.Push(new() { - if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) - { - _changes.Push(new() - { - Address = address, - Type = ChangeType.BalanceChange, - PreviousValue = balanceChanges.Last().Value, - BlockAccessIndex = Index - }); - - balanceChanges.RemoveAt(balanceChanges.Count - 1); - } - return; - } + Address = address, + Type = ChangeType.BalanceChange, + PreviousValue = oldBalanceChange, + PreTxBalance = before, + BlockAccessIndex = Index + }); - if (balanceChanges.Count != 0 && balanceChanges.Last().Key == Index) + if (changedDuringTx) { - _changes.Push(new() - { - Address = address, - Type = ChangeType.BalanceChange, - PreviousValue = balanceChanges.Last().Value, - BlockAccessIndex = Index - }); - - balanceChanges.RemoveAt(balanceChanges.Count - 1); + accountChanges.AddBalanceChange(balanceChange); } - else - { - _changes.Push(new() - { - Address = address, - Type = ChangeType.BalanceChange, - PreviousValue = null, - PreTxBalance = before, - BlockAccessIndex = Index - }); - } - - balanceChanges.Add(balanceChange.BlockAccessIndex, balanceChange); } public void AddCodeChange(Address address, byte[] before, byte[] after) @@ -142,39 +107,24 @@ public void AddCodeChange(Address address, byte[] before, byte[] after) NewCode = after }; - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); + // is needed? // need more complex logic like for storage & balance changes? if (Enumerable.SequenceEqual(before, after)) { return; } - SortedList codeChanges = accountChanges.CodeChanges; - if (codeChanges.Count != 0 && codeChanges.Last().Key == Index) - { - _changes.Push(new() - { - Address = address, - Type = ChangeType.CodeChange, - PreviousValue = codeChanges.Last().Value - }); - codeChanges.RemoveAt(codeChanges.Count - 1); - } - else + accountChanges.PopCodeChange(Index, out CodeChange? oldCodeChange); + _changes.Push(new() { - _changes.Push(new() - { - Address = address, - Type = ChangeType.CodeChange, - PreviousValue = null - }); - } - codeChanges.Add(codeChange.BlockAccessIndex, codeChange); + Address = address, + Type = ChangeType.CodeChange, + PreviousValue = oldCodeChange + }); + + accountChanges.AddCodeChange(codeChange); } public void AddNonceChange(Address address, ulong newNonce) @@ -190,33 +140,17 @@ public void AddNonceChange(Address address, ulong newNonce) NewNonce = newNonce }; - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); - SortedList nonceChanges = accountChanges.NonceChanges; - if (nonceChanges.Count != 0 && nonceChanges.Last().Key == Index) - { - _changes.Push(new() - { - Address = address, - Type = ChangeType.NonceChange, - PreviousValue = nonceChanges.Last().Value - }); - nonceChanges.RemoveAt(nonceChanges.Count - 1); - } - else + accountChanges.PopNonceChange(Index, out NonceChange? oldNonceChange); + _changes.Push(new() { - _changes.Push(new() - { - Address = address, - Type = ChangeType.NonceChange, - PreviousValue = null - }); - } - nonceChanges.Add(nonceChange.BlockAccessIndex, nonceChange); + Address = address, + Type = ChangeType.NonceChange, + PreviousValue = oldNonceChange + }); + + accountChanges.AddNonceChange(nonceChange); } public readonly void AddAccountRead(Address address) @@ -229,11 +163,7 @@ public readonly void AddAccountRead(Address address) public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan before, ReadOnlySpan after) { - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); if (before != after) { @@ -246,12 +176,7 @@ public void AddStorageChange(Address address, UInt256 storageIndex, ReadOnlySpan public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] after) { Address address = storageCell.Address; - - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); if (before is null || !Enumerable.SequenceEqual(before, after)) { @@ -261,199 +186,139 @@ public void AddStorageChange(in StorageCell storageCell, byte[] before, byte[] a } } - public void AddStorageRead(in StorageCell storageCell) + public readonly void AddStorageRead(in StorageCell storageCell) { byte[] key = new byte[32]; storageCell.Index.ToBigEndian(key); AddStorageRead(storageCell.Address, key); } - public void AddStorageRead(Address address, byte[] key) + public readonly void AddStorageRead(Address address, byte[] key) { - StorageRead storageRead = new() - { - Key = new(key) - }; - - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - } + AccountChanges accountChanges = GetOrAddAccountChanges(address); - if (!accountChanges.StorageChanges.ContainsKey(key)) + if (!accountChanges.HasStorageChange(key)) { - accountChanges.StorageReads.Add(storageRead); + accountChanges.AddStorageRead(key); } } public readonly void DeleteAccount(Address address) { - if (!_accountChanges.TryGetValue(address, out AccountChanges accountChanges)) - { - accountChanges = new(address); - _accountChanges.Add(address, accountChanges); - return; - } - - foreach (byte[] key in accountChanges.StorageChanges.Keys) - { - accountChanges.StorageReads.Add(new(Bytes32.Wrap(key))); - } - - accountChanges.StorageChanges.Clear(); - accountChanges.NonceChanges.Clear(); - accountChanges.CodeChanges.Clear(); + AccountChanges accountChanges = GetOrAddAccountChanges(address); + accountChanges.SelfDestruct(); } private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan before, in ReadOnlySpan after) { - Span newValue = stackalloc byte[32]; - newValue.Clear(); - after.CopyTo(newValue[(32 - after.Length)..]); - StorageChange storageChange = new() - { - BlockAccessIndex = Index, - NewValue = new(newValue.ToArray()) - }; - byte[] storageKey = [.. key]; - StorageChange? previousStorage = null; - - if (!accountChanges.StorageChanges.TryGetValue(storageKey, out SlotChanges storageChanges)) - { - accountChanges.StorageChanges.Add(storageKey, new(storageKey)); - storageChanges = accountChanges.StorageChanges[storageKey]; - } - - // storage change edge case - if (!HasStorageChangedDuringTx(accountChanges.Address, storageKey, before, after)) - { - if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) - { - _changes.Push(new() - { - Address = accountChanges.Address, - Type = ChangeType.StorageChange, - Slot = storageKey, - PreviousValue = storageChanges.Changes[^1], - BlockAccessIndex = Index - }); - - storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); - } - - if (storageChanges.Changes.Count == 0) - { - accountChanges.StorageChanges.Remove(storageKey); - } - - if (!accountChanges.StorageChanges.ContainsKey(storageKey)) - { - accountChanges.StorageReads.Add(new(Bytes32.Wrap(storageKey))); - } - return; - } + SlotChanges slotChanges = accountChanges.GetOrAddSlotChanges(storageKey); - if (storageChanges.Changes is not [] && storageChanges.Changes[^1].BlockAccessIndex == Index) - { - previousStorage = storageChanges.Changes[^1]; - storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); - } + bool changedDuringTx = HasStorageChangedDuringTx(accountChanges.Address, storageKey, before, after); + slotChanges.PopStorageChange(Index, out StorageChange? oldStorageChange); - storageChanges.Changes.Add(storageChange); - // accountChanges.StorageChanges[storageKey] = storageChanges; _changes.Push(new() { Address = accountChanges.Address, + BlockAccessIndex = Index, Slot = storageKey, Type = ChangeType.StorageChange, - PreviousValue = previousStorage, + PreviousValue = oldStorageChange, PreTxStorage = [.. before] }); - accountChanges.StorageReads.Remove(new(Bytes32.Wrap(storageKey))); + if (changedDuringTx) + { + byte[] newValue = new byte[32]; + after.CopyTo(newValue.AsSpan()[(32 - after.Length)..]); + StorageChange storageChange = new() + { + BlockAccessIndex = Index, + NewValue = newValue + }; + + slotChanges.Changes.Add(storageChange); + accountChanges.RemoveStorageRead(storageKey); + } + else + { + accountChanges.ClearSlotChangesIfEmpty(storageKey); + if (!accountChanges.HasStorageChange(storageKey)) + { + accountChanges.AddStorageRead(storageKey); + } + } } public readonly int TakeSnapshot() => _changes.Count; - // todo: can simplify to wipe all balance, code, nonce changes? - public void Restore(int snapshot) + public readonly void Restore(int snapshot) { snapshot = int.Max(0, snapshot); while (_changes.Count > snapshot) { Change change = _changes.Pop(); - int count; + AccountChanges accountChanges = _accountChanges[change.Address]; + // int count; switch (change.Type) { case ChangeType.BalanceChange: BalanceChange? previousBalance = change.PreviousValue is null ? null : (BalanceChange)change.PreviousValue; - SortedList balanceChanges = _accountChanges[change.Address].BalanceChanges; // balance could have gone back to pre-tx value - // so would already be removed - count = balanceChanges.Count; - if (count > 0 && balanceChanges.Last().Key == change.BlockAccessIndex) - { - balanceChanges.RemoveAt(balanceChanges.Count - 1); - } - + // so would already be empty + accountChanges.PopBalanceChange(change.BlockAccessIndex, out _); // todo: this index must be the same? if (previousBalance is not null) { - balanceChanges.Add(Index, previousBalance.Value); + accountChanges.AddBalanceChange(previousBalance.Value); } break; case ChangeType.CodeChange: CodeChange? previousCode = change.PreviousValue is null ? null : (CodeChange)change.PreviousValue; - SortedList codeChanges = _accountChanges[change.Address].CodeChanges; - codeChanges.RemoveAt(codeChanges.Count - 1); + accountChanges.PopCodeChange(Index, out _); if (previousCode is not null) { - codeChanges.Add(Index, previousCode.Value); + accountChanges.AddCodeChange(previousCode.Value); } break; case ChangeType.NonceChange: - NonceChange? previousNode = change.PreviousValue is null ? null : (NonceChange)change.PreviousValue; - SortedList nonceChanges = _accountChanges[change.Address].NonceChanges; + NonceChange? previousNonce = change.PreviousValue is null ? null : (NonceChange)change.PreviousValue; - nonceChanges.RemoveAt(nonceChanges.Count - 1); - if (previousNode is not null) + accountChanges.PopNonceChange(Index, out _); + if (previousNonce is not null) { - nonceChanges.Add(Index, previousNode.Value); + accountChanges.AddNonceChange(previousNonce.Value); } break; case ChangeType.StorageChange: StorageChange? previousStorage = change.PreviousValue is null ? null : (StorageChange)change.PreviousValue; - SlotChanges storageChanges = _accountChanges[change.Address].StorageChanges[change.Slot!]; + accountChanges.TryGetSlotChanges(change.Slot!, out SlotChanges? slotChanges); // replace change with read - _accountChanges[change.Address].StorageReads.Add(new(Bytes32.Wrap(change.Slot!))); + accountChanges.AddStorageRead(change.Slot!); - storageChanges.Changes.RemoveAt(storageChanges.Changes.Count - 1); + slotChanges!.PopStorageChange(Index, out _); if (previousStorage is not null) { - storageChanges.Changes.Add(previousStorage.Value); + slotChanges.Changes.Add(previousStorage.Value); } - if (storageChanges.Changes.Count == 0) - { - _accountChanges[change.Address].StorageChanges.Remove(change.Slot!); - } + accountChanges.ClearSlotChangesIfEmpty(change.Slot!); break; } } } public override readonly string? ToString() - => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; + => ""; // json serialise + // => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeInstr, UInt256 afterInstr) { AccountChanges accountChanges = _accountChanges[address]; - int count = accountChanges.BalanceChanges.Count; + int count = accountChanges.BalanceChanges.Count(); if (count == 0) { @@ -462,7 +327,7 @@ private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeI return beforeInstr != afterInstr; } - foreach (BalanceChange balanceChange in accountChanges.BalanceChanges.Values.Reverse()) + foreach (BalanceChange balanceChange in accountChanges.BalanceChanges.Reverse()) { if (balanceChange.BlockAccessIndex != Index) { @@ -488,21 +353,20 @@ private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeI private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in ReadOnlySpan beforeInstr, in ReadOnlySpan afterInstr) { AccountChanges accountChanges = _accountChanges[address]; - int count = accountChanges.StorageChanges[key].Changes.Count; - if (count == 0) + if (!accountChanges.TryGetSlotChanges(key, out SlotChanges? slotChanges)) { // first storage change of block // return storage prior to this instruction return !Enumerable.SequenceEqual(beforeInstr.ToArray(), afterInstr.ToArray()); } - foreach (StorageChange storageChange in accountChanges.StorageChanges[key].Changes.AsEnumerable().Reverse()) + foreach (StorageChange storageChange in slotChanges.Changes.AsEnumerable().Reverse()) { if (storageChange.BlockAccessIndex != Index) { // storage changed in previous tx in block - return !Enumerable.SequenceEqual(storageChange.NewValue.Unwrap(), afterInstr.ToArray()); + return !Enumerable.SequenceEqual(storageChange.NewValue, afterInstr.ToArray()); } } @@ -520,6 +384,17 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in throw new Exception("Error calculating pre tx storage"); } + private readonly AccountChanges GetOrAddAccountChanges(Address address) + { + if (!_accountChanges.TryGetValue(address, out AccountChanges? existing)) + { + AccountChanges accountChanges = new(address); + _accountChanges.Add(address, accountChanges); + return accountChanges; + } + return existing; + } + private enum ChangeType { BalanceChange = 0, @@ -527,6 +402,7 @@ private enum ChangeType NonceChange = 2, StorageChange = 3 } + private readonly struct Change { public Address Address { get; init; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index 32767d88ca6..ee8e6cfa5f1 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -38,6 +38,6 @@ private static bool CompareByteArrays(byte[]? left, byte[]? right) => public static bool operator !=(CodeChange left, CodeChange right) => !(left == right); - public override readonly string? ToString() - => $"{BlockAccessIndex}, 0x{Bytes.ToHexString(NewCode)}"; + // public override readonly string? ToString() + // => $"{BlockAccessIndex}, 0x{Bytes.ToHexString(NewCode)}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs index d43d6c8c8c4..b5093565f56 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -26,6 +26,6 @@ public override readonly int GetHashCode() => public static bool operator !=(NonceChange left, NonceChange right) => !(left == right); - public override readonly string? ToString() - => $"{BlockAccessIndex}, {NewNonce}"; + // public override readonly string? ToString() + // => $"{BlockAccessIndex}, {NewNonce}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs index 9f1368097ef..9d3561eab10 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -4,12 +4,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; -using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; -public readonly struct SlotChanges(byte[] slot, List changes) : IEquatable +public class SlotChanges(byte[] slot, List changes) : IEquatable { public byte[] Slot { get; init; } = slot; public List Changes { get; init; } = changes; @@ -18,14 +18,15 @@ public SlotChanges(byte[] slot) : this(slot, []) { } - public readonly bool Equals(SlotChanges other) => + public bool Equals(SlotChanges? other) => + other is not null && CompareByteArrays(Slot, other.Slot) && Changes.SequenceEqual(other.Changes); - public override readonly bool Equals(object? obj) => + public override bool Equals(object? obj) => obj is SlotChanges other && Equals(other); - public override readonly int GetHashCode() => + public override int GetHashCode() => HashCode.Combine(Slot, Changes); private static bool CompareByteArrays(byte[]? left, byte[]? right) => @@ -43,9 +44,28 @@ private static bool CompareByteArrays(byte[]? left, byte[]? right) => public static bool operator !=(SlotChanges left, SlotChanges right) => !(left == right); - public override readonly string? ToString() + public bool PopStorageChange(ushort index, [NotNullWhen(true)] out StorageChange? storageChange) { - string changes = string.Join(", ", [.. Changes.Select(s => s.ToString())]); - return $"[0x{Bytes.ToHexString(Slot)}, [{changes}]]"; + storageChange = null; + + if (Changes.Count == 0) + return false; + + StorageChange lastChange = Changes.Last(); + + if (lastChange.BlockAccessIndex == index) + { + Changes.RemoveAt(Changes.Count - 1); + storageChange = lastChange; + return true; + } + + return false; } + + // public override readonly string? ToString() + // { + // string changes = string.Join(", ", [.. Changes.Select(s => s.ToString())]); + // return $"[0x{Bytes.ToHexString(Slot)}, [{changes}]]"; + // } } \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index d8f06595771..3cc4c582a4f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -7,14 +7,14 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct StorageChange(ushort blockAccessIndex, Bytes32 newValue) : IEquatable, IIndexedChange +public readonly struct StorageChange(ushort blockAccessIndex, byte[] newValue) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; - public Bytes32 NewValue { get; init; } = newValue; + public byte[] NewValue { get; init; } = newValue; public readonly bool Equals(StorageChange other) => BlockAccessIndex == other.BlockAccessIndex && - NewValue.Unwrap().SequenceEqual(other.NewValue.Unwrap()); + NewValue.SequenceEqual(other.NewValue); public override readonly bool Equals(object? obj) => obj is StorageChange other && Equals(other); @@ -27,7 +27,6 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageChange left, StorageChange right) => !(left == right); - - public override readonly string? ToString() - => $"{BlockAccessIndex}, {NewValue}"; + // public override readonly string? ToString() + // => $"{BlockAccessIndex}, {NewValue}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs index 9183c2aa12b..8d999f56505 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -7,15 +7,15 @@ namespace Nethermind.Core.BlockAccessLists; -public readonly struct StorageRead(Bytes32 key) : IEquatable, IComparable +public readonly struct StorageRead(byte[] key) : IEquatable, IComparable { - public Bytes32 Key { get; init; } = key; + public byte[] Key { get; init; } = key; public int CompareTo(StorageRead other) - => Bytes.BytesComparer.Compare(Key.Unwrap(), other.Key.Unwrap()); + => Bytes.BytesComparer.Compare(Key, other.Key); public readonly bool Equals(StorageRead other) => - Key.Unwrap().SequenceEqual(other.Key.Unwrap()); + Key.SequenceEqual(other.Key); public override readonly bool Equals(object? obj) => obj is StorageRead other && Equals(other); @@ -29,6 +29,6 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageRead left, StorageRead right) => !(left == right); - public override readonly string? ToString() - => $"0x{Bytes.ToHexString(Key.Unwrap())}"; + // public override readonly string? ToString() + // => $"0x{Bytes.ToHexString(Key.Unwrap())}"; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs index d233bd8f29e..c86d00bca66 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/AccountChangesDecoder.cs @@ -92,15 +92,7 @@ public AccountChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBe return index; }, s => s)); - return new() - { - Address = address, - StorageChanges = slotChangesMap, - StorageReads = storageReadsList, - BalanceChanges = balanceChangesList, - NonceChanges = nonceChangesList, - CodeChanges = codeChangesList - }; + return new(address, slotChangesMap, storageReadsList, balanceChangesList, nonceChangesList, codeChangesList); } public int GetLength(AccountChanges item, RlpBehaviors rlpBehaviors) @@ -120,17 +112,17 @@ public void Encode(RlpStream stream, AccountChanges item, RlpBehaviors rlpBehavi { stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.Address); - stream.EncodeArray([.. item.StorageChanges.Values], rlpBehaviors); + stream.EncodeArray([.. item.StorageChanges], rlpBehaviors); stream.EncodeArray([.. item.StorageReads], rlpBehaviors); - stream.EncodeArray([.. item.BalanceChanges.Values], rlpBehaviors); - stream.EncodeArray([.. item.NonceChanges.Values], rlpBehaviors); - stream.EncodeArray([.. item.CodeChanges.Values], rlpBehaviors); + stream.EncodeArray([.. item.BalanceChanges], rlpBehaviors); + stream.EncodeArray([.. item.NonceChanges], rlpBehaviors); + stream.EncodeArray([.. item.CodeChanges], rlpBehaviors); } private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehaviors) { int slotChangesLen = 0; - foreach (SlotChanges slotChanges in item.StorageChanges.Values) + foreach (SlotChanges slotChanges in item.StorageChanges) { slotChangesLen += SlotChangesDecoder.Instance.GetLength(slotChanges, rlpBehaviors); } @@ -144,21 +136,21 @@ private static int GetContentLength(AccountChanges item, RlpBehaviors rlpBehavio storageReadsLen = Rlp.LengthOfSequence(storageReadsLen); int balanceChangesLen = 0; - foreach (BalanceChange balanceChange in item.BalanceChanges.Values) + foreach (BalanceChange balanceChange in item.BalanceChanges) { balanceChangesLen += BalanceChangeDecoder.Instance.GetLength(balanceChange, rlpBehaviors); } balanceChangesLen = Rlp.LengthOfSequence(balanceChangesLen); int nonceChangesLen = 0; - foreach (NonceChange nonceChange in item.NonceChanges.Values) + foreach (NonceChange nonceChange in item.NonceChanges) { nonceChangesLen += NonceChangeDecoder.Instance.GetLength(nonceChange, rlpBehaviors); } nonceChangesLen = Rlp.LengthOfSequence(nonceChangesLen); int codeChangesLen = 0; - foreach (CodeChange codeChange in item.CodeChanges.Values) + foreach (CodeChange codeChange in item.CodeChanges) { codeChangesLen += CodeChangeDecoder.Instance.GetLength(codeChange, rlpBehaviors); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index 15704a37ccc..df4ba57883f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -51,9 +51,9 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { - stream.EncodeArray([.. item.GetAccountChanges()], rlpBehaviors); + stream.EncodeArray([.. item.AccountChanges], rlpBehaviors); } private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) - => AccountChangesDecoder.Instance.GetContentLength([.. item.GetAccountChanges()], rlpBehaviors); + => AccountChangesDecoder.Instance.GetContentLength([.. item.AccountChanges], rlpBehaviors); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs index c8d1cc5391b..409d7a82510 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/SlotChangesDecoder.cs @@ -26,11 +26,7 @@ public SlotChanges Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav } StorageChange[] changes = ctx.DecodeArray(StorageChangeDecoder.Instance, true, default, _codeLimit); - SlotChanges slotChanges = new() - { - Slot = slot, - Changes = [.. changes] - }; + SlotChanges slotChanges = new(slot, [.. changes]); if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs index 730e6037d21..0e6e1945571 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageChangeDecoder.cs @@ -26,7 +26,7 @@ public StorageChange Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBeh StorageChange storageChange = new() { BlockAccessIndex = blockAccessIndex, - NewValue = new(newValue) + NewValue = newValue }; if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) @@ -54,9 +54,9 @@ public void Encode(RlpStream stream, StorageChange item, RlpBehaviors rlpBehavio { stream.StartSequence(GetContentLength(item, rlpBehaviors)); stream.Encode(item.BlockAccessIndex); - stream.Encode(item.NewValue.Unwrap()); + stream.Encode(item.NewValue); } public static int GetContentLength(StorageChange item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewValue.Unwrap()); + => Rlp.LengthOf(item.BlockAccessIndex) + Rlp.LengthOf(item.NewValue); } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs index a9b8e662071..e1c18321eab 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/StorageReadDecoder.cs @@ -24,7 +24,7 @@ public StorageRead Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehav StorageRead storageRead = new() { - Key = new(key), + Key = key, }; return storageRead; @@ -41,8 +41,8 @@ public StorageRead Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) } public void Encode(RlpStream stream, StorageRead item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - => stream.Encode(item.Key.Unwrap()); + => stream.Encode(item.Key); public static int GetContentLength(StorageRead item, RlpBehaviors rlpBehaviors) - => Rlp.LengthOf(item.Key.Unwrap()); + => Rlp.LengthOf(item.Key); } From 5a4fd751ec97d6e2e71e5c433bd99f657cb1eb57 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:58:14 +0000 Subject: [PATCH 188/215] debug assert fail for unreachable codde --- .../BlockAccessLists/BlockAccessList.cs | 11 +++++++---- .../Eip7928/BlockAccessListDecoder.cs | 4 +--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 2db0c1651ac..86517a0d88e 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Data; +using System.Diagnostics; using System.Linq; using System.Text.Json.Serialization; using Nethermind.Int256; @@ -346,8 +347,9 @@ private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeI } } - // assert error? should never happen - throw new Exception("Error calculating pre tx balance"); + // should never happen + Debug.Fail("Error calculating pre tx balance"); + return true; } private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in ReadOnlySpan beforeInstr, in ReadOnlySpan afterInstr) @@ -380,8 +382,9 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in } } - // assert error? should never happen - throw new Exception("Error calculating pre tx storage"); + // should never happen + Debug.Fail("Error calculating pre tx storage"); + return true; } private readonly AccountChanges GetOrAddAccountChanges(Address address) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs index df4ba57883f..5f187644cc9 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7928/BlockAccessListDecoder.cs @@ -50,9 +50,7 @@ public BlockAccessList Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) } public void Encode(RlpStream stream, BlockAccessList item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - stream.EncodeArray([.. item.AccountChanges], rlpBehaviors); - } + => stream.EncodeArray([.. item.AccountChanges], rlpBehaviors); private static int GetContentLength(BlockAccessList item, RlpBehaviors rlpBehaviors) => AccountChangesDecoder.Instance.GetContentLength([.. item.AccountChanges], rlpBehaviors); From 3441a90efc70f958193c72305c97302580939661 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 12 Nov 2025 18:59:05 +0000 Subject: [PATCH 189/215] remove code change diff check --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 86517a0d88e..64e96cd85c1 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -110,13 +110,6 @@ public void AddCodeChange(Address address, byte[] before, byte[] after) AccountChanges accountChanges = GetOrAddAccountChanges(address); - // is needed? - // need more complex logic like for storage & balance changes? - if (Enumerable.SequenceEqual(before, after)) - { - return; - } - accountChanges.PopCodeChange(Index, out CodeChange? oldCodeChange); _changes.Push(new() { From df5a82d92e7be7fa26059b50053e9e2b4404f919 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:05:44 +0000 Subject: [PATCH 190/215] string encoding use json --- .../Validators/BlockValidator.cs | 3 +-- .../BlockAccessLists/AccountChanges.cs | 15 --------------- .../BlockAccessLists/BalanceChange.cs | 3 --- .../BlockAccessLists/BlockAccessList.cs | 5 ++--- .../BlockAccessLists/CodeChange.cs | 4 ---- .../BlockAccessLists/SlotChanges.cs | 6 ------ .../BlockAccessLists/StorageChange.cs | 2 -- .../BlockAccessLists/StorageRead.cs | 3 --- 8 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 11f360312bc..c0a43ed06fe 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -237,11 +237,10 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B if (processedBlock.Header.BlockAccessListHash != suggestedBlock.Header.BlockAccessListHash) { - // Console.WriteLine("processed block:"); - // Console.WriteLine(processedBlock.BlockAccessList.ToString()); if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); suggestedBlock.GeneratedBlockAccessList = processedBlock.BlockAccessList; + if (_logger.IsWarn) _logger.Warn($"Generated block access list:\n{suggestedBlock.GeneratedBlockAccessList}\nSuggested block access list:\n{suggestedBlock.BlockAccessList}"); } if (receipts.Length != processedBlock.Transactions.Length) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index 77d289a9620..acc30f25fb4 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -190,19 +190,4 @@ private static bool PopChange(SortedList changes, ushort index, [N return false; } - - // public override string? ToString() - // { - // string storageChangesList = string.Join(",\n\t\t\t", [.. StorageChanges.Values.Select(s => s.ToString())]); - // string storageChanges = StorageChanges.Count == 0 ? "[] #storage_changes" : $"[ #storage_changes\n\t\t\t{storageChangesList}\n\t\t]"; - // string storageReadsList = string.Join(",\n\t\t\t", [.. StorageReads.Select(s => s.ToString())]); - // string storageReads = StorageReads.Count == 0 ? "[] #storage_reads" : $"[ #storage_reads\n\t\t\t{storageReadsList}\n\t\t]"; - // string balanceChangesList = string.Join(",\n\t\t\t", [.. BalanceChanges.Values.Select(s => s.ToString())]); - // string balanceChanges = BalanceChanges.Count == 0 ? "[] #balance_changes" : $"[ #balance_changes\n\t\t\t{balanceChangesList}\n\t\t]"; - // string nonceChangesList = string.Join(",\n\t\t\t", [.. NonceChanges.Values.Select(s => s.ToString())]); - // string nonceChanges = NonceChanges.Count == 0 ? "[] #nonce_changes" : $"[ #nonce_changes\n\t\t\t{nonceChangesList}\n\t\t]"; - // string codeChangesList = string.Join(",\n\t\t\t", [.. CodeChanges.Values.Select(s => s.ToString())]); - // string codeChanges = CodeChanges.Count == 0 ? "[] #code_changes" : $"[ #code_changes\n\t\t\t{codeChangesList}\n\t\t]"; - // return $"\t[\n\t\t{Address},\n\t\t{storageChanges},\n\t\t{storageReads},\n\t\t{balanceChanges},\n\t\t{nonceChanges},\n\t\t{codeChanges}\n\t]"; - // } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index 032159f5290..15b822be3f8 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -26,7 +26,4 @@ public override readonly int GetHashCode() => public static bool operator !=(BalanceChange left, BalanceChange right) => !(left == right); - - // public override readonly string? ToString() - // => $"{BlockAccessIndex}, {PostBalance}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 64e96cd85c1..72b128e20ca 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -6,6 +6,7 @@ using System.Data; using System.Diagnostics; using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; using Nethermind.Int256; @@ -254,7 +255,6 @@ public readonly void Restore(int snapshot) { Change change = _changes.Pop(); AccountChanges accountChanges = _accountChanges[change.Address]; - // int count; switch (change.Type) { case ChangeType.BalanceChange: @@ -306,8 +306,7 @@ public readonly void Restore(int snapshot) } public override readonly string? ToString() - => ""; // json serialise - // => "[\n" + string.Join(",\n", [.. _accountChanges.Values.Select(account => account.ToString())]) + "\n]"; + => JsonSerializer.Serialize(this); private readonly bool HasBalanceChangedDuringTx(Address address, UInt256 beforeInstr, UInt256 afterInstr) { diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index ee8e6cfa5f1..36b4afdb5c1 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -4,7 +4,6 @@ using System; using System.Linq; -using Nethermind.Core.Extensions; namespace Nethermind.Core.BlockAccessLists; @@ -37,7 +36,4 @@ private static bool CompareByteArrays(byte[]? left, byte[]? right) => public static bool operator !=(CodeChange left, CodeChange right) => !(left == right); - - // public override readonly string? ToString() - // => $"{BlockAccessIndex}, 0x{Bytes.ToHexString(NewCode)}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs index 9d3561eab10..efee98c7b84 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -62,10 +62,4 @@ public bool PopStorageChange(ushort index, [NotNullWhen(true)] out StorageChange return false; } - - // public override readonly string? ToString() - // { - // string changes = string.Join(", ", [.. Changes.Select(s => s.ToString())]); - // return $"[0x{Bytes.ToHexString(Slot)}, [{changes}]]"; - // } } \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index 3cc4c582a4f..b276a74e967 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -27,6 +27,4 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageChange left, StorageChange right) => !(left == right); - // public override readonly string? ToString() - // => $"{BlockAccessIndex}, {NewValue}"; } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs index 8d999f56505..848ee16c0ca 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -28,7 +28,4 @@ public override readonly int GetHashCode() => public static bool operator !=(StorageRead left, StorageRead right) => !(left == right); - - // public override readonly string? ToString() - // => $"0x{Bytes.ToHexString(Key.Unwrap())}"; } From 692e22b92ae87310586cf13266c5b9f7673ae84b Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 12 Nov 2025 20:48:26 +0000 Subject: [PATCH 191/215] fix: check count empty when checking if storage changed --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 2 +- .../Nethermind.Core/BlockAccessLists/NonceChange.cs | 3 --- src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs | 4 ++-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 72b128e20ca..066ccf0eccb 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -348,7 +348,7 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in { AccountChanges accountChanges = _accountChanges[address]; - if (!accountChanges.TryGetSlotChanges(key, out SlotChanges? slotChanges)) + if (!accountChanges.TryGetSlotChanges(key, out SlotChanges? slotChanges) || slotChanges.Changes.Count == 0) { // first storage change of block // return storage prior to this instruction diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs index b5093565f56..3907f1c218f 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/NonceChange.cs @@ -25,7 +25,4 @@ public override readonly int GetHashCode() => public static bool operator !=(NonceChange left, NonceChange right) => !(left == right); - - // public override readonly string? ToString() - // => $"{BlockAccessIndex}, {NewNonce}"; } diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 1f564a5ca78..264be4fd8f7 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -23,8 +23,8 @@ public BlockDecoder() : this(new HeaderDecoder()) { } throw new RlpException($"Received a 0 length stream when decoding a {nameof(Block)}"); } - Console.WriteLine("DECODING BLOCK"); - Console.WriteLine(Convert.ToHexString(rlpStream.Data)); + // Console.WriteLine("DECODING BLOCK"); + // Console.WriteLine(Convert.ToHexString(rlpStream.Data)); if (rlpStream.IsNextItemNull()) { From 47cb345694854a43fb98bc2ad0cfad9aa5b04d8d Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:08:56 +0000 Subject: [PATCH 192/215] fix merge --- .../Nethermind.Core.Test/Blockchain/BasicTestBlockchain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/BasicTestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/BasicTestBlockchain.cs index c17398190b9..e4edcd794fe 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/BasicTestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/BasicTestBlockchain.cs @@ -4,9 +4,9 @@ using System; using System.Threading.Tasks; using Autofac; +using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Evm; -using Nethermind.Specs; using Nethermind.State; namespace Nethermind.Core.Test.Blockchain; From 8cf31e6248fda2df914f6bdc6dcf2fd48588aad7 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:42:06 +0000 Subject: [PATCH 193/215] fix json serialisation --- .../Nethermind.Consensus/Validators/BlockValidator.cs | 2 -- .../Nethermind.Core/BlockAccessLists/AccountChanges.cs | 2 ++ .../Nethermind.Core/BlockAccessLists/BalanceChange.cs | 3 +++ src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs | 3 +++ src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs | 3 +++ .../Nethermind.Core/BlockAccessLists/StorageChange.cs | 3 +++ src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs | 3 +++ .../JsonConverters}/ForcedNumberConversion.cs | 0 .../JsonConverters}/NumberConversion.cs | 0 .../JsonConverters}/UInt256Converter.cs | 0 10 files changed, 17 insertions(+), 2 deletions(-) rename src/Nethermind/{Nethermind.Serialization.Json => Nethermind.Core/JsonConverters}/ForcedNumberConversion.cs (100%) rename src/Nethermind/{Nethermind.Serialization.Json => Nethermind.Core/JsonConverters}/NumberConversion.cs (100%) rename src/Nethermind/{Nethermind.Serialization.Json => Nethermind.Core/JsonConverters}/UInt256Converter.cs (100%) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index c0a43ed06fe..7b66a3246ae 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -4,10 +4,8 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Text; -using System.Text.Json; using Nethermind.Blockchain; using Nethermind.Core; -using Nethermind.Core.BlockAccessLists; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Messages; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index acc30f25fb4..f9f24114183 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -8,11 +8,13 @@ using System.Linq; using System.Text.Json.Serialization; using Nethermind.Core.Extensions; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public class AccountChanges : IEquatable { + [JsonConverter(typeof(AddressConverter))] public Address Address { get; init; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs index 15b822be3f8..06ea44b4a93 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BalanceChange.cs @@ -2,13 +2,16 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Text.Json.Serialization; using Nethermind.Int256; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public readonly struct BalanceChange(ushort blockAccessIndex, UInt256 postBalance) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + [JsonConverter(typeof(UInt256Converter))] public UInt256 PostBalance { get; init; } = postBalance; public readonly bool Equals(BalanceChange other) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs index 36b4afdb5c1..14be8de3145 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/CodeChange.cs @@ -4,12 +4,15 @@ using System; using System.Linq; +using System.Text.Json.Serialization; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public readonly struct CodeChange(ushort blockAccessIndex, byte[] newCode) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + [JsonConverter(typeof(ByteArrayConverter))] public byte[] NewCode { get; init; } = newCode; public readonly bool Equals(CodeChange other) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs index efee98c7b84..41a5cafa4b8 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/SlotChanges.cs @@ -6,11 +6,14 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Text.Json.Serialization; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public class SlotChanges(byte[] slot, List changes) : IEquatable { + [JsonConverter(typeof(ByteArrayConverter))] public byte[] Slot { get; init; } = slot; public List Changes { get; init; } = changes; diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs index b276a74e967..0c0fe976961 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageChange.cs @@ -4,12 +4,15 @@ using System; using System.Linq; +using System.Text.Json.Serialization; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public readonly struct StorageChange(ushort blockAccessIndex, byte[] newValue) : IEquatable, IIndexedChange { public ushort BlockAccessIndex { get; init; } = blockAccessIndex; + [JsonConverter(typeof(ByteArrayConverter))] public byte[] NewValue { get; init; } = newValue; public readonly bool Equals(StorageChange other) => diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs index 848ee16c0ca..9eead94f52d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/StorageRead.cs @@ -3,12 +3,15 @@ using System; using System.Linq; +using System.Text.Json.Serialization; using Nethermind.Core.Extensions; +using Nethermind.Serialization.Json; namespace Nethermind.Core.BlockAccessLists; public readonly struct StorageRead(byte[] key) : IEquatable, IComparable { + [JsonConverter(typeof(ByteArrayConverter))] public byte[] Key { get; init; } = key; public int CompareTo(StorageRead other) diff --git a/src/Nethermind/Nethermind.Serialization.Json/ForcedNumberConversion.cs b/src/Nethermind/Nethermind.Core/JsonConverters/ForcedNumberConversion.cs similarity index 100% rename from src/Nethermind/Nethermind.Serialization.Json/ForcedNumberConversion.cs rename to src/Nethermind/Nethermind.Core/JsonConverters/ForcedNumberConversion.cs diff --git a/src/Nethermind/Nethermind.Serialization.Json/NumberConversion.cs b/src/Nethermind/Nethermind.Core/JsonConverters/NumberConversion.cs similarity index 100% rename from src/Nethermind/Nethermind.Serialization.Json/NumberConversion.cs rename to src/Nethermind/Nethermind.Core/JsonConverters/NumberConversion.cs diff --git a/src/Nethermind/Nethermind.Serialization.Json/UInt256Converter.cs b/src/Nethermind/Nethermind.Core/JsonConverters/UInt256Converter.cs similarity index 100% rename from src/Nethermind/Nethermind.Serialization.Json/UInt256Converter.cs rename to src/Nethermind/Nethermind.Core/JsonConverters/UInt256Converter.cs From 391c81747530960e156ba710af445098055b98f8 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:44:29 +0000 Subject: [PATCH 194/215] fix suggested & generated --- .../Nethermind.Consensus/Processing/BlockProcessor.cs | 2 +- .../Nethermind.Consensus/Validators/BlockValidator.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index 6cf6d5a1d9c..ca0d7f1c248 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -187,7 +187,7 @@ protected virtual TxReceipt[] ProcessBlock( } else { - body.BlockAccessList = _tracedAccessWorldState.BlockAccessList; + block.GeneratedBlockAccessList = _tracedAccessWorldState.BlockAccessList; block.EncodedBlockAccessList = Rlp.Encode(_tracedAccessWorldState.BlockAccessList).Bytes; header.BlockAccessListHash = new(ValueKeccak.Compute(block.EncodedBlockAccessList).Bytes); } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 7b66a3246ae..3eea3faf471 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -237,8 +237,7 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B { if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); - suggestedBlock.GeneratedBlockAccessList = processedBlock.BlockAccessList; - if (_logger.IsWarn) _logger.Warn($"Generated block access list:\n{suggestedBlock.GeneratedBlockAccessList}\nSuggested block access list:\n{suggestedBlock.BlockAccessList}"); + if (_logger.IsWarn) _logger.Warn($"Generated block access list:\n{processedBlock.GeneratedBlockAccessList}\nSuggested block access list:\n{processedBlock.BlockAccessList}"); } if (receipts.Length != processedBlock.Transactions.Length) From d38c6ba7cf24892c9ee8a57fca18d2900b7338b6 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 17:28:22 +0000 Subject: [PATCH 195/215] fix check code change --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 066ccf0eccb..693056aa054 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -111,6 +111,11 @@ public void AddCodeChange(Address address, byte[] before, byte[] after) AccountChanges accountChanges = GetOrAddAccountChanges(address); + if (Enumerable.SequenceEqual(before, after)) + { + return; + } + accountChanges.PopCodeChange(Index, out CodeChange? oldCodeChange); _changes.Push(new() { From 6fffe333ace77122b80431327629d694e70103a1 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 18:51:16 +0000 Subject: [PATCH 196/215] blockchain test blob schedule load --- .../Ethereum.Test.Base/BlockchainTestBase.cs | 4 +- .../Ethereum.Test.Base/BlockchainTestJson.cs | 15 +++++++ .../Ethereum.Test.Base/JsonToEthereumTest.cs | 22 ++++++++++- .../OverridableReleaseSpec.cs | 39 +++++++++++++++++-- 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index ffa1cb8e845..ca5450e797f 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -353,8 +353,8 @@ private async static Task RunNewPayloads(TestEngineNewPayloadsJson[]? newPayload { RlpStream rlpContext = Bytes.FromHexString(testBlockJson.Rlp!).AsRlpStream(); Block suggestedBlock = Rlp.Decode(rlpContext); - // Console.WriteLine("suggested block:"); - // Console.WriteLine(suggestedBlock.BlockAccessList); + Console.WriteLine("suggested block:"); + Console.WriteLine(suggestedBlock.BlockAccessList); // Hash256 tmp = new(ValueKeccak.Compute(Rlp.Encode(suggestedBlock.BlockAccessList!.Value).Bytes).Bytes); if (testBlockJson.BlockHeader is not null) diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs index c344cd773da..2727692ee16 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestJson.cs @@ -20,6 +20,7 @@ public class BlockchainTestJson public IReleaseSpec? EthereumNetworkAfterTransition { get; set; } public ForkActivation? TransitionForkActivation { get; set; } public string? LastBlockHash { get; set; } + public ConfigJson? Config { get; set; } public string? GenesisRlp { get; set; } public TestBlockJson[]? Blocks { get; set; } @@ -34,4 +35,18 @@ public class BlockchainTestJson public string? SealEngine { get; set; } public string? LoadFailure { get; set; } } + + public class ConfigJson + { + public string? Network { get; set; } + public string? Chainid { get; set; } + public Dictionary? BlobSchedule; + } + + public class BlobScheduleEntryJson + { + public string? Target; + public string? Max; + public string? BaseFeeUpdateFraction; + } } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 861b131bb9d..3ee3c5a74d7 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -19,6 +19,7 @@ using Nethermind.Serialization.Json; using Nethermind.Serialization.Rlp; using Nethermind.Specs; +using Nethermind.Specs.Test; namespace Ethereum.Test.Base { @@ -430,11 +431,11 @@ public static IEnumerable ConvertToBlockchainTests(string json) string[] transitionInfo = testSpec.Network.Split("At"); string[] networks = transitionInfo[0].Split("To"); - testSpec.EthereumNetwork = SpecNameParser.Parse(networks[0]); + testSpec.EthereumNetwork = LoadSpec(networks[0], testSpec.Config?.BlobSchedule); if (transitionInfo.Length > 1) { testSpec.TransitionForkActivation = TransitionForkActivation(transitionInfo[1]); - testSpec.EthereumNetworkAfterTransition = SpecNameParser.Parse(networks[1]); + testSpec.EthereumNetworkAfterTransition = LoadSpec(networks[1], testSpec.Config?.BlobSchedule); } (string name, string category) = GetNameAndCategory(testName); @@ -444,6 +445,23 @@ public static IEnumerable ConvertToBlockchainTests(string json) return testsByName; } + private static IReleaseSpec LoadSpec(string name, Dictionary? blobSchedule) + { + IReleaseSpec spec = SpecNameParser.Parse(name); + if (blobSchedule is null) + { + return spec; + } + + BlobScheduleEntryJson blobCount = blobSchedule[name]; + return new OverridableReleaseSpec(spec) + { + MaxBlobCount = System.Convert.ToUInt64(blobCount.Max, 16), + TargetBlobCount = System.Convert.ToUInt64(blobCount.Target, 16), + BlobBaseFeeUpdateFraction = System.Convert.ToUInt64(blobCount.BaseFeeUpdateFraction, 16) + }; + } + private static (string name, string category) GetNameAndCategory(string key) { key = key.Replace('\\', '/'); diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 433374f543c..f3ad9df5036 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -160,10 +160,43 @@ public ulong Eip4844TransitionTimestamp } } - public ulong TargetBlobCount => spec.TargetBlobCount; - public ulong MaxBlobCount => spec.MaxBlobCount; + private ulong? _overridenTargetBlobCount; + public ulong TargetBlobCount + { + get + { + return _overridenTargetBlobCount ?? spec.TargetBlobCount; + } + set + { + _overridenTargetBlobCount = value; + } + } + private ulong? _overridenMaxBlobCount; + public ulong MaxBlobCount + { + get + { + return _overridenMaxBlobCount ?? spec.MaxBlobCount; + } + set + { + _overridenMaxBlobCount = value; + } + } public ulong MaxBlobsPerTx => spec.MaxBlobsPerTx; - public UInt256 BlobBaseFeeUpdateFraction => spec.BlobBaseFeeUpdateFraction; + private UInt256? _overridenBlobBaseFeeUpdateFraction; + public UInt256 BlobBaseFeeUpdateFraction + { + get + { + return _overridenBlobBaseFeeUpdateFraction ?? spec.BlobBaseFeeUpdateFraction; + } + set + { + _overridenBlobBaseFeeUpdateFraction = value; + } + } public bool IsEip1153Enabled => spec.IsEip1153Enabled; public bool IsEip3651Enabled => spec.IsEip3651Enabled; public bool IsEip3855Enabled => spec.IsEip3855Enabled; From d74a4b8ddf9b28b3c3a6ccba910200c8789d183e Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:02:25 +0000 Subject: [PATCH 197/215] nonce change on account creation --- src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index 7adac90282e..40818b04db1 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -154,6 +154,10 @@ public override void CreateAccount(Address address, in UInt256 balance, in UInt2 { BlockAccessList.AddBalanceChange(address, 0, balance); } + if (nonce != 0) + { + BlockAccessList.AddNonceChange(address, (ulong)nonce); + } } _innerWorldState.CreateAccount(address, balance, nonce); } From 84c1157cc19b22bbff999d0ab1f88dd779adc659 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:06:05 +0000 Subject: [PATCH 198/215] debugging logs for blob schedule --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 1 + .../Nethermind.Consensus/Validators/HeaderValidator.cs | 2 +- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 3ee3c5a74d7..1ced04f8fda 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -454,6 +454,7 @@ private static IReleaseSpec LoadSpec(string name, Dictionary transitions, ChainSpec c throw new ArgumentException($"Blob schedule target ({settings.Target}) should not exceed max ({settings.Max})."); } + Console.WriteLine($"[BAL TEST] Loaded blob schedule transition t{settings.Timestamp} {settings.TargetBlobCount}/{settings.MaxBlobCount}"); transitions.Add(settings.Timestamp); } } From befc83aae1bcaf56ae1d24b78d830c7ff90ed063 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:13:05 +0000 Subject: [PATCH 199/215] fix compile --- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index b7d60f76acf..7b6e67cf45a 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -115,7 +115,7 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c throw new ArgumentException($"Blob schedule target ({settings.Target}) should not exceed max ({settings.Max})."); } - Console.WriteLine($"[BAL TEST] Loaded blob schedule transition t{settings.Timestamp} {settings.TargetBlobCount}/{settings.MaxBlobCount}"); + Console.WriteLine($"[BAL TEST] Loaded blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}"); transitions.Add(settings.Timestamp); } } From 5d98db834711c9575ff22c5681e1117580b0283f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 12:35:50 +0000 Subject: [PATCH 200/215] more logs --- src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs b/src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs index 7dbe87e1de8..a2916ba260b 100644 --- a/src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -110,6 +111,7 @@ static bool FakeExponentialOverflow(UInt256 factor, UInt256 num, UInt256 denomin ulong parentBlobGas = excessBlobGas + blobGasUsed; ulong targetBlobGasPerBlock = releaseSpec.GetTargetBlobGasPerBlock(); + Console.WriteLine($"[BAL TEST] parent excess={excessBlobGas} used={blobGasUsed} target={targetBlobGasPerBlock}"); if (parentBlobGas < targetBlobGasPerBlock) { return 0; @@ -120,14 +122,17 @@ static bool FakeExponentialOverflow(UInt256 factor, UInt256 num, UInt256 denomin TryCalculateFeePerBlobGas(parentBlockHeader, releaseSpec.BlobBaseFeeUpdateFraction, out UInt256 feePerBlobGas); UInt256 floorCost = Eip7918Constants.BlobBaseCost * parentBlockHeader.BaseFeePerGas; UInt256 targetCost = Eip4844Constants.GasPerBlob * feePerBlobGas; + Console.WriteLine($"[BAL TEST] floor={floorCost} target={targetCost}"); // if below floor cost then increase excess blob gas if (floorCost > targetCost) { + Console.WriteLine($"[BAL TEST] below floor"); ulong target = releaseSpec.TargetBlobCount; ulong max = releaseSpec.MaxBlobCount; return excessBlobGas + (blobGasUsed * (max - target) / max); } + Console.WriteLine($"[BAL TEST] above floor"); } return parentBlobGas - targetBlobGasPerBlock; From ebb5b770cc313f4c5135d11a75b0a8024baae276 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:13:36 +0000 Subject: [PATCH 201/215] try basing amsterdam on osaka --- src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs index 24d71e16554..ed01d5d6ea3 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs @@ -6,7 +6,7 @@ namespace Nethermind.Specs.Forks; -public class Amsterdam : BPO5 +public class Amsterdam : Osaka { private static IReleaseSpec _instance; From 1c76673e1a621fe4cae49979698175e6fa8206f9 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:22:27 +0000 Subject: [PATCH 202/215] undo change --- src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs index ed01d5d6ea3..24d71e16554 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/25_Amsterdam.cs @@ -6,7 +6,7 @@ namespace Nethermind.Specs.Forks; -public class Amsterdam : Osaka +public class Amsterdam : BPO5 { private static IReleaseSpec _instance; From ee35d5f1692a2cce7de1ae51f1971b4fd97980d3 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:32:55 +0000 Subject: [PATCH 203/215] print HIVE vars --- src/Nethermind/Nethermind.Hive/HiveRunner.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Hive/HiveRunner.cs b/src/Nethermind/Nethermind.Hive/HiveRunner.cs index 4f876371342..71778f6fe84 100644 --- a/src/Nethermind/Nethermind.Hive/HiveRunner.cs +++ b/src/Nethermind/Nethermind.Hive/HiveRunner.cs @@ -85,11 +85,22 @@ private void ListEnvironmentVariables() "HIVE_CHAIN_ID", "HIVE_BOOTNODE", "HIVE_TESTNET", "HIVE_NODETYPE", "HIVE_FORK_HOMESTEAD", "HIVE_FORK_DAO_BLOCK", "HIVE_FORK_DAO_VOTE", "HIVE_FORK_TANGERINE", "HIVE_FORK_SPURIOUS", "HIVE_FORK_METROPOLIS", "HIVE_FORK_BYZANTIUM", "HIVE_FORK_CONSTANTINOPLE", "HIVE_FORK_PETERSBURG", - "HIVE_MINER", "HIVE_MINER_EXTRA", "HIVE_FORK_BERLIN", "HIVE_FORK_LONDON" + "HIVE_MINER", "HIVE_MINER_EXTRA", "HIVE_FORK_BERLIN", "HIVE_FORK_LONDON", + "HIVE_MERGE_BLOCK_ID", "HIVE_SHANGHAI_TIMESTAMP", "HIVE_CANCUN_TIMESTAMP", + "HIVE_CANCUN_BLOB_TARGET", "HIVE_CANCUN_BLOB_MAX", "HIVE_CANCUN_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_PRAGUE_TIMESTAMP", "HIVE_PRAGUE_BLOB_TARGET", "HIVE_PRAGUE_BLOB_MAX", + "HIVE_PRAGUE_BLOB_BASE_FEE_UPDATE_FRACTION", "HIVE_OSAKA_TIMESTAMP", "HIVE_OSAKA_BLOB_TARGET", + "HIVE_OSAKA_BLOB_MAX", "HIVE_OSAKA_BLOB_BASE_FEE_UPDATE_FRACTION", "HIVE_AMSTERDAM_TIMESTAMP", + "HIVE_AMSTERDAM_BLOB_TARGET", "HIVE_AMSTERDAM_BLOB_MAX", "HIVE_AMSTERDAM_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_BPO1_TIMESTAMP", "HIVE_BPO1_BLOB_TARGET", "HIVE_BPO1_BLOB_MAX", "HIVE_BPO1_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_BPO2_TIMESTAMP", "HIVE_BPO2_BLOB_TARGET", "HIVE_BPO2_BLOB_MAX", "HIVE_BPO2_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_BPO3_TIMESTAMP", "HIVE_BPO3_BLOB_TARGET", "HIVE_BPO3_BLOB_MAX", "HIVE_BPO3_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_BPO4_TIMESTAMP", "HIVE_BPO4_BLOB_TARGET", "HIVE_BPO4_BLOB_MAX", "HIVE_BPO4_BLOB_BASE_FEE_UPDATE_FRACTION", + "HIVE_BPO5_TIMESTAMP", "HIVE_BPO5_BLOB_TARGET", "HIVE_BPO5_BLOB_MAX", "HIVE_BPO5_BLOB_BASE_FEE_UPDATE_FRACTION" }; foreach (string variableName in variableNames) { - if (_logger.IsInfo) _logger.Info($"{variableName}: {Environment.GetEnvironmentVariable(variableName)}"); + if (_logger.IsInfo) _logger.Info($"{variableName}: {Environment.GetEnvironmentVariable(variableName) ?? "null"}"); } } From 0a066b4f276527879a76a7b1b536caa5fca89c88 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:34:42 +0000 Subject: [PATCH 204/215] new blobschedule log --- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 7b6e67cf45a..1f47105b181 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -100,6 +100,7 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c foreach (BlobScheduleSettings settings in chainSpec.Parameters.BlobSchedule) { + Console.WriteLine($"[BAL TEST] Loading blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}. Genesis t={genesisTimestamp}"); if (settings.Timestamp <= genesisTimestamp) { continue; @@ -115,7 +116,6 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c throw new ArgumentException($"Blob schedule target ({settings.Target}) should not exceed max ({settings.Max})."); } - Console.WriteLine($"[BAL TEST] Loaded blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}"); transitions.Add(settings.Timestamp); } } From 7f6ab109318a6da1010782caeb89bf5ac17da461 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:46:21 +0000 Subject: [PATCH 205/215] override to 6/9 --- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 1f47105b181..a414b90ee36 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -101,6 +101,9 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c foreach (BlobScheduleSettings settings in chainSpec.Parameters.BlobSchedule) { Console.WriteLine($"[BAL TEST] Loading blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}. Genesis t={genesisTimestamp}"); + settings.Max = 9; + settings.Target = 6; + settings.BaseFeeUpdateFraction = 5007716; if (settings.Timestamp <= genesisTimestamp) { continue; From 3fb5f654a76eee62c68c49e627ec4edb92cf994b Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 19:45:43 +0000 Subject: [PATCH 206/215] check of code has changed during tx --- .../BlockAccessLists/BlockAccessList.cs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 693056aa054..1ca54ab7c29 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -116,15 +116,20 @@ public void AddCodeChange(Address address, byte[] before, byte[] after) return; } + bool changedDuringTx = HasCodeChangedDuringTx(accountChanges.Address, before, after); accountChanges.PopCodeChange(Index, out CodeChange? oldCodeChange); _changes.Push(new() { Address = address, Type = ChangeType.CodeChange, - PreviousValue = oldCodeChange + PreviousValue = oldCodeChange, + PreTxCode = before }); - accountChanges.AddCodeChange(codeChange); + if (changedDuringTx) + { + accountChanges.AddCodeChange(codeChange); + } } public void AddNonceChange(Address address, ulong newNonce) @@ -384,6 +389,42 @@ private readonly bool HasStorageChangedDuringTx(Address address, byte[] key, in return true; } + private readonly bool HasCodeChangedDuringTx(Address address, in ReadOnlySpan beforeInstr, in ReadOnlySpan afterInstr) + { + AccountChanges accountChanges = _accountChanges[address]; + int count = accountChanges.CodeChanges.Count(); + + if (count == 0) + { + // first code change of block + // return code prior to this instruction + return !Enumerable.SequenceEqual(beforeInstr.ToArray(), afterInstr.ToArray()); + } + + foreach (CodeChange codeChange in accountChanges.CodeChanges.Reverse()) + { + if (codeChange.BlockAccessIndex != Index) + { + // code changed in previous tx in block + return !Enumerable.SequenceEqual(codeChange.NewCode, afterInstr.ToArray()); + } + } + + // storage only changed within this transaction + foreach (Change change in _changes) + { + if (change.Type == ChangeType.CodeChange && change.Address == address && change.PreviousValue is null) + { + // first change of this transaction & block + return change.PreTxCode is null || !Enumerable.SequenceEqual(change.PreTxCode, afterInstr.ToArray()); + } + } + + // should never happen + Debug.Fail("Error calculating pre tx code"); + return true; + } + private readonly AccountChanges GetOrAddAccountChanges(Address address) { if (!_accountChanges.TryGetValue(address, out AccountChanges? existing)) @@ -411,6 +452,7 @@ private readonly struct Change public IIndexedChange? PreviousValue { get; init; } public UInt256? PreTxBalance { get; init; } public byte[]? PreTxStorage { get; init; } + public byte[]? PreTxCode { get; init; } public ushort BlockAccessIndex { get; init; } } } From c8ed3d2ef2b24abbc3feaa076cb83d1f35d61d37 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 19:48:36 +0000 Subject: [PATCH 207/215] include system address balance change --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 1ca54ab7c29..d2ec1c0ad0d 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -64,11 +64,6 @@ public void ResetBlockAccessIndex() public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { - if (address == Address.SystemUser) - { - return; - } - BalanceChange balanceChange = new() { BlockAccessIndex = Index, From c2b7c0ae7cc27d74f82f0d756e653b1e925b7243 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:51:17 +0000 Subject: [PATCH 208/215] fix adding back storage read, handling of system account --- .../BlockAccessLists/AccountChanges.cs | 3 ++- .../BlockAccessLists/BlockAccessList.cs | 16 +++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs index f9f24114183..6594dfda072 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/AccountChanges.cs @@ -95,11 +95,12 @@ public bool HasStorageChange(byte[] key) public bool TryGetSlotChanges(byte[] key, [NotNullWhen(true)] out SlotChanges? slotChanges) => _storageChanges.TryGetValue(key, out slotChanges); - public void ClearSlotChangesIfEmpty(byte[] key) + public void ClearEmptySlotChangesAndAddRead(byte[] key) { if (TryGetSlotChanges(key, out SlotChanges? slotChanges) && slotChanges.Changes.Count == 0) { _storageChanges.Remove(key); + _storageReads.Add(new(key)); } } diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index d2ec1c0ad0d..616c9b87eff 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -64,6 +64,11 @@ public void ResetBlockAccessIndex() public void AddBalanceChange(Address address, UInt256 before, UInt256 after) { + if (address == Address.SystemUser && before == after) + { + return; + } + BalanceChange balanceChange = new() { BlockAccessIndex = Index, @@ -242,11 +247,7 @@ private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan } else { - accountChanges.ClearSlotChangesIfEmpty(storageKey); - if (!accountChanges.HasStorageChange(storageKey)) - { - accountChanges.AddStorageRead(storageKey); - } + accountChanges.ClearEmptySlotChangesAndAddRead(storageKey); } } @@ -295,16 +296,13 @@ public readonly void Restore(int snapshot) StorageChange? previousStorage = change.PreviousValue is null ? null : (StorageChange)change.PreviousValue; accountChanges.TryGetSlotChanges(change.Slot!, out SlotChanges? slotChanges); - // replace change with read - accountChanges.AddStorageRead(change.Slot!); - slotChanges!.PopStorageChange(Index, out _); if (previousStorage is not null) { slotChanges.Changes.Add(previousStorage.Value); } - accountChanges.ClearSlotChangesIfEmpty(change.Slot!); + accountChanges.ClearEmptySlotChangesAndAddRead(change.Slot!); break; } } From e90fce722b716e7155f76cef0abfd28dc81ba6bc Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 26 Nov 2025 01:17:06 +0000 Subject: [PATCH 209/215] undo bpo override --- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index a414b90ee36..686ccfb3cf3 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -100,10 +100,10 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c foreach (BlobScheduleSettings settings in chainSpec.Parameters.BlobSchedule) { - Console.WriteLine($"[BAL TEST] Loading blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}. Genesis t={genesisTimestamp}"); - settings.Max = 9; - settings.Target = 6; - settings.BaseFeeUpdateFraction = 5007716; + // Console.WriteLine($"[BAL TEST] Loading blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}. Genesis t={genesisTimestamp}"); + // settings.Max = 9; + // settings.Target = 6; + // settings.BaseFeeUpdateFraction = 5007716; if (settings.Timestamp <= genesisTimestamp) { continue; From b357c93a1ccd5c4069f29c8cc417ce1f6f4ad5ce Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:54:25 +0000 Subject: [PATCH 210/215] move delegation check later call --- .../Nethermind.Evm/Instructions/EvmInstructions.Call.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs index a3db55e61c0..e4cccc4a48a 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.Call.cs @@ -134,9 +134,6 @@ public static EvmExceptionType InstructionCall( !stack.PopUInt256(out UInt256 outputLength)) goto StackUnderflow; - // Charge gas for accessing the account's code (including delegation logic if applicable). - if (!EvmCalculations.ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) goto OutOfGas; - // For non-delegate calls, the transfer value is the call value. UInt256 transferValue = typeof(TOpCall) == typeof(OpDelegateCall) ? UInt256.Zero : callValue; // Enforce static call restrictions: no value transfer allowed unless it's a CALLCODE. @@ -170,10 +167,12 @@ public static EvmExceptionType InstructionCall( } // Update gas: call cost, memory expansion for input and output, and extra gas. + // Charge gas for accessing the account's code (including delegation logic if applicable). if (!EvmCalculations.UpdateGas(spec.GetCallCost(), ref gasAvailable) || !EvmCalculations.UpdateMemoryCost(vm.EvmState, ref gasAvailable, in dataOffset, dataLength) || !EvmCalculations.UpdateMemoryCost(vm.EvmState, ref gasAvailable, in outputOffset, outputLength) || - !EvmCalculations.UpdateGas(gasExtra, ref gasAvailable)) + !EvmCalculations.UpdateGas(gasExtra, ref gasAvailable) || + !EvmCalculations.ChargeAccountAccessGasWithDelegation(ref gasAvailable, vm, codeSource)) goto OutOfGas; // Retrieve code information for the call and schedule background analysis if needed. From 2063877a1d9599c27f7e656ea9445daae8d392cd Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 26 Nov 2025 11:55:57 +0000 Subject: [PATCH 211/215] remove extra logs --- src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs | 1 - .../Nethermind.Consensus/Validators/HeaderValidator.cs | 2 +- src/Nethermind/Nethermind.Evm/BlobGasCalculator.cs | 4 ---- .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 4 ---- 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 1ced04f8fda..3ee3c5a74d7 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -454,7 +454,6 @@ private static IReleaseSpec LoadSpec(string name, Dictionary targetCost) { - Console.WriteLine($"[BAL TEST] below floor"); ulong target = releaseSpec.TargetBlobCount; ulong max = releaseSpec.MaxBlobCount; return excessBlobGas + (blobGasUsed * (max - target) / max); } - Console.WriteLine($"[BAL TEST] above floor"); } return parentBlobGas - targetBlobGasPerBlock; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 686ccfb3cf3..5c98217818e 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -100,10 +100,6 @@ static void AddBlobScheduleTransitions(SortedSet transitions, ChainSpec c foreach (BlobScheduleSettings settings in chainSpec.Parameters.BlobSchedule) { - // Console.WriteLine($"[BAL TEST] Loading blob schedule transition t{settings.Timestamp} {settings.Target}/{settings.Max}. Genesis t={genesisTimestamp}"); - // settings.Max = 9; - // settings.Target = 6; - // settings.BaseFeeUpdateFraction = 5007716; if (settings.Timestamp <= genesisTimestamp) { continue; From 70d2afbbc0f59ed382fd1a2c38178640dc718719 Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:18:02 +0000 Subject: [PATCH 212/215] set balance to 0 on selfdestruct --- .../Nethermind.Core/BlockAccessLists/BlockAccessList.cs | 3 ++- src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs index 616c9b87eff..cd094f5b43e 100644 --- a/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs +++ b/src/Nethermind/Nethermind.Core/BlockAccessLists/BlockAccessList.cs @@ -208,10 +208,11 @@ public readonly void AddStorageRead(Address address, byte[] key) } } - public readonly void DeleteAccount(Address address) + public void DeleteAccount(Address address, UInt256 oldBalance) { AccountChanges accountChanges = GetOrAddAccountChanges(address); accountChanges.SelfDestruct(); + AddBalanceChange(address, oldBalance, 0); } private void StorageChange(AccountChanges accountChanges, in ReadOnlySpan key, in ReadOnlySpan before, in ReadOnlySpan after) diff --git a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs index 40818b04db1..fafaf3f1c18 100644 --- a/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs +++ b/src/Nethermind/Nethermind.Evm/State/TracedAccessWorldState.cs @@ -140,7 +140,7 @@ public override void DeleteAccount(Address address) { if (Enabled) { - BlockAccessList.DeleteAccount(address); + BlockAccessList.DeleteAccount(address, _innerWorldState.GetBalance(address)); } _innerWorldState.DeleteAccount(address); } From 43d2e737657ce84caaf0979726856ca66af33afc Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:08:20 +0000 Subject: [PATCH 213/215] copy generatedbal to suggested block --- src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 3eea3faf471..c4d62e564d7 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -238,6 +238,7 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B if (_logger.IsWarn) _logger.Warn($"- block access list hash : expected {suggestedBlock.Header.BlockAccessListHash}, got {processedBlock.Header.BlockAccessListHash}"); error ??= BlockErrorMessages.InvalidBlockLevelAccessListRoot(suggestedBlock.Header.BlockAccessListHash, processedBlock.Header.BlockAccessListHash); if (_logger.IsWarn) _logger.Warn($"Generated block access list:\n{processedBlock.GeneratedBlockAccessList}\nSuggested block access list:\n{processedBlock.BlockAccessList}"); + suggestedBlock.GeneratedBlockAccessList = processedBlock.GeneratedBlockAccessList; } if (receipts.Length != processedBlock.Transactions.Length) From 4c78c3bba59a4f6e8f37039f22b0c21a61d87705 Mon Sep 17 00:00:00 2001 From: Stefan <22667037+qu0b@users.noreply.github.com> Date: Mon, 1 Dec 2025 17:57:11 +0100 Subject: [PATCH 214/215] Fix cross-block state leak of `_parityTouchBugAccount.ShouldDelete` flag in VirtualMachine (#9856) reset parity touch bug shouldDelete --- src/Nethermind/Nethermind.Evm/VirtualMachine.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index b38ce65a793..ee271825afb 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -146,6 +146,9 @@ public virtual TransactionSubstate ExecuteTransaction( _txTracer = txTracer; _worldState = worldState; + // Reset Parity touch bug state to prevent cross-transaction leakage. + _parityTouchBugAccount.ShouldDelete = false; + // Prepare the specification and opcode mapping based on the current block header. IReleaseSpec spec = BlockExecutionContext.Spec; PrepareOpcodes(spec); From aeb8a7309a448bb9ffa06f0ee607c878af13eb4f Mon Sep 17 00:00:00 2001 From: Marc Harvey-Hill <10379486+Marchhill@users.noreply.github.com> Date: Thu, 4 Dec 2025 12:10:14 +0000 Subject: [PATCH 215/215] codecopy fix --- .../Instructions/EvmInstructions.CodeCopy.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs index ce5ff2061b7..79684a619a7 100644 --- a/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs +++ b/src/Nethermind/Nethermind.Evm/Instructions/EvmInstructions.CodeCopy.cs @@ -153,14 +153,14 @@ public static EvmExceptionType InstructionExtCodeCopy( if (!EvmCalculations.ChargeAccountAccessGas(ref gasAvailable, vm, address)) goto OutOfGas; - (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); - if (!result.IsZero) { // Update memory cost if the destination region requires expansion. if (!EvmCalculations.UpdateMemoryCost(vm.EvmState, ref gasAvailable, in a, result)) goto OutOfGas; + (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); + ICodeInfo codeInfo = vm.CodeInfoRepository .GetCachedCodeInfo(address, followDelegation: false, spec, out _); @@ -191,6 +191,10 @@ public static EvmExceptionType InstructionExtCodeCopy( vm.TxTracer.ReportMemoryChange(a, in slice); } } + else + { + (vm.WorldState as TracedAccessWorldState)?.AddAccountRead(address); + } return EvmExceptionType.None; // Jump forward to be unpredicted by the branch predictor.