diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockChainTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockChainTests.cs new file mode 100644 index 00000000000..2474c0cfa2b --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueBlockChainTests.cs @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Ethereum.Test.Base; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +[Explicit("These tests are not ready yet")] +public class PragueBlockChainTests : BlockchainTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public async Task Test(BlockchainTest test) => await RunTest(test); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy(), $"fixtures/blockchain_tests/prague"); + return loader.LoadTests().OfType(); + } +} diff --git a/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs new file mode 100644 index 00000000000..107b4e249f1 --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Pyspec.Test/PragueStateTests.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; +using Ethereum.Test.Base; +using FluentAssertions; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Pyspec.Test; + +[TestFixture] +[Parallelizable(ParallelScope.All)] +[Explicit("These tests are not ready yet")] +public class PragueStateTests : GeneralStateTestBase +{ + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) => RunTest(test).Pass.Should().BeTrue(); + + private static IEnumerable LoadTests() + { + TestsSourceLoader loader = new(new LoadPyspecTestsStrategy(), $"fixtures/state_tests/prague"); + return loader.LoadTests().Cast(); + } +} diff --git a/src/Nethermind/Ethereum.Blockchain.Test/EIP6110Tests.cs b/src/Nethermind/Ethereum.Blockchain.Test/EIP6110Tests.cs new file mode 100644 index 00000000000..d55bf6b3af1 --- /dev/null +++ b/src/Nethermind/Ethereum.Blockchain.Test/EIP6110Tests.cs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Ethereum.Test.Base; +using NUnit.Framework; + +namespace Ethereum.Blockchain.Test +{ + [TestFixture] + [Parallelizable(ParallelScope.All)] + public class Eip6110Tests : GeneralStateTestBase + { + [TestCaseSource(nameof(LoadTests))] + public void Test(GeneralStateTest test) + { + Assert.True(RunTest(test).Pass); + } + + public static IEnumerable LoadTests() + { + var loader = new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), "stEIP6110"); + return (IEnumerable)loader.LoadTests(); + } + } +} diff --git a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs index 11a8841a92f..0afabd5c361 100644 --- a/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/BlockchainTestBase.cs @@ -155,21 +155,23 @@ protected async Task RunTest(BlockchainTest test, Stopwatch? codeInfoRepository, _logManager); + TransactionProcessor? txProcessor = new( + specProvider, + stateProvider, + virtualMachine, + codeInfoRepository, + _logManager); + IBlockProcessor blockProcessor = new BlockProcessor( specProvider, blockValidator, rewardCalculator, - new BlockProcessor.BlockValidationTransactionsExecutor( - new TransactionProcessor( - specProvider, - stateProvider, - virtualMachine, - codeInfoRepository, - _logManager), + new BlockProcessor.BlockValidationTransactionsExecutor(txProcessor, stateProvider), stateProvider, receiptStorage, new BlockhashStore(specProvider, stateProvider), + txProcessor, _logManager); IBlockchainProcessor blockchainProcessor = new BlockchainProcessor( diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralStateTest.cs b/src/Nethermind/Ethereum.Test.Base/GeneralStateTest.cs index 46e1f68a3de..09158536941 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralStateTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralStateTest.cs @@ -37,6 +37,8 @@ public class GeneralStateTest : IEthereumTest public UInt256? ParentBlobGasUsed { get; set; } public UInt256? ParentExcessBlobGas { get; set; } + public Hash256? RequestsRoot { get; set; } + public override string ToString() { return $"{Path.GetFileName(Category)}.{Name}_{ForkName}"; diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 2e6758b1ccf..334f1f4dd48 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -109,6 +109,7 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) header.ParentBeaconBlockRoot = test.CurrentBeaconRoot; header.ExcessBlobGas = test.CurrentExcessBlobGas ?? (test.Fork is Cancun ? 0ul : null); header.BlobGasUsed = BlobGasCalculator.CalculateBlobGas(test.Transaction); + header.RequestsRoot = test.RequestsRoot; Stopwatch stopwatch = Stopwatch.StartNew(); IReleaseSpec? spec = specProvider.GetSpec((ForkActivation)test.CurrentNumber); @@ -178,7 +179,7 @@ private static void InitializeTestState(GeneralStateTest test, WorldState stateP } stateProvider.CreateAccount(accountState.Key, accountState.Value.Balance); - stateProvider.InsertCode(accountState.Key, accountState.Value.Code, specProvider.GenesisSpec); + stateProvider.InsertCode(accountState.Key, accountState.Value.Code, specProvider.GenesisSpec, false); stateProvider.SetNonce(accountState.Key, accountState.Value.Nonce); } diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index d180fa40dd8..5e33bde1488 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -208,6 +208,7 @@ public static IEnumerable Convert(string name, GeneralStateTes } List blockchainTests = new(); + Console.WriteLine($"Loaded {testJson}"); foreach (KeyValuePair postStateBySpec in testJson.Post) { int iterationNumber = 0; @@ -287,9 +288,11 @@ public static IEnumerable Convert(string json) { Dictionary testsInFile = _serializer.Deserialize>(json); + List tests = new(); foreach (KeyValuePair namedTest in testsInFile) { + Console.WriteLine($"Loading {namedTest.Key}\n {namedTest.Value.Post}"); tests.AddRange(Convert(namedTest.Key, namedTest.Value)); } diff --git a/src/Nethermind/Nethermind.AccountAbstraction.Test/AccountAbstractionRpcModuleTests.TestAccountAbstractionRpcBlockchain.cs b/src/Nethermind/Nethermind.AccountAbstraction.Test/AccountAbstractionRpcModuleTests.TestAccountAbstractionRpcBlockchain.cs index 4529540fa5b..15789b99c74 100644 --- a/src/Nethermind/Nethermind.AccountAbstraction.Test/AccountAbstractionRpcModuleTests.TestAccountAbstractionRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.AccountAbstraction.Test/AccountAbstractionRpcModuleTests.TestAccountAbstractionRpcBlockchain.cs @@ -190,6 +190,7 @@ protected override BlockProcessor CreateBlockProcessor() State, ReceiptStorage, new BlockhashStore(SpecProvider, State), + TxProcessor, LogManager); AbiParameterConverter.RegisterFactory(new AbiTypeFactory(new AbiTuple())); diff --git a/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs b/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs index cfbfb9cb9d7..f9c5eee3961 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/AuraBlockProcessorTests.cs @@ -160,6 +160,7 @@ void Process(AuRaBlockProcessor auRaBlockProcessor, int blockNumber, Hash256 sta LimboLogs.Instance, Substitute.For(), new WithdrawalProcessor(stateProvider, LimboLogs.Instance), + transactionProcessor, null, txFilter, contractRewriter: contractRewriter); diff --git a/src/Nethermind/Nethermind.AuRa.Test/Contract/AuRaContractGasLimitOverrideTests.cs b/src/Nethermind/Nethermind.AuRa.Test/Contract/AuRaContractGasLimitOverrideTests.cs index 7df3df7dcca..24b29330e4c 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/Contract/AuRaContractGasLimitOverrideTests.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/Contract/AuRaContractGasLimitOverrideTests.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -17,7 +16,6 @@ using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Logging; -using Nethermind.Trie.Pruning; using NUnit.Framework; namespace Nethermind.AuRa.Test.Contract; @@ -101,6 +99,7 @@ protected override BlockProcessor CreateBlockProcessor() LimboLogs.Instance, BlockTree, NullWithdrawalProcessor.Instance, + TxProcessor, null, null, GasLimitCalculator as AuRaContractGasLimitOverride); diff --git a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxCertifierFilterTests.cs b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxCertifierFilterTests.cs index 00a2894d175..f6403fa1184 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxCertifierFilterTests.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxCertifierFilterTests.cs @@ -19,7 +19,6 @@ using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Logging; -using Nethermind.Trie.Pruning; using Nethermind.TxPool; using NSubstitute; using NSubstitute.ExceptionExtensions; @@ -157,8 +156,8 @@ protected override BlockProcessor CreateBlockProcessor() LimboLogs.Instance, BlockTree, NullWithdrawalProcessor.Instance, - null - ); + TxProcessor, + null); } protected override Task AddBlocksOnStart() => Task.CompletedTask; diff --git a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs index e18f723dbf9..7bae62ac4df 100644 --- a/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs +++ b/src/Nethermind/Nethermind.AuRa.Test/Transactions/TxPermissionFilterTest.cs @@ -298,6 +298,7 @@ protected override BlockProcessor CreateBlockProcessor() LimboLogs.Instance, BlockTree, NullWithdrawalProcessor.Instance, + TxProcessor, null, PermissionBasedTxFilter); } diff --git a/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs index 58b3d0317bf..8e8172f1b0a 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/BlockProcessorTests.cs @@ -50,6 +50,7 @@ public void Prepared_block_contains_author_field() stateProvider, NullReceiptStorage.Instance, Substitute.For(), + transactionProcessor, LimboLogs.Instance); BlockHeader header = Build.A.BlockHeader.WithAuthor(TestItem.AddressD).TestObject; @@ -79,6 +80,7 @@ public void Recovers_state_on_cancel() stateProvider, NullReceiptStorage.Instance, Substitute.For(), + transactionProcessor, LimboLogs.Instance); BlockHeader header = Build.A.BlockHeader.WithNumber(1).WithAuthor(TestItem.AddressD).TestObject; diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs index fa9634a956d..78e1b46d72f 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Producers/DevBlockproducerTests.cs @@ -78,6 +78,7 @@ public void Test() stateProvider, NullReceiptStorage.Instance, new BlockhashStore(specProvider, stateProvider), + txProcessor, LimboLogs.Instance); BlockchainProcessor blockchainProcessor = new( blockTree, diff --git a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs index fa629f88730..a9d6497b430 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs @@ -78,6 +78,7 @@ public void Setup() stateProvider, NullReceiptStorage.Instance, new BlockhashStore(MainnetSpecProvider.Instance, stateProvider), + transactionProcessor, LimboLogs.Instance); _blockchainProcessor = new BlockchainProcessor( _blockTree, diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs index 8d5848e1052..860f9dab86e 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/BlockValidatorTests.cs @@ -3,6 +3,7 @@ using Nethermind.Consensus.Messages; using Nethermind.Consensus.Validators; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; @@ -202,5 +203,52 @@ public void ValidateSuggestedBlock_SuggestedBlockIsInvalid_CorrectErrorIsSet(Blo Assert.That(error, Does.StartWith(expectedError)); } + + [Test] + public void ValidateBodyAgainstHeader_BlockHasInvalidRequestRoot_ReturnsFalse() + { + Block block = Build.A.Block + .WithConsensusRequests(new ConsensusRequest[] { + Build.Deposit.WithIndex(0).TestObject, + Build.WithdrawalRequest.TestObject + }) + .TestObject; + block.Header.RequestsRoot = Keccak.OfAnEmptyString; + + Assert.That( + BlockValidator.ValidateBodyAgainstHeader(block.Header, block.Body), + Is.False); + } + + [Test] + public void ValidateBodyRequests_BlockHasReuests_InOrder_ReturnsTrue() + { + Block block = Build.A.Block + .WithConsensusRequests(new ConsensusRequest[] { + Build.Deposit.WithIndex(0).TestObject, + Build.WithdrawalRequest.TestObject + }) + .TestObject; + + Assert.That( + BlockValidator.ValidateRequestsOrder(block, out string? _), + Is.True); + } + + [Test] + public void ValidateBodyRequests_BlockHasReuests_OutOfOrder_ReturnsFalse() + { + Block block = Build.A.Block + .WithConsensusRequests(new ConsensusRequest[] { + Build.WithdrawalRequest.TestObject, + Build.Deposit.WithIndex(0).TestObject + }) + .TestObject; + + Assert.That( + BlockValidator.ValidateRequestsOrder(block, out string? _), + Is.False); + } + } } diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/WithdrawalValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/WithdrawalValidatorTests.cs index e26ab68e503..8d065f2666e 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/WithdrawalValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/WithdrawalValidatorTests.cs @@ -39,8 +39,7 @@ public void Withdrawals_with_incorrect_withdrawals_root_are_invalid() { ISpecProvider specProvider = new CustomSpecProvider(((ForkActivation)0, Shanghai.Instance)); BlockValidator blockValidator = new(Always.Valid, Always.Valid, Always.Valid, specProvider, LimboLogs.Instance); - Withdrawal[] withdrawals = { TestItem.WithdrawalA_1Eth, TestItem.WithdrawalB_2Eth }; - Hash256 withdrawalRoot = new WithdrawalTrie(withdrawals).RootHash; + Withdrawal[] withdrawals = [TestItem.WithdrawalA_1Eth, TestItem.WithdrawalB_2Eth]; bool isValid = blockValidator.ValidateSuggestedBlock(Build.A.Block.WithWithdrawals(withdrawals).WithWithdrawalsRoot(TestItem.KeccakD).TestObject); Assert.False(isValid); } @@ -50,7 +49,7 @@ public void Empty_withdrawals_are_valid_post_shanghai() { ISpecProvider specProvider = new CustomSpecProvider(((ForkActivation)0, Shanghai.Instance)); BlockValidator blockValidator = new(Always.Valid, Always.Valid, Always.Valid, specProvider, LimboLogs.Instance); - Withdrawal[] withdrawals = { }; + Withdrawal[] withdrawals = []; Hash256 withdrawalRoot = new WithdrawalTrie(withdrawals).RootHash; bool isValid = blockValidator.ValidateSuggestedBlock(Build.A.Block.WithWithdrawals(withdrawals).WithWithdrawalsRoot(withdrawalRoot).TestObject); Assert.True(isValid); @@ -61,7 +60,7 @@ public void Correct_withdrawals_block_post_shanghai() { ISpecProvider specProvider = new CustomSpecProvider(((ForkActivation)0, Shanghai.Instance)); BlockValidator blockValidator = new(Always.Valid, Always.Valid, Always.Valid, specProvider, LimboLogs.Instance); - Withdrawal[] withdrawals = { TestItem.WithdrawalA_1Eth, TestItem.WithdrawalB_2Eth }; + Withdrawal[] withdrawals = [TestItem.WithdrawalA_1Eth, TestItem.WithdrawalB_2Eth]; Hash256 withdrawalRoot = new WithdrawalTrie(withdrawals).RootHash; bool isValid = blockValidator.ValidateSuggestedBlock(Build.A.Block.WithWithdrawals(withdrawals).WithWithdrawalsRoot(withdrawalRoot).TestObject); Assert.True(isValid); diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs index 89276e3c7f8..7f9244a187a 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/BeaconBlockRootHandler.cs @@ -1,39 +1,57 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Core.Specs; +using System; using Nethermind.Core; -using Nethermind.Int256; -using Nethermind.State; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Crypto; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.State; -namespace Nethermind.Consensus.BeaconBlockRoot; - +namespace Nethermind.Blockchain.BeaconBlockRoot; public class BeaconBlockRootHandler : IBeaconBlockRootHandler { - public void ApplyContractStateChanges(Block block, IReleaseSpec spec, IWorldState stateProvider) + private readonly ITransactionProcessor _processor; + private static Address Default4788Address = new Address("0x000f3df6d732807ef1319fb7b8bb8522d0beac02"); + private readonly ILogger _logger; + private const long GasLimit = 30_000_000L; + public BeaconBlockRootHandler( + ITransactionProcessor processor, + ILogManager logManager) { + _processor = processor; + _logger = logManager.GetClassLogger(); + } + public void ExecuteSystemCall(Block block, IReleaseSpec spec) + { + BlockHeader? header = block.Header; if (!spec.IsBeaconBlockRootAvailable || - block.IsGenesis || - block.Header.ParentBeaconBlockRoot is null) - return; - - Address eip4788Account = spec.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress; - - if (!stateProvider.AccountExists(eip4788Account)) - return; - - UInt256 timestamp = (UInt256)block.Timestamp; - Hash256 parentBeaconBlockRoot = block.ParentBeaconBlockRoot; - - UInt256.Mod(timestamp, Eip4788Constants.HistoryBufferLength, out UInt256 timestampReduced); - UInt256 rootIndex = timestampReduced + Eip4788Constants.HistoryBufferLength; - - StorageCell tsStorageCell = new(eip4788Account, timestampReduced); - StorageCell brStorageCell = new(eip4788Account, rootIndex); - - stateProvider.Set(tsStorageCell, Bytes.WithoutLeadingZeros(timestamp.ToBigEndian()).ToArray()); - stateProvider.Set(brStorageCell, Bytes.WithoutLeadingZeros(parentBeaconBlockRoot.Bytes).ToArray()); + header.IsGenesis || + header.ParentBeaconBlockRoot is null) return; + + Transaction? transaction = new() + { + Value = UInt256.Zero, + Data = header.ParentBeaconBlockRoot.Bytes.ToArray(), + To = spec.Eip4788ContractAddress ?? Default4788Address, + SenderAddress = Address.SystemUser, + GasLimit = GasLimit, + GasPrice = UInt256.Zero, + }; + transaction.Hash = transaction.CalculateHash(); + + try + { + _processor.Execute(transaction, header, NullTxTracer.Instance); + } + catch (Exception e) + { + if (_logger.IsError) _logger.Error("Error during calling BeaconBlockRoot contract", e); + } } } diff --git a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs index d30074fc4cc..8ed27077c04 100644 --- a/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs +++ b/src/Nethermind/Nethermind.Blockchain/BeaconBlockRoot/IBeaconBlockRootHandler.cs @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using Nethermind.Core.Specs; using Nethermind.Core; +using Nethermind.Core.Specs; using Nethermind.State; -namespace Nethermind.Consensus.BeaconBlockRoot; +namespace Nethermind.Blockchain.BeaconBlockRoot; public interface IBeaconBlockRootHandler { - void ApplyContractStateChanges(Block block, IReleaseSpec spec, IWorldState state); + void ExecuteSystemCall(Block block, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Blockchain/Contracts/Contract.cs b/src/Nethermind/Nethermind.Blockchain/Contracts/Contract.cs index cbb39fff9f0..3b4eec49104 100644 --- a/src/Nethermind/Nethermind.Blockchain/Contracts/Contract.cs +++ b/src/Nethermind/Nethermind.Blockchain/Contracts/Contract.cs @@ -201,7 +201,6 @@ protected byte[] CallCore(ITransactionProcessor transactionProcessor, BlockHeade return tracer.ReturnValue; } } - protected object[] DecodeReturnData(string functionName, byte[] data) { AbiEncodingInfo abiEncodingInfo = AbiDefinition.GetFunction(functionName).GetReturnInfo(); diff --git a/src/Nethermind/Nethermind.Blockchain/FullPruning/FullPruner.cs b/src/Nethermind/Nethermind.Blockchain/FullPruning/FullPruner.cs old mode 100755 new mode 100644 index b8c6ef1331f..e951e03226a --- a/src/Nethermind/Nethermind.Blockchain/FullPruning/FullPruner.cs +++ b/src/Nethermind/Nethermind.Blockchain/FullPruning/FullPruner.cs @@ -138,6 +138,7 @@ await WaitForMainChainChange((e) => } catch (Exception e) { + Console.Out.WriteLine($"Its {e}"); if (_logger.IsError) _logger.Error("full pruning failed. ", e); } finally diff --git a/src/Nethermind/Nethermind.Blockchain/GenesisLoader.cs b/src/Nethermind/Nethermind.Blockchain/GenesisLoader.cs index 28f4979bafe..83263ec8fcb 100644 --- a/src/Nethermind/Nethermind.Blockchain/GenesisLoader.cs +++ b/src/Nethermind/Nethermind.Blockchain/GenesisLoader.cs @@ -14,7 +14,6 @@ using Nethermind.Evm.TransactionProcessing; using Nethermind.Specs.ChainSpecStyle; using Nethermind.State; -using Nethermind.Consensus.BeaconBlockRoot; namespace Nethermind.Blockchain { @@ -24,7 +23,6 @@ public class GenesisLoader private readonly ISpecProvider _specProvider; private readonly IWorldState _stateProvider; private readonly ITransactionProcessor _transactionProcessor; - private readonly BeaconBlockRootHandler _beaconBlockRootHandler; public GenesisLoader( ChainSpec chainSpec, @@ -36,7 +34,6 @@ public GenesisLoader( _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); _transactionProcessor = transactionProcessor ?? throw new ArgumentNullException(nameof(transactionProcessor)); - _beaconBlockRootHandler = new BeaconBlockRootHandler(); } public Block Load() diff --git a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs index 69e6cb7156f..63c2696a38d 100644 --- a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs +++ b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs @@ -141,6 +141,7 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f stateProvider, NullReceiptStorage.Instance, new BlockhashStore(goerliSpecProvider, stateProvider), + transactionProcessor, nodeLogManager); BlockchainProcessor processor = new(blockTree, blockProcessor, new AuthorRecoveryStep(snapshotManager), stateReader, nodeLogManager, BlockchainProcessor.Options.NoReceipts); @@ -160,6 +161,7 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f minerStateProvider, NullReceiptStorage.Instance, new BlockhashStore(goerliSpecProvider, minerStateProvider), + minerTransactionProcessor, nodeLogManager); BlockchainProcessor minerProcessor = new(blockTree, minerBlockProcessor, new AuthorRecoveryStep(snapshotManager), stateReader, nodeLogManager, BlockchainProcessor.Options.NoReceipts); diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/AuRaBlockProcessor.cs b/src/Nethermind/Nethermind.Consensus.AuRa/AuRaBlockProcessor.cs index 51190c3dd7e..dda607567c3 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/AuRaBlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/AuRaBlockProcessor.cs @@ -8,6 +8,7 @@ using Nethermind.Blockchain.Receipts; using Nethermind.Consensus.AuRa.Validators; using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Transactions; using Nethermind.Consensus.Validators; @@ -17,6 +18,7 @@ using Nethermind.Crypto; using Nethermind.Evm; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; using Nethermind.State; using Nethermind.TxPool; @@ -42,11 +44,13 @@ public AuRaBlockProcessor( ILogManager logManager, IBlockFinder blockTree, IWithdrawalProcessor withdrawalProcessor, + ITransactionProcessor transactionProcessor, IAuRaValidator? auRaValidator, ITxFilter? txFilter = null, AuRaContractGasLimitOverride? gasLimitOverride = null, ContractRewriter? contractRewriter = null, - IBlockCachePreWarmer? preWarmer = null) + IBlockCachePreWarmer? preWarmer = null, + IConsensusRequestsProcessor? consensusRequestsProcessor = null) : base( specProvider, blockValidator, @@ -55,9 +59,11 @@ public AuRaBlockProcessor( stateProvider, receiptStorage, new BlockhashStore(specProvider, stateProvider), + transactionProcessor, logManager, withdrawalProcessor, - preWarmer: preWarmer) + preWarmer: preWarmer, + consensusRequestsProcessor: consensusRequestsProcessor) { _specProvider = specProvider; _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/BeaconBlockRoot/NullBeaconBlockRootHandler.cs b/src/Nethermind/Nethermind.Consensus.AuRa/BeaconBlockRoot/NullBeaconBlockRootHandler.cs deleted file mode 100644 index 96138999814..00000000000 --- a/src/Nethermind/Nethermind.Consensus.AuRa/BeaconBlockRoot/NullBeaconBlockRootHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited -// SPDX-License-Identifier: LGPL-3.0-only - -using Nethermind.Consensus.BeaconBlockRoot; -using Nethermind.Core; -using Nethermind.Core.Specs; -using Nethermind.State; - -namespace Nethermind.Consensus.AuRa.BeaconBlockRoot; -internal class NullBeaconBlockRootHandler : IBeaconBlockRootHandler -{ - public void ApplyContractStateChanges(Block block, IReleaseSpec spec, IWorldState state) - { - } - - public static IBeaconBlockRootHandler Instance { get; } = new NullBeaconBlockRootHandler(); -} diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs index 9253574e72a..3bfac506256 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs @@ -109,6 +109,7 @@ protected virtual AuRaBlockProcessor NewAuraBlockProcessor(ITxFilter txFilter, B _api.LogManager, _api.BlockTree!, NullWithdrawalProcessor.Instance, + _api.TransactionProcessor, CreateAuRaValidator(), txFilter, GetGasLimitCalculator(), diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/StartBlockProducerAuRa.cs b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/StartBlockProducerAuRa.cs index ee84c5f21a2..4abf23d082e 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/StartBlockProducerAuRa.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/StartBlockProducerAuRa.cs @@ -157,6 +157,7 @@ private BlockProcessor CreateBlockProcessor(IReadOnlyTxProcessingScope changeabl _api.LogManager, _api.BlockTree, NullWithdrawalProcessor.Instance, + _api.TransactionProcessor, _validator, auRaTxFilter, CreateGasLimitCalculator(_api) as AuRaContractGasLimitOverride, diff --git a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs index 3f2b574975b..421cbe19677 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs @@ -15,6 +15,7 @@ using Nethermind.Consensus.Producers; using Nethermind.Consensus.Transactions; using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Crypto; @@ -493,7 +494,8 @@ ILogManager logManager header, selectedTxs, Array.Empty(), - spec.WithdrawalsEnabled ? Enumerable.Empty() : null + spec.WithdrawalsEnabled ? Enumerable.Empty() : null, + spec.RequestsEnabled ? Enumerable.Empty() : null ); header.TxRoot = TxTrie.CalculateRoot(block.Transactions); block.Header.Author = _sealer.Address; diff --git a/src/Nethermind/Nethermind.Consensus.Clique/CliquePlugin.cs b/src/Nethermind/Nethermind.Consensus.Clique/CliquePlugin.cs index e8dbbcf0175..aa279192cb7 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/CliquePlugin.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/CliquePlugin.cs @@ -112,6 +112,7 @@ public IBlockProducer InitBlockProducer(ITxSource? additionalTxSource = null) scope.WorldState, NullReceiptStorage.Instance, new BlockhashStore(getFromApi.SpecProvider, scope.WorldState), + getFromApi.TransactionProcessor, getFromApi.LogManager, new BlockProductionWithdrawalProcessor(new WithdrawalProcessor(scope.WorldState, getFromApi.LogManager))); diff --git a/src/Nethermind/Nethermind.Consensus.Ethash/NethDevPlugin.cs b/src/Nethermind/Nethermind.Consensus.Ethash/NethDevPlugin.cs index 4297e243a42..c1dec5aeddd 100644 --- a/src/Nethermind/Nethermind.Consensus.Ethash/NethDevPlugin.cs +++ b/src/Nethermind/Nethermind.Consensus.Ethash/NethDevPlugin.cs @@ -82,6 +82,7 @@ public IBlockProducer InitBlockProducer(ITxSource? additionalTxSource = null) scope.WorldState, NullReceiptStorage.Instance, new BlockhashStore(getFromApi.SpecProvider, scope.WorldState), + getFromApi.TransactionProcessor, getFromApi.LogManager); IBlockchainProcessor producerChainProcessor = new BlockchainProcessor( diff --git a/src/Nethermind/Nethermind.Consensus.Test/DepositProcessorTests.cs b/src/Nethermind/Nethermind.Consensus.Test/DepositProcessorTests.cs new file mode 100644 index 00000000000..af3e12b1505 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus.Test/DepositProcessorTests.cs @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using FluentAssertions; +using Nethermind.Abi; +using Nethermind.Consensus.Requests; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using NSubstitute; +using NUnit.Framework; + + +namespace Nethermind.Consensus.Test; + +public class DepositProcessorTests +{ + [Test] + public void ShouldProcessDeposit() + { + Block block = Build.A.Block.TestObject; + DepositsProcessor depositsProcessor = new(); + + var deposit = new Deposit() + { + Amount = 32000000000, + Index = 0, + Pubkey = Bytes.FromHexString( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + Signature = Bytes.FromHexString( + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), + WithdrawalCredentials = + Bytes.FromHexString("0000000000000000000000000000000000000000000000000000000000000002") + }; + + TxReceipt txReceipt = Build.A.Receipt.WithLogs( + Build.A.LogEntry.WithData( + Bytes.FromHexString( + "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000080040597307000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000" + ) + ) + .WithAddress( + new Core.Address(Bytes.FromHexString("0x00000000219ab540356cbb839cbe05303d7705fa")) + ).TestObject + ).TestObject; + + IReleaseSpec spec = Substitute.For(); + + spec.DepositsEnabled.Returns(true); + spec.DepositContractAddress.Returns( + new Core.Address(Bytes.FromHexString("0x00000000219ab540356cbb839cbe05303d7705fa")) + ); + + var processedDeposits = depositsProcessor.ProcessDeposits(block, new[] { txReceipt }, spec).ToList(); + + Assert.That(processedDeposits, Has.Count.EqualTo(1)); + + Deposit processedDeposit = processedDeposits[0]; + + processedDeposit.Should().BeEquivalentTo(deposit); + } +} diff --git a/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs b/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs index 598c8784907..145810a7364 100644 --- a/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs +++ b/src/Nethermind/Nethermind.Consensus/EngineApiVersions.cs @@ -8,4 +8,5 @@ public static class EngineApiVersions public const int Paris = 1; public const int Shanghai = 2; public const int Cancun = 3; + public const int Prague = 4; } diff --git a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs index d949d8fcfe5..b40be7bc3aa 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/BlockErrorMessages.cs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; -using Nethermind.Crypto; +using Nethermind.Core.Crypto; +using Nethermind.Serialization.Json; namespace Nethermind.Consensus.Messages; public static class BlockErrorMessages @@ -116,4 +117,9 @@ public static string InvalidTxInBlock(int i) => public const string NegativeGasUsed = "NegativeGasUsed: Cannot be negative."; + + public static string MissingRequests => "MissingRequests: Requests cannot be null in block when EIP-6110 or EIP-7002 are activated."; + public static string RequestsNotEnabled => "RequestsNotEnabled: Requests must be null in block when EIP-6110 and EIP-7002 are not activated."; + public static string InvalidRequestsRoot(Hash256? expected, Hash256? actual) => $"InvalidRequestsRoot: Requests root hash mismatch in block: expected {expected}, got {actual}"; + public static string InvalidRequestsOrder => "InvalidRequestsOrder: Requests are not in the correct order in block."; } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs index ba82ee02eb8..6e0eab907da 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockExtensions.cs @@ -18,8 +18,8 @@ internal static class BlockExtensions { public static Block CreateCopy(this Block block, BlockHeader header) => block is BlockToProduce blockToProduce - ? new BlockToProduce(header, blockToProduce.Transactions, blockToProduce.Uncles, blockToProduce.Withdrawals) - : new Block(header, block.Transactions, block.Uncles, block.Withdrawals); + ? new BlockToProduce(header, blockToProduce.Transactions, blockToProduce.Uncles, blockToProduce.Withdrawals, blockToProduce.Requests) + : new Block(header, block.Transactions, block.Uncles, block.Withdrawals, block.Requests); public static IEnumerable GetTransactions(this Block block) => block is BlockToProduce blockToProduce diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs index fe9ad56f4d7..f1d13e5ceae 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs @@ -9,10 +9,11 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Blockchain; +using Nethermind.Blockchain.BeaconBlockRoot; using Nethermind.Blockchain.Blocks; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.Receipts; -using Nethermind.Consensus.BeaconBlockRoot; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; @@ -22,6 +23,7 @@ using Nethermind.Crypto; using Nethermind.Evm; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Specs.Forks; @@ -42,6 +44,8 @@ public partial class BlockProcessor : IBlockProcessor private readonly IBlockValidator _blockValidator; private readonly IRewardCalculator _rewardCalculator; private readonly IBlockProcessor.IBlockTransactionsExecutor _blockTransactionsExecutor; + + private readonly IConsensusRequestsProcessor _consensusRequestsProcessor; private readonly IBlockhashStore _blockhashStore; private readonly IBlockCachePreWarmer? _preWarmer; private const int MaxUncommittedBlocks = 64; @@ -60,10 +64,13 @@ public BlockProcessor( IWorldState? stateProvider, IReceiptStorage? receiptStorage, IBlockhashStore? blockHashStore, + ITransactionProcessor transactionProcessor, ILogManager? logManager, IWithdrawalProcessor? withdrawalProcessor = null, + IBeaconBlockRootHandler? beaconBlockRootHandler = null, IReceiptsRootCalculator? receiptsRootCalculator = null, - IBlockCachePreWarmer? preWarmer = null) + IBlockCachePreWarmer? preWarmer = null, + IConsensusRequestsProcessor? consensusRequestsProcessor = null) { _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); @@ -74,9 +81,11 @@ public BlockProcessor( _rewardCalculator = rewardCalculator ?? throw new ArgumentNullException(nameof(rewardCalculator)); _blockTransactionsExecutor = blockTransactionsExecutor ?? throw new ArgumentNullException(nameof(blockTransactionsExecutor)); _receiptsRootCalculator = receiptsRootCalculator ?? ReceiptsRootCalculator.Instance; + _beaconBlockRootHandler = beaconBlockRootHandler ?? new BeaconBlockRootHandler(transactionProcessor, logManager); + _consensusRequestsProcessor = consensusRequestsProcessor ?? new ConsensusRequestsProcessor(transactionProcessor); + _blockhashStore = blockHashStore ?? throw new ArgumentNullException(nameof(blockHashStore)); _preWarmer = preWarmer; - _beaconBlockRootHandler = new BeaconBlockRootHandler(); ReceiptsTracer = new BlockReceiptsTracer(); } @@ -256,7 +265,7 @@ protected virtual TxReceipt[] ProcessBlock( ReceiptsTracer.SetOtherTracer(blockTracer); ReceiptsTracer.StartNewBlockTrace(block); - _beaconBlockRootHandler.ApplyContractStateChanges(block, spec, _stateProvider); + _beaconBlockRootHandler.ExecuteSystemCall(block, spec); _blockhashStore.ApplyBlockhashStateChanges(block.Header); _stateProvider.Commit(spec, commitStorageRoots: false); @@ -271,6 +280,8 @@ protected virtual TxReceipt[] ProcessBlock( block.Header.ReceiptsRoot = _receiptsRootCalculator.GetReceiptsRoot(receipts, spec, block.ReceiptsRoot); ApplyMinerRewards(block, blockTracer, spec); _withdrawalProcessor.ProcessWithdrawals(block, spec); + _consensusRequestsProcessor.ProcessRequests(spec, _stateProvider, block, receipts); + ReceiptsTracer.EndBlockTrace(); _stateProvider.Commit(spec, commitStorageRoots: true); @@ -295,7 +306,7 @@ protected virtual TxReceipt[] ProcessBlock( // TODO: block processor pipeline private void StoreTxReceipts(Block block, TxReceipt[] txReceipts) { - // Setting canonical is done when the BlockAddedToMain event is firec + // Setting canonical is done when the BlockAddedToMain event is fired _receiptStorage.Insert(block, txReceipts, false); } @@ -328,8 +339,9 @@ private Block PrepareBlockForProcessing(Block suggestedBlock) ReceiptsRoot = bh.ReceiptsRoot, BaseFeePerGas = bh.BaseFeePerGas, WithdrawalsRoot = bh.WithdrawalsRoot, + RequestsRoot = bh.RequestsRoot, IsPostMerge = bh.IsPostMerge, - ParentBeaconBlockRoot = bh.ParentBeaconBlockRoot, + ParentBeaconBlockRoot = bh.ParentBeaconBlockRoot }; if (!ShouldComputeStateRoot(bh)) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyChainProcessingEnv.cs b/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyChainProcessingEnv.cs index f4bc42832c2..9112c38d115 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyChainProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ReadOnlyChainProcessingEnv.cs @@ -68,6 +68,7 @@ IBlockProcessor.IBlockTransactionsExecutor transactionsExecutor scope.WorldState, receiptStorage, new BlockhashStore(specProvider, scope.WorldState), + scope.TransactionProcessor, logManager); } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs index a2c71caa342..1e66df7efa1 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerEnvFactory.cs @@ -7,6 +7,7 @@ using Nethermind.Config; using Nethermind.Consensus.Comparers; using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Transactions; using Nethermind.Consensus.Validators; @@ -33,6 +34,7 @@ public class BlockProducerEnvFactory : IBlockProducerEnvFactory protected readonly ITransactionComparerProvider _transactionComparerProvider; protected readonly IBlocksConfig _blocksConfig; protected readonly ILogManager _logManager; + private readonly IConsensusRequestsProcessor? _consensusRequestsProcessor; public IBlockTransactionsExecutorFactory TransactionsExecutorFactory { get; set; } @@ -47,7 +49,8 @@ public BlockProducerEnvFactory( ITxPool txPool, ITransactionComparerProvider transactionComparerProvider, IBlocksConfig blocksConfig, - ILogManager logManager) + ILogManager logManager, + IConsensusRequestsProcessor? consensusRequestsProcessor = null) { _worldStateManager = worldStateManager; _blockTree = blockTree; @@ -60,6 +63,7 @@ public BlockProducerEnvFactory( _transactionComparerProvider = transactionComparerProvider; _blocksConfig = blocksConfig; _logManager = logManager; + _consensusRequestsProcessor = consensusRequestsProcessor; TransactionsExecutorFactory = new BlockProducerTransactionsExecutorFactory(specProvider, logManager); } @@ -149,8 +153,10 @@ protected virtual BlockProcessor CreateBlockProcessor( readOnlyTxProcessingEnv.WorldState, receiptStorage, new BlockhashStore(_specProvider, readOnlyTxProcessingEnv.WorldState), + readOnlyTxProcessingEnv.TransactionProcessor, logManager, - new BlockProductionWithdrawalProcessor(new WithdrawalProcessor(readOnlyTxProcessingEnv.WorldState, logManager))); - + new BlockProductionWithdrawalProcessor(new WithdrawalProcessor(readOnlyTxProcessingEnv.WorldState, logManager)), + consensusRequestsProcessor: _consensusRequestsProcessor + ); } } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs index 7a23c516027..be52d7b7203 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; //TODO: Redo clique block producer [assembly: InternalsVisibleTo("Nethermind.Consensus.Clique")] @@ -29,12 +30,12 @@ internal class BlockToProduce : Block } } - public BlockToProduce( - BlockHeader blockHeader, + public BlockToProduce(BlockHeader blockHeader, IEnumerable transactions, IEnumerable uncles, - IEnumerable? withdrawals = null) - : base(blockHeader, Array.Empty(), uncles, withdrawals) + IEnumerable? withdrawals = null, + IEnumerable? requests = null) + : base(blockHeader, Array.Empty(), uncles, withdrawals, requests) { Transactions = transactions; } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/PayloadAttributes.cs b/src/Nethermind/Nethermind.Consensus/Producers/PayloadAttributes.cs index 0a38cffe602..d565349875e 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/PayloadAttributes.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/PayloadAttributes.cs @@ -155,7 +155,7 @@ public virtual PayloadAttributesValidationResult Validate( apiVersion: apiVersion, actualVersion: this.GetVersion(), timestampVersion: specProvider.GetSpec(ForkActivation.TimestampOnly(Timestamp)) - .ExpectedEngineSpecVersion(), + .ExpectedPayloadAttributesVersion(), "PayloadAttributesV", out error); } @@ -172,7 +172,7 @@ public static int GetVersion(this PayloadAttributes executionPayload) => _ => EngineApiVersions.Paris }; - public static int ExpectedEngineSpecVersion(this IReleaseSpec spec) => + public static int ExpectedPayloadAttributesVersion(this IReleaseSpec spec) => spec switch { { IsEip4844Enabled: true } => EngineApiVersions.Cancun, diff --git a/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs new file mode 100644 index 00000000000..e8dd43b8de8 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Serialization.Rlp; +using Nethermind.State; +using Nethermind.State.Proofs; + +namespace Nethermind.Consensus.Requests; + +public class ConsensusRequestsProcessor(ITransactionProcessor transactionProcessor) : IConsensusRequestsProcessor +{ + private readonly ConsolidationRequestsProcessor _consolidationRequestsProcessor = new(transactionProcessor); + private readonly WithdrawalRequestsProcessor _withdrawalRequestsProcessor = new(transactionProcessor); + private readonly IDepositsProcessor _depositsProcessor = new DepositsProcessor(); + + public void ProcessRequests(IReleaseSpec spec, IWorldState state, Block block, TxReceipt[] receipts) + { + if (!spec.RequestsEnabled) + return; + + using ArrayPoolList requestsList = new(receipts.Length * 2); + + requestsList.AddRange(_depositsProcessor.ProcessDeposits(block, receipts, spec)); + requestsList.AddRange(_withdrawalRequestsProcessor.ReadWithdrawalRequests(spec, state, block)); + requestsList.AddRange(_consolidationRequestsProcessor.ReadConsolidationRequests(spec, state, block)); + + ConsensusRequest[] requests = requestsList.ToArray(); + Hash256 root = new RequestsTrie(requests).RootHash; + block.Body.Requests = requests; + block.Header.RequestsRoot = root; + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/ConsolidationRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/ConsolidationRequestProcessor.cs new file mode 100644 index 00000000000..876abdfe8d3 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/ConsolidationRequestProcessor.cs @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Crypto; +using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.State; + +namespace Nethermind.Consensus.Requests; + +// https://eips.ethereum.org/EIPS/eip-7251#block-processing +public class ConsolidationRequestsProcessor(ITransactionProcessor transactionProcessor) +{ + private const long GasLimit = 30_000_000L; + + public IEnumerable ReadConsolidationRequests(IReleaseSpec spec, IWorldState state, Block block) + { + if (!spec.ConsolidationRequestsEnabled) + yield break; + + Address eip7251Account = spec.Eip7251ContractAddress; + if (!state.AccountExists(eip7251Account)) // not needed anymore? + yield break; + + CallOutputTracer tracer = new(); + + Transaction? transaction = new() + { + Value = UInt256.Zero, + Data = Array.Empty(), + To = spec.Eip7251ContractAddress, + SenderAddress = Address.SystemUser, + GasLimit = GasLimit, + GasPrice = UInt256.Zero, + }; + transaction.Hash = transaction.CalculateHash(); + + transactionProcessor.Execute(transaction, new BlockExecutionContext(block.Header), tracer); + var result = tracer.ReturnValue; + if (result == null || result.Length == 0) + yield break; + + int sizeOfClass = 20 + 48 + 48; + int count = result.Length / sizeOfClass; + for (int i = 0; i < count; ++i) + { + ConsolidationRequest request = new(); + Span span = new Span(result, i * sizeOfClass, sizeOfClass); + request.SourceAddress = new Address(span.Slice(0, 20).ToArray()); + request.SourcePubkey = span.Slice(20, 48).ToArray(); + request.TargetPubkey = span.Slice(68, 48).ToArray(); + + yield return request; + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/DepositsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/DepositsProcessor.cs new file mode 100644 index 00000000000..4566019db4f --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/DepositsProcessor.cs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Abi; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Specs; +using System.Linq; +using Nethermind.Core.Extensions; +using System; +using Nethermind.Int256; + +namespace Nethermind.Consensus.Requests; + +public class DepositsProcessor : IDepositsProcessor +{ + private AbiSignature depositEventABI = new("DepositEvent", AbiType.DynamicBytes, AbiType.DynamicBytes, AbiType.DynamicBytes, AbiType.DynamicBytes, AbiType.DynamicBytes); + AbiEncoder abiEncoder = AbiEncoder.Instance; + + public IEnumerable ProcessDeposits(Block block, TxReceipt[] receipts, IReleaseSpec spec) + { + if (spec.DepositsEnabled) + { + for (int i = 0; i < receipts.Length; i++) + { + LogEntry[]? logEntries = receipts[i].Logs; + if (logEntries is not null) + { + for (int index = 0; index < logEntries.Length; index++) + { + LogEntry log = logEntries[index]; + if (log.LoggersAddress == spec.DepositContractAddress) + { + var result = abiEncoder.Decode(AbiEncodingStyle.None, depositEventABI, log.Data); + + var newDeposit = new Deposit() + { + Pubkey = (byte[])result[0], + WithdrawalCredentials = (byte[])result[1], + Amount = BitConverter.ToUInt64((byte[])result[2], 0), + Signature = (byte[])result[3], + Index = BitConverter.ToUInt64((byte[])result[4], 0) + }; + + yield return newDeposit; + } + } + } + } + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/IConsensusRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/IConsensusRequestsProcessor.cs new file mode 100644 index 00000000000..01ed8f37278 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/IConsensusRequestsProcessor.cs @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.State; + +namespace Nethermind.Consensus.Requests; + +public interface IConsensusRequestsProcessor +{ + void ProcessRequests(IReleaseSpec spec, IWorldState state, Block block, TxReceipt[] receipts); +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/IDepositsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/IDepositsProcessor.cs new file mode 100644 index 00000000000..f73cdaa4556 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/IDepositsProcessor.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Specs; + +namespace Nethermind.Consensus.Requests; + +public interface IDepositsProcessor +{ + IEnumerable ProcessDeposits(Block block, TxReceipt[] receipts, IReleaseSpec spec); +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs new file mode 100644 index 00000000000..37b9a3ee6ec --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Specs; +using Nethermind.Crypto; +using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.State; +using System.Buffers.Binary; + +namespace Nethermind.Consensus.Requests; + +// https://eips.ethereum.org/EIPS/eip-7002#block-processing +public class WithdrawalRequestsProcessor(ITransactionProcessor transactionProcessor) +{ + private const long GasLimit = 30_000_000L; + + public IEnumerable ReadWithdrawalRequests(IReleaseSpec spec, IWorldState state, Block block) + { + if (!spec.WithdrawalRequestsEnabled) + yield break; + + Address eip7002Account = spec.Eip7002ContractAddress; + if (!state.AccountExists(eip7002Account)) // not needed anymore? + yield break; + + CallOutputTracer tracer = new(); + + Transaction? transaction = new() + { + Value = UInt256.Zero, + Data = Array.Empty(), + To = spec.Eip7002ContractAddress, + SenderAddress = Address.SystemUser, + GasLimit = GasLimit, + GasPrice = UInt256.Zero, + }; + transaction.Hash = transaction.CalculateHash(); + + transactionProcessor.Execute(transaction, new BlockExecutionContext(block.Header), tracer); + var result = tracer.ReturnValue; + if (result == null || result.Length == 0) + yield break; + + int sizeOfClass = 20 + 48 + 8; + int count = result.Length / sizeOfClass; + for (int i = 0; i < count; ++i) + { + WithdrawalRequest request = new(); + Span span = new Span(result, i * sizeOfClass, sizeOfClass); + request.SourceAddress = new Address(span.Slice(0, 20).ToArray()); + request.ValidatorPubkey = span.Slice(20, 48).ToArray(); + request.Amount = BinaryPrimitives.ReadUInt64BigEndian(span.Slice(68, 8)); + + yield return request; + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 04e0ca08f68..9652e89cb54 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; using System.Text; using Nethermind.Blockchain; using Nethermind.Consensus.Messages; @@ -12,32 +15,25 @@ using Nethermind.Evm; using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Serialization.Rlp; using Nethermind.State.Proofs; using Nethermind.TxPool; namespace Nethermind.Consensus.Validators; -public class BlockValidator : IBlockValidator +public class BlockValidator( + ITxValidator? txValidator, + IHeaderValidator? headerValidator, + IUnclesValidator? unclesValidator, + ISpecProvider? specProvider, + ILogManager? logManager) + : IBlockValidator { - private readonly IHeaderValidator _headerValidator; - private readonly ITxValidator _txValidator; - private readonly IUnclesValidator _unclesValidator; - private readonly ISpecProvider _specProvider; - private readonly ILogger _logger; - - public BlockValidator( - ITxValidator? txValidator, - IHeaderValidator? headerValidator, - IUnclesValidator? unclesValidator, - ISpecProvider? specProvider, - ILogManager? logManager) - { - _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); - _txValidator = txValidator ?? throw new ArgumentNullException(nameof(txValidator)); - _unclesValidator = unclesValidator ?? throw new ArgumentNullException(nameof(unclesValidator)); - _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _headerValidator = headerValidator ?? throw new ArgumentNullException(nameof(headerValidator)); - } + private readonly IHeaderValidator _headerValidator = headerValidator ?? throw new ArgumentNullException(nameof(headerValidator)); + private readonly ITxValidator _txValidator = txValidator ?? throw new ArgumentNullException(nameof(txValidator)); + private readonly IUnclesValidator _unclesValidator = unclesValidator ?? throw new ArgumentNullException(nameof(unclesValidator)); + private readonly ISpecProvider _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); + private readonly ILogger _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); public bool Validate(BlockHeader header, BlockHeader? parent, bool isUncle) { @@ -149,6 +145,9 @@ public bool ValidateSuggestedBlock(Block block, out string? errorMessage, bool v { return false; } + + if (!ValidateRequests(block, spec, out errorMessage)) + return false; } return true; @@ -178,56 +177,61 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B /// true if the is valid; otherwise, false. public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, Block suggestedBlock, out string? error) { - bool isValid = processedBlock.Header.Hash == suggestedBlock.Header.Hash; - - if (isValid) + if (processedBlock.Header.Hash == suggestedBlock.Header.Hash) { error = null; return true; } + if (_logger.IsWarn) _logger.Warn($"Processed block {processedBlock.ToString(Block.Format.Short)} is invalid:"); if (_logger.IsWarn) _logger.Warn($"- hash: expected {suggestedBlock.Hash}, got {processedBlock.Hash}"); error = null; if (processedBlock.Header.GasUsed != suggestedBlock.Header.GasUsed) { if (_logger.IsWarn) _logger.Warn($"- gas used: expected {suggestedBlock.Header.GasUsed}, got {processedBlock.Header.GasUsed} (diff: {processedBlock.Header.GasUsed - suggestedBlock.Header.GasUsed})"); - error = error ?? BlockErrorMessages.HeaderGasUsedMismatch; + error ??= BlockErrorMessages.HeaderGasUsedMismatch; } if (processedBlock.Header.Bloom != suggestedBlock.Header.Bloom) { if (_logger.IsWarn) _logger.Warn($"- bloom: expected {suggestedBlock.Header.Bloom}, got {processedBlock.Header.Bloom}"); - error = error ?? BlockErrorMessages.InvalidLogsBloom; + error ??= BlockErrorMessages.InvalidLogsBloom; } if (processedBlock.Header.ReceiptsRoot != suggestedBlock.Header.ReceiptsRoot) { if (_logger.IsWarn) _logger.Warn($"- receipts root: expected {suggestedBlock.Header.ReceiptsRoot}, got {processedBlock.Header.ReceiptsRoot}"); - error = error ?? BlockErrorMessages.InvalidReceiptsRoot; + error ??= BlockErrorMessages.InvalidReceiptsRoot; } if (processedBlock.Header.StateRoot != suggestedBlock.Header.StateRoot) { if (_logger.IsWarn) _logger.Warn($"- state root: expected {suggestedBlock.Header.StateRoot}, got {processedBlock.Header.StateRoot}"); - error = error ?? BlockErrorMessages.InvalidStateRoot; + error ??= BlockErrorMessages.InvalidStateRoot; } if (processedBlock.Header.BlobGasUsed != suggestedBlock.Header.BlobGasUsed) { if (_logger.IsWarn) _logger.Warn($"- blob gas used: expected {suggestedBlock.Header.BlobGasUsed}, got {processedBlock.Header.BlobGasUsed}"); - error = error ?? BlockErrorMessages.HeaderBlobGasMismatch; + error ??= BlockErrorMessages.HeaderBlobGasMismatch; } if (processedBlock.Header.ExcessBlobGas != suggestedBlock.Header.ExcessBlobGas) { if (_logger.IsWarn) _logger.Warn($"- excess blob gas: expected {suggestedBlock.Header.ExcessBlobGas}, got {processedBlock.Header.ExcessBlobGas}"); - error = error ?? BlockErrorMessages.IncorrectExcessBlobGas; + error ??= BlockErrorMessages.IncorrectExcessBlobGas; } if (processedBlock.Header.ParentBeaconBlockRoot != suggestedBlock.Header.ParentBeaconBlockRoot) { if (_logger.IsWarn) _logger.Warn($"- parent beacon block root : expected {suggestedBlock.Header.ParentBeaconBlockRoot}, got {processedBlock.Header.ParentBeaconBlockRoot}"); - error = error ?? BlockErrorMessages.InvalidParentBeaconBlockRoot; + error ??= BlockErrorMessages.InvalidParentBeaconBlockRoot; + } + + if (processedBlock.Header.RequestsRoot != suggestedBlock.Header.RequestsRoot) + { + if (_logger.IsWarn) _logger.Warn($"- requests root : expected {suggestedBlock.Header.RequestsRoot}, got {processedBlock.Header.RequestsRoot}"); + error ??= BlockErrorMessages.InvalidRequestsRoot(suggestedBlock.Header.RequestsRoot, processedBlock.Header.RequestsRoot); } for (int i = 0; i < processedBlock.Transactions.Length; i++) @@ -235,14 +239,16 @@ public bool ValidateProcessedBlock(Block processedBlock, TxReceipt[] receipts, B if (receipts[i].Error is not null && receipts[i].GasUsed == 0 && receipts[i].Error == "invalid") { if (_logger.IsWarn) _logger.Warn($"- invalid transaction {i}"); - error = error ?? BlockErrorMessages.InvalidTxInBlock(i); + error ??= BlockErrorMessages.InvalidTxInBlock(i); } } + if (suggestedBlock.ExtraData is not null) { if (_logger.IsWarn) _logger.Warn($"- block extra data : {suggestedBlock.ExtraData.ToHexString()}, UTF8: {Encoding.UTF8.GetString(suggestedBlock.ExtraData)}"); } - return isValid; + + return false; } public bool ValidateWithdrawals(Block block, out string? error) => @@ -284,6 +290,65 @@ private bool ValidateWithdrawals(Block block, IReleaseSpec spec, out string? err return true; } + public bool ValidateRequests(Block block, out string? error) => + ValidateRequests(block, _specProvider.GetSpec(block.Header), out error); + + public bool ValidateRequests(Block block, IReleaseSpec spec, out string? error) + { + if (spec.RequestsEnabled && block.Requests is null) + { + error = BlockErrorMessages.MissingRequests; + + if (_logger.IsWarn) _logger.Warn(error); + + return false; + } + + if (!spec.RequestsEnabled && block.Requests is not null) + { + error = BlockErrorMessages.RequestsNotEnabled; + + if (_logger.IsWarn) _logger.Warn(error); + + return false; + } + + if (!ValidateRequestsHashMatches(block, out Hash256 requestsRoot)) + { + error = BlockErrorMessages.InvalidRequestsRoot(block.Header.RequestsRoot, requestsRoot); + if (_logger.IsWarn) _logger.Warn($"DepositsRoot root hash mismatch in block {block.ToString(Block.Format.FullHashAndNumber)}: expected {block.Header.RequestsRoot}, got {requestsRoot}"); + + return false; + } + + // validate that the requests types are in ascending order + if (!ValidateRequestsOrder(block, out error)) + { + if (_logger.IsWarn) _logger.Warn(error); + return false; + } + + error = null; + return true; + } + + public static bool ValidateRequestsOrder(Block block, out string? error) + { + if (block.Requests is not null) + { + for (int i = 1; i < block.Requests.Length; i++) + { + if (block.Requests[i].Type < block.Requests[i - 1].Type) + { + error = BlockErrorMessages.InvalidRequestsOrder; + return false; + } + } + } + error = null; + return true; + } + private bool ValidateTransactions(Block block, IReleaseSpec spec, out string? errorMessage) { Transaction[] transactions = block.Transactions; @@ -364,46 +429,49 @@ private bool ValidateEip4844Fields(Block block, IReleaseSpec spec, out string? e } public static bool ValidateBodyAgainstHeader(BlockHeader header, BlockBody toBeValidated) => - ValidateTxRootMatchesTxs(header, toBeValidated, out _) && - ValidateUnclesHashMatches(header, toBeValidated, out _) && - ValidateWithdrawalsHashMatches(header, toBeValidated, out _); + ValidateTxRootMatchesTxs(header, toBeValidated, out _) + && ValidateUnclesHashMatches(header, toBeValidated, out _) + && ValidateWithdrawalsHashMatches(header, toBeValidated, out _) + && ValidateRequestsHashMatches(header, toBeValidated, out _); - public static bool ValidateTxRootMatchesTxs(Block block, out Hash256 txRoot) - { - return ValidateTxRootMatchesTxs(block.Header, block.Body, out txRoot); - } - public static bool ValidateTxRootMatchesTxs(BlockHeader header, BlockBody body, out Hash256 txRoot) - { - txRoot = TxTrie.CalculateRoot(body.Transactions); - return txRoot == header.TxRoot; - } + public static bool ValidateTxRootMatchesTxs(Block block, out Hash256 txRoot) => + ValidateTxRootMatchesTxs(block.Header, block.Body, out txRoot); - public static bool ValidateUnclesHashMatches(Block block, out Hash256 unclesHash) - { - return ValidateUnclesHashMatches(block.Header, block.Body, out unclesHash); - } + public static bool ValidateTxRootMatchesTxs(BlockHeader header, BlockBody body, out Hash256 txRoot) => + (txRoot = TxTrie.CalculateRoot(body.Transactions)) == header.TxRoot; - public static bool ValidateUnclesHashMatches(BlockHeader header, BlockBody body, out Hash256 unclesHash) - { - unclesHash = UnclesHash.Calculate(body.Uncles); + public static bool ValidateUnclesHashMatches(Block block, out Hash256 unclesHash) => + ValidateUnclesHashMatches(block.Header, block.Body, out unclesHash); - return header.UnclesHash == unclesHash; - } + public static bool ValidateUnclesHashMatches(BlockHeader header, BlockBody body, out Hash256 unclesHash) => + (unclesHash = UnclesHash.Calculate(body.Uncles)) == header.UnclesHash; - public static bool ValidateWithdrawalsHashMatches(Block block, out Hash256? withdrawalsRoot) - { - return ValidateWithdrawalsHashMatches(block.Header, block.Body, out withdrawalsRoot); - } + public static bool ValidateWithdrawalsHashMatches(Block block, out Hash256? withdrawalsRoot) => + ValidateWithdrawalsHashMatches(block.Header, block.Body, out withdrawalsRoot); public static bool ValidateWithdrawalsHashMatches(BlockHeader header, BlockBody body, out Hash256? withdrawalsRoot) { - withdrawalsRoot = null; if (body.Withdrawals is null) + { + withdrawalsRoot = null; return header.WithdrawalsRoot is null; + } + + return (withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash) == header.WithdrawalsRoot; + } - withdrawalsRoot = new WithdrawalTrie(body.Withdrawals).RootHash; + public static bool ValidateRequestsHashMatches(Block block, out Hash256? requestsRoot) => + ValidateRequestsHashMatches(block.Header, block.Body, out requestsRoot); + + public static bool ValidateRequestsHashMatches(BlockHeader header, BlockBody body, out Hash256? requestsRoot) + { + if (body.Requests == null) + { + requestsRoot = null; + return header.RequestsRoot is null; + } - return header.WithdrawalsRoot == withdrawalsRoot; + return (requestsRoot = new RequestsTrie(body.Requests).RootHash) == header.RequestsRoot; } private static string Invalid(Block block) => diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index 3e8b5f68aaa..b3fdbcdc5c9 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Nethermind.Blockchain; using Nethermind.Blockchain.Blocks; +using Nethermind.Blockchain.BeaconBlockRoot; using Nethermind.Blockchain.Find; using Nethermind.Blockchain.FullPruning; using Nethermind.Blockchain.Headers; @@ -14,10 +15,10 @@ using Nethermind.Blockchain.Synchronization; using Nethermind.Config; using Nethermind.Consensus; -using Nethermind.Consensus.BeaconBlockRoot; using Nethermind.Consensus.Comparers; using Nethermind.Consensus.Processing; using Nethermind.Consensus.Producers; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Transactions; using Nethermind.Consensus.Validators; @@ -37,6 +38,7 @@ using Nethermind.Specs.Test; using Nethermind.State; using Nethermind.State.Repositories; +using Nethermind.Synchronization; using Nethermind.Trie; using Nethermind.Trie.Pruning; using Nethermind.TxPool; @@ -55,7 +57,6 @@ public class TestBlockchain : IDisposable public IDb CodeDb => DbProvider.CodeDb; public IWorldStateManager WorldStateManager { get; set; } = null!; public IBlockProcessor BlockProcessor { get; set; } = null!; - public IBeaconBlockRootHandler BeaconBlockRootHandler { get; set; } = null!; public IBlockchainProcessor BlockchainProcessor { get; set; } = null!; public IBlockPreprocessorStep BlockPreprocessorStep { get; set; } = null!; @@ -109,6 +110,8 @@ protected TestBlockchain() private ReceiptCanonicalityMonitor? _canonicalityMonitor; public IBlockValidator BlockValidator { get; set; } = null!; + + public IBeaconBlockRootHandler BeaconBlockRootHandler { get; set; } = null!; public BuildBlocksWhenRequested BlockProductionTrigger { get; } = new(); public IReadOnlyTrieStore ReadOnlyTrieStore { get; private set; } = null!; @@ -117,6 +120,7 @@ protected TestBlockchain() public ProducedBlockSuggester Suggester { get; protected set; } = null!; + public IConsensusRequestsProcessor? ConsensusRequestsProcessor { get; protected set; } = null!; public ChainLevelInfoRepository ChainLevelInfoRepository { get; protected set; } = null!; public static TransactionBuilder BuildSimpleTransaction => Builders.Build.A.Transaction.SignedAndResolved(TestItem.PrivateKeyA).To(AccountB); @@ -134,7 +138,7 @@ protected virtual async Task Build(ISpecProvider? specProvider = // Eip4788 precompile state account if (specProvider?.GenesisSpec?.IsBeaconBlockRootAvailable ?? false) { - State.CreateAccount(SpecProvider.GenesisSpec.Eip4788ContractAddress, 1); + State.CreateAccount(SpecProvider.GenesisSpec.Eip4788ContractAddress!, 1); } // Eip2935 @@ -193,6 +197,7 @@ protected virtual async Task Build(ISpecProvider? specProvider = HeaderValidator = new HeaderValidator(BlockTree, Always.Valid, SpecProvider, LogManager); _canonicalityMonitor ??= new ReceiptCanonicalityMonitor(ReceiptStorage, LogManager); + BeaconBlockRootHandler = new BeaconBlockRootHandler(TxProcessor, LogManager); BlockValidator = new BlockValidator( new TxValidator(SpecProvider.ChainId), @@ -208,7 +213,7 @@ protected virtual async Task Build(ISpecProvider? specProvider = BloomStorage bloomStorage = new(new BloomConfig(), new MemDb(), new InMemoryDictionaryFileStoreFactory()); ReceiptsRecovery receiptsRecovery = new(new EthereumEcdsa(SpecProvider.ChainId), SpecProvider); LogFinder = new LogFinder(BlockTree, ReceiptStorage, ReceiptStorage, bloomStorage, LimboLogs.Instance, receiptsRecovery); - BeaconBlockRootHandler = new BeaconBlockRootHandler(); + BeaconBlockRootHandler = new BeaconBlockRootHandler(TxProcessor, LogManager); BlockProcessor = CreateBlockProcessor(); BlockchainProcessor chainProcessor = new(BlockTree, BlockProcessor, BlockPreprocessorStep, StateReader, LogManager, Consensus.Processing.BlockchainProcessor.Options.Default); @@ -357,6 +362,12 @@ protected virtual Block GetGenesisBlock() genesisBlockBuilder.WithParentBeaconBlockRoot(Keccak.Zero); } + if (SpecProvider.GenesisSpec.RequestsEnabled) + { + genesisBlockBuilder.WithConsensusRequests(0); + } + + genesisBlockBuilder.WithStateRoot(State.StateRoot); return genesisBlockBuilder.TestObject; } @@ -377,7 +388,9 @@ protected virtual IBlockProcessor CreateBlockProcessor() => State, ReceiptStorage, new BlockhashStore(SpecProvider, State), - LogManager); + TxProcessor, + LogManager, + consensusRequestsProcessor: ConsensusRequestsProcessor); public async Task WaitForNewHead() { diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs index bcd67e3caa4..a43039ef8af 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Crypto; @@ -275,6 +276,28 @@ public BlockBuilder WithWithdrawals(params Withdrawal[]? withdrawals) return this; } + public BlockBuilder WithConsensusRequests(int count) + { + var consensusRequests = new ConsensusRequest[count]; + + for (var i = 0; i < count; i++) + consensusRequests[i] = new(); + + return WithConsensusRequests(consensusRequests); + } + + public BlockBuilder WithConsensusRequests(params ConsensusRequest[]? requests) + { + TestObjectInternal = TestObjectInternal + .WithReplacedBody(TestObjectInternal.Body.WithChangedConsensusRequests(requests)); + + TestObjectInternal.Header.RequestsRoot = requests is null + ? null + : new RequestsTrie(requests).RootHash; + + return this; + } + public BlockBuilder WithParentBeaconBlockRoot(Hash256? parentBeaconBlockRoot) { TestObjectInternal.Header.ParentBeaconBlockRoot = parentBeaconBlockRoot; diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockHeaderBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockHeaderBuilder.cs index 6ce7af4acd1..81d12c7a463 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockHeaderBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockHeaderBuilder.cs @@ -195,4 +195,10 @@ public BlockHeaderBuilder WithParentBeaconBlockRoot(Hash256? parentBeaconBlockRo TestObjectInternal.ParentBeaconBlockRoot = parentBeaconBlockRoot; return this; } + + public BlockHeaderBuilder WithRequestsRoot(Hash256? requestsRoot) + { + TestObjectInternal.RequestsRoot = requestsRoot; + return this; + } } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/Build.Deposit.cs b/src/Nethermind/Nethermind.Core.Test/Builders/Build.Deposit.cs new file mode 100644 index 00000000000..a665ac91bba --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/Build.Deposit.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.Test.Builders; + +public partial class Build +{ + public static DepositBuilder Deposit => new(); +} diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/Build.WithdrawalRequest.cs b/src/Nethermind/Nethermind.Core.Test/Builders/Build.WithdrawalRequest.cs new file mode 100644 index 00000000000..29332f47d5c --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/Build.WithdrawalRequest.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.Test.Builders; + +public partial class Build +{ + public static WithdrawalRequestBuilder WithdrawalRequest => new(); +} diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/ConsolidationRequestBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/ConsolidationRequestBuilder.cs new file mode 100644 index 00000000000..63628ef305b --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/ConsolidationRequestBuilder.cs @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.Test.Builders; +using Nethermind.Core.ConsensusRequests; + +public class ConsolidationRequestBuilder : BuilderBase +{ + public ConsolidationRequestBuilder() => TestObject = new(); + + public ConsolidationRequestBuilder WithSourceAddress(Address sourceAddress) + { + TestObject.SourceAddress = sourceAddress; + + return this; + } + + public ConsolidationRequestBuilder WithSourcePubkey(byte[] SourcePubkey) + { + TestObject.SourcePubkey = SourcePubkey; + + return this; + } + + public ConsolidationRequestBuilder WithTargetPubkey(byte[] TargetPubkey) + { + TestObject.TargetPubkey = TargetPubkey; + + return this; + } + +} diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/DepositBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/DepositBuilder.cs new file mode 100644 index 00000000000..48a06b77f8e --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/DepositBuilder.cs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Core.Test.Builders; + +public class DepositBuilder : BuilderBase +{ + public DepositBuilder() => TestObject = new(); + + public DepositBuilder WithAmount(ulong amount) + { + TestObject.Amount = amount; + + return this; + } + + public DepositBuilder WithIndex(ulong index) + { + TestObject.Index = index; + + return this; + } + + public DepositBuilder WithWithdrawalCredentials(byte[] withdrawalCredentials) + { + TestObject.WithdrawalCredentials = withdrawalCredentials; + + return this; + } + + public DepositBuilder WithSignature(byte[] signature) + { + TestObject.Signature = signature; + + return this; + } + public DepositBuilder WithPublicKey(byte[] pubKey) + { + TestObject.Pubkey = pubKey; + + return this; + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs index a653619f397..ac8cf8e0313 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs @@ -5,6 +5,7 @@ using System.IO; using System.Net; using System.Text.Json; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core.Crypto; using Nethermind.Crypto; using Nethermind.Int256; @@ -99,6 +100,28 @@ public static Hash256 KeccakFromNumber(int i) public static Withdrawal WithdrawalE_5Eth = new() { Address = AddressE, Index = 5, ValidatorIndex = 2005, AmountInGwei = 5_000_000_000 }; public static Withdrawal WithdrawalF_6Eth = new() { Address = AddressF, Index = 6, ValidatorIndex = 2006, AmountInGwei = 6_000_000_000 }; + public static Deposit DepositA_1Eth = new() { Index = 1, Pubkey = PublicKeyA.Bytes, Amount = 1_000_000_000, WithdrawalCredentials = AddressA.Bytes, Signature = KeccakA.Bytes.ToArray() }; + public static Deposit DepositB_2Eth = new() { Index = 2, Pubkey = PublicKeyB.Bytes, Amount = 2_000_000_000, WithdrawalCredentials = AddressB.Bytes, Signature = KeccakB.Bytes.ToArray() }; + public static Deposit DepositC_3Eth = new() { Index = 3, Pubkey = PublicKeyC.Bytes, Amount = 3_000_000_000, WithdrawalCredentials = AddressC.Bytes, Signature = KeccakC.Bytes.ToArray() }; + public static Deposit DepositD_4Eth = new() { Index = 4, Pubkey = PublicKeyD.Bytes, Amount = 4_000_000_000, WithdrawalCredentials = AddressD.Bytes, Signature = KeccakD.Bytes.ToArray() }; + public static Deposit DepositE_5Eth = new() { Index = 5, Pubkey = PublicKeyE.Bytes, Amount = 5_000_000_000, WithdrawalCredentials = AddressE.Bytes, Signature = KeccakE.Bytes.ToArray() }; + public static Deposit DepositF_6Eth = new() { Index = 6, Pubkey = PublicKeyF.Bytes, Amount = 6_000_000_000, WithdrawalCredentials = AddressF.Bytes, Signature = KeccakF.Bytes.ToArray() }; + + + public static WithdrawalRequest WithdrawalRequestA = new() { SourceAddress = AddressA, ValidatorPubkey = PublicKeyA.Bytes }; + public static WithdrawalRequest WithdrawalRequestB = new() { SourceAddress = AddressB, ValidatorPubkey = PublicKeyB.Bytes }; + public static WithdrawalRequest WithdrawalRequestC = new() { SourceAddress = AddressC, ValidatorPubkey = PublicKeyC.Bytes }; + public static WithdrawalRequest WithdrawalRequestD = new() { SourceAddress = AddressD, ValidatorPubkey = PublicKeyD.Bytes }; + public static WithdrawalRequest WithdrawalRequestE = new() { SourceAddress = AddressE, ValidatorPubkey = PublicKeyE.Bytes }; + public static WithdrawalRequest WithdrawalRequestF = new() { SourceAddress = AddressF, ValidatorPubkey = PublicKeyF.Bytes }; + + public static ConsolidationRequest ConsolidationRequestA = new() { SourceAddress = AddressA, SourcePubkey = PublicKeyA.Bytes, TargetPubkey = PublicKeyB.Bytes }; + public static ConsolidationRequest ConsolidationRequestB = new() { SourceAddress = AddressB, SourcePubkey = PublicKeyB.Bytes, TargetPubkey = PublicKeyC.Bytes }; + public static ConsolidationRequest ConsolidationRequestC = new() { SourceAddress = AddressC, SourcePubkey = PublicKeyC.Bytes, TargetPubkey = PublicKeyD.Bytes }; + public static ConsolidationRequest ConsolidationRequestD = new() { SourceAddress = AddressD, SourcePubkey = PublicKeyD.Bytes, TargetPubkey = PublicKeyE.Bytes }; + public static ConsolidationRequest ConsolidationRequestE = new() { SourceAddress = AddressE, SourcePubkey = PublicKeyE.Bytes, TargetPubkey = PublicKeyF.Bytes }; + public static ConsolidationRequest ConsolidationRequestF = new() { SourceAddress = AddressF, SourcePubkey = PublicKeyF.Bytes, TargetPubkey = PublicKeyA.Bytes }; + public static IPEndPoint IPEndPointA = IPEndPoint.Parse("10.0.0.1"); public static IPEndPoint IPEndPointB = IPEndPoint.Parse("10.0.0.2"); public static IPEndPoint IPEndPointC = IPEndPoint.Parse("10.0.0.3"); diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/WithdrawalRequestBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/WithdrawalRequestBuilder.cs new file mode 100644 index 00000000000..4a80e64beca --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Builders/WithdrawalRequestBuilder.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core.Test.Builders; +using Nethermind.Core.ConsensusRequests; + +public class WithdrawalRequestBuilder : BuilderBase +{ + public WithdrawalRequestBuilder() => TestObject = new(); + + + public WithdrawalRequestBuilder WithAmount(ulong amount) + { + TestObject.Amount = amount; + + return this; + } + + public WithdrawalRequestBuilder WithSourceAddress(Address sourceAddress) + { + TestObject.SourceAddress = sourceAddress; + + return this; + } + + public WithdrawalRequestBuilder WithValidatorPubkey(byte[] ValidatorPubkey) + { + TestObject.ValidatorPubkey = ValidatorPubkey; + + return this; + } + +} diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/BlockDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/BlockDecoderTests.cs index 2b9b49dda37..8350699b8fa 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/BlockDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/BlockDecoderTests.cs @@ -3,10 +3,12 @@ using System; using System.IO; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Serialization.Rlp; @@ -41,6 +43,32 @@ public BlockDecoderTests() .TestObject; } + var requests = new ConsensusRequest[8]; + + for (var i = 0; i < requests.Length; i++) + { + if (i % 2 == 0) + { + requests[i] = Build.Deposit + .WithIndex(long.MaxValue) + .WithPublicKey(new byte[] { (byte)i }) + .WithSignature(new byte[] { (byte)i }) + .WithWithdrawalCredentials(new byte[] { (byte)i }) + .WithAmount(int.MaxValue) + .TestObject; + } + else + { + byte[] ValidatorPubkey = new byte[48]; + ValidatorPubkey[11] = 11; + requests[i] = Build.WithdrawalRequest + .WithSourceAddress(TestItem.AddressA) + .WithValidatorPubkey(ValidatorPubkey) + .WithAmount(int.MaxValue) + .TestObject; + } + } + _scenarios = new[] { Build.A.Block.WithNumber(1).TestObject, @@ -86,6 +114,16 @@ public BlockDecoderTests() .WithBlobGasUsed(ulong.MaxValue) .WithExcessBlobGas(ulong.MaxValue) .WithMixHash(Keccak.EmptyTreeHash) + .TestObject, + Build.A.Block.WithNumber(1) + .WithBaseFeePerGas(1) + .WithTransactions(transactions) + .WithUncles(uncles) + .WithWithdrawals(8) + .WithBlobGasUsed(ulong.MaxValue) + .WithExcessBlobGas(ulong.MaxValue) + .WithMixHash(Keccak.EmptyTreeHash) + .WithConsensusRequests(requests) .TestObject }; } diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs new file mode 100644 index 00000000000..7d9b039283e --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs @@ -0,0 +1,214 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using FluentAssertions; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Extensions; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; +using Nethermind.Serialization.Rlp; +using NUnit.Framework; +using Nethermind.Core.Test.Builders; + +namespace Nethermind.Core.Test.Encoding; + +public class ConsensusRequestDecoderTests +{ + [Test] + public void Roundtrip_deposit() + { + ConsensusRequest deposit = new Deposit() + { + Index = long.MaxValue, + Pubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Signature = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + WithdrawalCredentials = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }; + + byte[] rlp = Rlp.Encode(deposit).Bytes; + ConsensusRequest decoded = Rlp.Decode(rlp); + + decoded.Should().BeEquivalentTo(deposit); + } + + [Test] + public void Roundtrip_withdrawalRequest() + { + byte[] ValidatorPubkey = new byte[48]; + ValidatorPubkey[11] = 11; + ConsensusRequest withdrawalRequest = new WithdrawalRequest() + { + SourceAddress = TestItem.AddressA, + ValidatorPubkey = ValidatorPubkey, + Amount = int.MaxValue + }; + + byte[] rlp = Rlp.Encode(withdrawalRequest).Bytes; + ConsensusRequest decoded = Rlp.Decode(rlp); + + decoded.Should().BeEquivalentTo(withdrawalRequest); + } + + [Test] + public void Roundtrip_consolidationRequest() + { + byte[] SourcePubkey = new byte[48]; + SourcePubkey[11] = 11; + byte[] TargetPubkey = new byte[48]; + TargetPubkey[22] = 22; + ConsensusRequest consolidationRequest = new ConsolidationRequest() + { + SourceAddress = TestItem.AddressA, + SourcePubkey = SourcePubkey, + TargetPubkey = TargetPubkey + }; + + byte[] rlp = Rlp.Encode(consolidationRequest).Bytes; + ConsensusRequest decoded = Rlp.Decode(rlp); + + decoded.Should().BeEquivalentTo(consolidationRequest); + } + + [Test] + public void Should_decode_deposit_with_ValueDecoderContext() + { + ConsensusRequest deposit = new Deposit() + { + Index = long.MaxValue, + Pubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Signature = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + WithdrawalCredentials = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }; + RlpStream stream = new(1024); + ConsensusRequestDecoder codec = new(); + + codec.Encode(stream, deposit); + + Rlp.ValueDecoderContext decoderContext = new(stream.Data.AsSpan()); + Deposit? decoded = (Deposit?)codec.Decode(ref decoderContext); + + decoded.Should().BeEquivalentTo(deposit); + } + + [Test] + public void Should_decode_withdrawalRequest_with_ValueDecoderContext() + { + ConsensusRequest withdrawalRequest = new WithdrawalRequest() + { + SourceAddress = TestItem.AddressA, + ValidatorPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }; + RlpStream stream = new(1024); + ConsensusRequestDecoder codec = new(); + + codec.Encode(stream, withdrawalRequest); + + Rlp.ValueDecoderContext decoderContext = new(stream.Data.AsSpan()); + WithdrawalRequest? decoded = (WithdrawalRequest?)codec.Decode(ref decoderContext); + + decoded.Should().BeEquivalentTo(withdrawalRequest); + } + + [Test] + public void Should_decode_consolidationRequest_with_ValueDecoderContext() + { + ConsensusRequest consolidationRequest = new ConsolidationRequest() + { + SourceAddress = TestItem.AddressA, + SourcePubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + TargetPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes() + }; + RlpStream stream = new(1024); + ConsensusRequestDecoder codec = new(); + + codec.Encode(stream, consolidationRequest); + + Rlp.ValueDecoderContext decoderContext = new(stream.Data.AsSpan()); + ConsolidationRequest? decoded = (ConsolidationRequest?)codec.Decode(ref decoderContext); + + decoded.Should().BeEquivalentTo(consolidationRequest); + } + + [Test] + public void Should_encode_deposit_same_for_Rlp_Encode_and_ConsensusRequestDecoder_Encode() + { + ConsensusRequest deposit = new Deposit() + { + Index = long.MaxValue, + Pubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Signature = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + WithdrawalCredentials = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }; + byte[] rlp1 = new ConsensusRequestDecoder().Encode(deposit).Bytes; + byte[] rlp2 = Rlp.Encode(deposit).Bytes; + + rlp1.Should().BeEquivalentTo(rlp2); + } + + [Test] + public void Should_encode_withdrawalRequest_same_for_Rlp_Encode_and_ConsensusRequestDecoder_Encode() + { + ConsensusRequest withdrawalRequest = new WithdrawalRequest() + { + SourceAddress = TestItem.AddressA, + ValidatorPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }; + byte[] rlp1 = new ConsensusRequestDecoder().Encode(withdrawalRequest).Bytes; + byte[] rlp2 = Rlp.Encode(withdrawalRequest).Bytes; + + rlp1.Should().BeEquivalentTo(rlp2); + } + + [Test] + public void Should_encode_consolidationRequest_same_for_Rlp_Encode_and_ConsensusRequestDecoder_Encode() + { + ConsensusRequest consolidationRequest = new ConsolidationRequest() + { + SourceAddress = TestItem.AddressA, + SourcePubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + TargetPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes() + }; + byte[] rlp1 = new ConsensusRequestDecoder().Encode(consolidationRequest).Bytes; + byte[] rlp2 = Rlp.Encode(consolidationRequest).Bytes; + + rlp1.Should().BeEquivalentTo(rlp2); + } + + [Test] + public void Should_encode_ConsensusRequests_Array() + { + ConsensusRequest[] requests = new ConsensusRequest[] + { + new Deposit() + { + Index = long.MaxValue, + Pubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Signature = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + WithdrawalCredentials = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }, + new WithdrawalRequest() + { + SourceAddress = TestItem.AddressA, + ValidatorPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + Amount = int.MaxValue + }, + new ConsolidationRequest() + { + SourceAddress = TestItem.AddressA, + SourcePubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), + TargetPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes() + } + }; + + byte[] rlp = Rlp.Encode(requests).Bytes; + RlpStream rlpStream = new(rlp); + ConsensusRequest[] decoded = Rlp.DecodeArray(rlpStream, new ConsensusRequestDecoder()); + decoded.Should().BeEquivalentTo(requests); + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/HeaderDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/HeaderDecoderTests.cs index 380e9a66f9a..587f3476ccb 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/HeaderDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/HeaderDecoderTests.cs @@ -156,6 +156,42 @@ public void Can_encode_decode_with_cancun_fields(ulong? blobGasUsed, ulong? exce blockHeader.ExcessBlobGas.Should().Be(excessBlobGas); } + [Test] + public void Can_encode_decode_with_WithdrawalRequestRoot() + { + BlockHeader header = Build.A.BlockHeader + .WithTimestamp(ulong.MaxValue) + .WithBaseFee(1) + .WithWithdrawalsRoot(Keccak.Zero) + .WithBlobGasUsed(0) + .WithExcessBlobGas(0) + .WithParentBeaconBlockRoot(TestItem.KeccakB) + .WithRequestsRoot(Keccak.Zero).TestObject; + + Rlp rlp = Rlp.Encode(header); + BlockHeader blockHeader = Rlp.Decode(rlp.Bytes.AsSpan()); + + blockHeader.ParentBeaconBlockRoot.Should().Be(TestItem.KeccakB); + } + + [Test] + public void Can_encode_decode_with_ValidatorExitRoot_equals_to_null() + { + BlockHeader header = Build.A.BlockHeader + .WithTimestamp(ulong.MaxValue) + .WithBaseFee(1) + .WithWithdrawalsRoot(Keccak.Zero) + .WithBlobGasUsed(0) + .WithExcessBlobGas(0) + .WithParentBeaconBlockRoot(TestItem.KeccakB) + .WithRequestsRoot(Keccak.Zero).TestObject; + + Rlp rlp = Rlp.Encode(header); + BlockHeader blockHeader = Rlp.Decode(rlp.Bytes.AsSpan()); + + blockHeader.ParentBeaconBlockRoot.Should().Be(TestItem.KeccakB); + } + public static IEnumerable CancunFieldsSource() { yield return new object?[] { null, null, null }; diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 2cf65535a9c..609455b96d3 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using Nethermind.Core.ConsensusRequests; using System.Text.Json.Serialization; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; @@ -22,19 +23,23 @@ public Block(BlockHeader header, BlockBody body) Body = body ?? throw new ArgumentNullException(nameof(body)); } - public Block( - BlockHeader header, + public Block(BlockHeader header, IEnumerable transactions, IEnumerable uncles, - IEnumerable? withdrawals = null) + IEnumerable? withdrawals = null, + IEnumerable? requests = null) { Header = header ?? throw new ArgumentNullException(nameof(header)); - Body = new(transactions.ToArray(), uncles.ToArray(), withdrawals?.ToArray()); + Body = new(transactions.ToArray(), uncles.ToArray(), withdrawals?.ToArray(), requests?.ToArray()); } public Block(BlockHeader header) : this( header, - new(null, null, header.WithdrawalsRoot is null ? null : Array.Empty()) + new( + null, + null, + header.WithdrawalsRoot is null ? null : Array.Empty(), + header.RequestsRoot is null ? null : Array.Empty()) ) { } @@ -58,7 +63,8 @@ public Transaction[] Transactions public BlockHeader[] Uncles => Body.Uncles; // do not add setter here - public Withdrawal[]? Withdrawals => Body.Withdrawals; + public Withdrawal[]? Withdrawals => Body.Withdrawals; // do not add setter here + public ConsensusRequest[]? Requests => Body.Requests; // do not add setter here public Hash256? Hash => Header.Hash; // do not add setter here @@ -111,6 +117,8 @@ public Transaction[] Transactions public Hash256? WithdrawalsRoot => Header.WithdrawalsRoot; // do not add setter here public Hash256? ParentBeaconBlockRoot => Header.ParentBeaconBlockRoot; // do not add setter here + public Hash256? RequestsRoot => Header.RequestsRoot; // do not add setter here + [JsonIgnore] public ArrayPoolList? AccountChanges { get; set; } [JsonIgnore] diff --git a/src/Nethermind/Nethermind.Core/BlockBody.cs b/src/Nethermind/Nethermind.Core/BlockBody.cs index 76658c6ab8a..26b918ef28f 100644 --- a/src/Nethermind/Nethermind.Core/BlockBody.cs +++ b/src/Nethermind/Nethermind.Core/BlockBody.cs @@ -2,25 +2,28 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using Nethermind.Core.ConsensusRequests; namespace Nethermind.Core { public class BlockBody { - public BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null) + public BlockBody(Transaction[]? transactions, BlockHeader[]? uncles, Withdrawal[]? withdrawals = null, ConsensusRequest[]? requests = null) { Transactions = transactions ?? Array.Empty(); Uncles = uncles ?? Array.Empty(); Withdrawals = withdrawals; + Requests = requests; } public BlockBody() : this(null, null, null) { } - public BlockBody WithChangedTransactions(Transaction[] transactions) => new(transactions, Uncles, Withdrawals); + public BlockBody WithChangedTransactions(Transaction[] transactions) => new(transactions, Uncles, Withdrawals, Requests); - public BlockBody WithChangedUncles(BlockHeader[] uncles) => new(Transactions, uncles, Withdrawals); + public BlockBody WithChangedUncles(BlockHeader[] uncles) => new(Transactions, uncles, Withdrawals, Requests); - public BlockBody WithChangedWithdrawals(Withdrawal[]? withdrawals) => new(Transactions, Uncles, withdrawals); + public BlockBody WithChangedWithdrawals(Withdrawal[]? withdrawals) => new(Transactions, Uncles, withdrawals, Requests); + public BlockBody WithChangedConsensusRequests(ConsensusRequest[]? consensusRequests) => new(Transactions, Uncles, Withdrawals, consensusRequests); public static BlockBody WithOneTransactionOnly(Transaction tx) => new(new[] { tx }, null, null); @@ -29,7 +32,8 @@ public BlockBody() : this(null, null, null) { } public BlockHeader[] Uncles { get; } public Withdrawal[]? Withdrawals { get; } + public ConsensusRequest[]? Requests { get; set; } - public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0; + public bool IsEmpty => Transactions.Length == 0 && Uncles.Length == 0 && (Withdrawals?.Length ?? 0) == 0 && (Requests?.Length ?? 0) == 0; } } diff --git a/src/Nethermind/Nethermind.Core/BlockHeader.cs b/src/Nethermind/Nethermind.Core/BlockHeader.cs index e337ab3c735..1ddd19503b9 100644 --- a/src/Nethermind/Nethermind.Core/BlockHeader.cs +++ b/src/Nethermind/Nethermind.Core/BlockHeader.cs @@ -27,7 +27,8 @@ public BlockHeader( byte[] extraData, ulong? blobGasUsed = null, ulong? excessBlobGas = null, - Hash256? parentBeaconBlockRoot = null) + Hash256? parentBeaconBlockRoot = null, + Hash256? requestsRoot = null) { ParentHash = parentHash; UnclesHash = unclesHash; @@ -38,6 +39,7 @@ public BlockHeader( Timestamp = timestamp; ExtraData = extraData; ParentBeaconBlockRoot = parentBeaconBlockRoot; + RequestsRoot = requestsRoot; BlobGasUsed = blobGasUsed; ExcessBlobGas = excessBlobGas; } @@ -70,11 +72,13 @@ public BlockHeader( public UInt256 BaseFeePerGas { get; set; } public Hash256? WithdrawalsRoot { get; set; } public Hash256? ParentBeaconBlockRoot { get; set; } + public Hash256? RequestsRoot { 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); + || (WithdrawalsRoot is not null && WithdrawalsRoot != Keccak.EmptyTreeHash) + || (RequestsRoot is not null && RequestsRoot != Keccak.EmptyTreeHash); public bool HasTransactions => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash); @@ -115,6 +119,10 @@ public string ToString(string indent) } builder.AppendLine($"{indent}IsPostMerge: {IsPostMerge}"); builder.AppendLine($"{indent}TotalDifficulty: {TotalDifficulty}"); + if (RequestsRoot is not null) + { + builder.AppendLine($"{indent}RequestsRoot: {RequestsRoot}"); + } return builder.ToString(); } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs new file mode 100644 index 00000000000..efd61b189ac --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + + +using System.Text.Json.Serialization; + +namespace Nethermind.Core.ConsensusRequests; + +public enum ConsensusRequestsType : byte +{ + Deposit = 0, + WithdrawalRequest = 1, + ConsolidationRequest = 2 +} + +public class ConsensusRequest +{ + [JsonIgnore] + public ConsensusRequestsType Type { get; protected set; } + + [JsonIgnore] + public ulong AmountField { get; protected set; } + + [JsonIgnore] + public Address? SourceAddressField { get; protected set; } + + [JsonIgnore] + public byte[]? PubKeyField { get; set; } + + [JsonIgnore] + public byte[]? WithdrawalCredentialsField { get; protected set; } + + [JsonIgnore] + public byte[]? SignatureField { get; protected set; } + + [JsonIgnore] + public ulong? IndexField { get; protected set; } +} + +public static class ConsensusRequestExtensions +{ + public static (int depositCount, int withdrawalRequestCount, int consolidationRequestCount) GetTypeCounts(this ConsensusRequest[]? requests) + { + int depositCount = 0; + int withdrawalRequestCount = 0; + int consolidationRequestCount = 0; + int length = requests?.Length ?? 0; + for (int i = 0; i < length; i++) + { + if (requests![i].Type == ConsensusRequestsType.Deposit) + { + depositCount++; + } + else if (requests[i].Type == ConsensusRequestsType.WithdrawalRequest) + { + withdrawalRequestCount++; + } + else + { + consolidationRequestCount++; + } + } + + return (depositCount, withdrawalRequestCount, consolidationRequestCount); + } + + public static (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) SplitRequests(this ConsensusRequest[]? requests) + { + if (requests is null) return (null, null, null); + (int depositCount, int withdrawalRequestCount, int consolidationRequestCount) = requests.GetTypeCounts(); + Deposit[]? deposits = new Deposit[depositCount]; + WithdrawalRequest[]? withdrawalRequests = new WithdrawalRequest[withdrawalRequestCount]; + ConsolidationRequest[]? consolidationRequests = new ConsolidationRequest[consolidationRequestCount]; + int depositIndex = 0; + int withdrawalRequestIndex = 0; + int consolidationRequestIndex = 0; + for (int i = 0; i < requests.Length; i++) + { + if (requests[i].Type == ConsensusRequestsType.Deposit) + { + deposits[depositIndex++] = (Deposit)requests[i]; + } + else if (requests[i].Type == ConsensusRequestsType.WithdrawalRequest) + { + withdrawalRequests[withdrawalRequestIndex++] = (WithdrawalRequest)requests[i]; + } + else + { + consolidationRequests[consolidationRequestIndex++] = (ConsolidationRequest)requests[i]; + } + } + + return (deposits, withdrawalRequests, consolidationRequests); + } +} diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs new file mode 100644 index 00000000000..57a205cab08 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Extensions; +using System.Text; + +namespace Nethermind.Core.ConsensusRequests; + +/// +/// Represents a Deposit that has been validated at the consensus layer. +/// +public class ConsolidationRequest : ConsensusRequest +{ + public ConsolidationRequest() + { + Type = ConsensusRequestsType.ConsolidationRequest; + } + public Address? SourceAddress + { + get { return SourceAddressField; } + set { SourceAddressField = value; } + } + + public byte[]? SourcePubkey + { + get { return PubKeyField; } + set { PubKeyField = value; } + } + + public byte[]? TargetPubkey + { + get { return WithdrawalCredentialsField; } + set { WithdrawalCredentialsField = value; } + } + + public override string ToString() => ToString(string.Empty); + + public string ToString(string indentation) => @$"{indentation}{nameof(ConsolidationRequest)} + {{ {nameof(SourceAddress)}: {SourceAddress}, + {nameof(SourcePubkey)}: {SourcePubkey?.ToHexString()}, + {nameof(TargetPubkey)}: {TargetPubkey?.ToHexString()}, + }}"; + + +} diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs new file mode 100644 index 00000000000..73e3b7d7aec --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Extensions; +using System.Text; + +namespace Nethermind.Core.ConsensusRequests; + +/// +/// Represents a Deposit that has been validated at the consensus layer. +/// +public class Deposit : ConsensusRequest +{ + public Deposit() + { + Type = ConsensusRequestsType.Deposit; + Amount = 0; + } + public byte[]? Pubkey + { + get { return PubKeyField; } + set { PubKeyField = value; } + } + + public byte[]? WithdrawalCredentials + { + get { return WithdrawalCredentialsField; } + set { WithdrawalCredentialsField = value; } + } + + public ulong Amount + { + get { return AmountField; } + set { AmountField = value; } + } + + public byte[]? Signature + { + get { return SignatureField; } + set { SignatureField = value; } + } + public ulong? Index + { + get { return IndexField; } + set { IndexField = value; } + } + public override string ToString() => ToString(string.Empty); + + public string ToString(string indentation) => @$"{indentation}{nameof(Deposit)} + {{{nameof(Index)}: {Index}, + {nameof(WithdrawalCredentials)}: {WithdrawalCredentials?.ToHexString()}, + {nameof(Amount)}: {Amount}, + {nameof(Signature)}: {Signature?.ToHexString()}, + {nameof(Pubkey)}: {Pubkey?.ToHexString()}}}"; + + +} diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs new file mode 100644 index 00000000000..e916dad0590 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Extensions; +using System.Text; + +namespace Nethermind.Core.ConsensusRequests; + +/// +/// Represents a Deposit that has been validated at the consensus layer. +/// +public class WithdrawalRequest : ConsensusRequest +{ + public WithdrawalRequest() + { + Type = ConsensusRequestsType.WithdrawalRequest; + Amount = 0; + } + public Address? SourceAddress + { + get { return SourceAddressField; } + set { SourceAddressField = value; } + } + + public byte[]? ValidatorPubkey + { + get { return PubKeyField; } + set { PubKeyField = value; } + } + + public ulong Amount + { + get { return AmountField; } + set { AmountField = value; } + } + public override string ToString() => ToString(string.Empty); + + public string ToString(string indentation) => @$"{indentation}{nameof(WithdrawalRequest)} + {{{nameof(SourceAddress)}: {SourceAddress}, + {nameof(ValidatorPubkey)}: {ValidatorPubkey?.ToHexString()}, + {nameof(Amount)}: {Amount}}}"; + + +} diff --git a/src/Nethermind/Nethermind.Core/Eip6110Constants.cs b/src/Nethermind/Nethermind.Core/Eip6110Constants.cs new file mode 100644 index 00000000000..6fd77bc88b0 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Eip6110Constants.cs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public static class Eip6110Constants +{ + public static readonly Address MainnetDepositContractAddress = new("0x00000000219ab540356cbb839cbe05303d7705fa"); + +} diff --git a/src/Nethermind/Nethermind.Core/Eip7002Constants.cs b/src/Nethermind/Nethermind.Core/Eip7002Constants.cs new file mode 100644 index 00000000000..b7f1ec1e36f --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Eip7002Constants.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public static class Eip7002Constants +{ + public static readonly Address WithdrawalRequestPredeployAddress = new("0x00A3ca265EBcb825B45F985A16CEFB49958cE017"); +} diff --git a/src/Nethermind/Nethermind.Core/Eip7251Constants.cs b/src/Nethermind/Nethermind.Core/Eip7251Constants.cs new file mode 100644 index 00000000000..4fc30bd2601 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Eip7251Constants.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +namespace Nethermind.Core; + +public static class Eip7251Constants +{ + public static readonly Address ConsolidationRequestPredeployAddress = new("0x00b42dbF2194e931E80326D950320f7d9Dbeac02"); +} diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 5d4f0781189..c9eda1a8f8f 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -203,7 +203,6 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// /// bool IsEip158IgnoredAccount(Address address); - /// /// BaseFee opcode /// @@ -266,7 +265,31 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// Parent Beacon Block precompile /// bool IsEip4788Enabled { get; } - Address Eip4788ContractAddress { get; } + Address? Eip4788ContractAddress { get; } + + + /// + /// EIP-6110: Supply validator deposits on chain + /// + bool IsEip6110Enabled { get; } + bool DepositsEnabled => IsEip6110Enabled; + Address DepositContractAddress { get; } + + /// + /// Execution layer triggerable exits + /// + bool IsEip7002Enabled { get; } + bool WithdrawalRequestsEnabled => IsEip7002Enabled; + Address Eip7002ContractAddress { get; } + + + /// + /// EIP-7251: triggered consolidations + /// + bool IsEip7251Enabled { get; } + bool ConsolidationRequestsEnabled => IsEip7251Enabled; + Address Eip7251ContractAddress { get; } + /// /// Save historical block hashes in state @@ -374,8 +397,26 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec public bool IsBeaconBlockRootAvailable => IsEip4788Enabled; public bool IsBlockHashInStateAvailable => IsEip7709Enabled; public bool MCopyIncluded => IsEip5656Enabled; + + /// + /// AuRaSystemCalls - true + /// GethStyleSystemCalls - false + /// + /// + /// We support two types of system calls in Nethermind: + /// 1. Geth-style: + /// - We don't create a system account if it doesn't exist. + /// - We adhere to geth behavior for State clearing - no state touch for subtraction. + /// - We don't use a custom release spec for those transactions. + /// 2. AuRa (Parity-style): + /// - We create a system account if it doesn't exist. + /// - We use a custom release spec with EIP158 disabled. + /// + public bool AuRaSystemCalls { get; } public bool BlobBaseFeeEnabled => IsEip4844Enabled; bool IsAuthorizationListEnabled => IsEip7702Enabled; + + public bool RequestsEnabled => ConsolidationRequestsEnabled || WithdrawalRequestsEnabled || DepositsEnabled; } } diff --git a/src/Nethermind/Nethermind.Core/TransactionExtensions.cs b/src/Nethermind/Nethermind.Core/TransactionExtensions.cs index 412bce0e3c8..cfb60440ae6 100644 --- a/src/Nethermind/Nethermind.Core/TransactionExtensions.cs +++ b/src/Nethermind/Nethermind.Core/TransactionExtensions.cs @@ -65,7 +65,9 @@ public static bool IsAboveInitCode(this Transaction tx, IReleaseSpec spec) { return tx.IsContractCreation && spec.IsEip3860Enabled && (tx.DataLength) > spec.MaxInitCodeSize; } - - + public static bool IsGethStyleSystemCall(this Transaction tx, IReleaseSpec spec) => + tx.IsSystem() && !spec.AuRaSystemCalls; + public static bool IsAuRaSystemCall(this Transaction tx, IReleaseSpec spec) => + tx.IsSystem() && spec.AuRaSystemCalls; } } diff --git a/src/Nethermind/Nethermind.Core/Withdrawal.cs b/src/Nethermind/Nethermind.Core/Withdrawal.cs index 13741586406..daaaeafb4af 100644 --- a/src/Nethermind/Nethermind.Core/Withdrawal.cs +++ b/src/Nethermind/Nethermind.Core/Withdrawal.cs @@ -45,3 +45,4 @@ public class Withdrawal .Append($"{nameof(AmountInGwei)}: {AmountInGwei}}}") .ToString(); } + diff --git a/src/Nethermind/Nethermind.Db/IPruningConfig.cs b/src/Nethermind/Nethermind.Db/IPruningConfig.cs old mode 100755 new mode 100644 diff --git a/src/Nethermind/Nethermind.Db/PruningConfig.cs b/src/Nethermind/Nethermind.Db/PruningConfig.cs old mode 100755 new mode 100644 diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index 2ab7e98f9e8..4399d6eaca1 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -55,8 +55,9 @@ public void GlobalSetup() codeInfo: new CodeInfo(ByteCode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, new AuthorizedCodeInfoRepository(codeInfoRepository, MainnetSpecProvider.Instance.ChainId)), - inputData: default + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + inputData: default, + isSystemExecutionEnv: false ); _evmState = new EvmState(long.MaxValue, _environment, ExecutionType.TRANSACTION, true, _stateProvider.TakeSnapshot(), false); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index 803443a850e..acc3c85632e 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -87,8 +87,9 @@ public void GlobalSetup() codeInfo: new CodeInfo(_bytecode.Concat(_bytecode).Concat(_bytecode).Concat(_bytecode).ToArray()), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, new AuthorizedCodeInfoRepository(codeInfoRepository, MainnetSpecProvider.Instance.ChainId)), - inputData: default + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, codeInfoRepository), + inputData: default, + isSystemExecutionEnv: false ); _evmState = new EvmState(100_000_000L, _environment, ExecutionType.TRANSACTION, true, _stateProvider.TakeSnapshot(), false); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index 3b7323058d6..0dc55f90381 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -98,8 +98,9 @@ public void GlobalSetup() codeInfo: new CodeInfo(Bytecode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, new AuthorizedCodeInfoRepository(codeInfoRepository, MainnetSpecProvider.Instance.ChainId)), - inputData: default + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, codeInfoRepository), + inputData: default, + isSystemExecutionEnv: false ); _evmState = new EvmState(100_000_000L, _environment, ExecutionType.TRANSACTION, true, _stateProvider.TakeSnapshot(), false); diff --git a/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs b/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs new file mode 100644 index 00000000000..057b2a9cc89 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using FluentAssertions; +using Nethermind.Consensus.Requests; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Db; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Specs; +using Nethermind.State; +using Nethermind.Trie.Pruning; +using NSubstitute; +using NUnit.Framework; + + +namespace Nethermind.Evm.Test; + +public class ConsolidationRequestProcessorTests +{ + + private ISpecProvider _specProvider; + private IEthereumEcdsa _ethereumEcdsa; + private ITransactionProcessor _transactionProcessor; + private IWorldState _stateProvider; + + private ICodeInfoRepository _codeInfoRepository; + + private static readonly UInt256 AccountBalance = 1.Ether(); + + private readonly Address eip7251Account = Eip7251Constants.ConsolidationRequestPredeployAddress; + + [SetUp] + public void Setup() + { + _specProvider = MainnetSpecProvider.Instance; + MemDb stateDb = new(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); + _stateProvider.CreateAccount(eip7251Account, AccountBalance); + _stateProvider.Commit(_specProvider.GenesisSpec); + _stateProvider.CommitTree(0); + + _codeInfoRepository = new CodeInfoRepository(); + + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, _codeInfoRepository, LimboLogs.Instance); + + _transactionProcessor = Substitute.For(); + + _transactionProcessor.Execute(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(ci => + { + CallOutputTracer tracer = ci.Arg(); + tracer.ReturnValue = Bytes.FromHexString("a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000affffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000effffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff"); + return new TransactionResult(); + }); + + _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId, LimboLogs.Instance); + } + + + [Test] + public void ShouldProcessConsolidationRequest() + { + + IReleaseSpec spec = Substitute.For(); + spec.ConsolidationRequestsEnabled.Returns(true); + spec.Eip7251ContractAddress.Returns(eip7251Account); + + Block block = Build.A.Block.TestObject; + + ConsolidationRequestsProcessor ConsolidationRequestsProcessor = new(transactionProcessor: _transactionProcessor); + + var ConsolidationRequest = new ConsolidationRequest() + { + SourceAddress = new Address(Bytes.FromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")), + SourcePubkey = Bytes.FromHexString("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + TargetPubkey = Bytes.FromHexString("0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000") + }; + + var ConsolidationRequests = ConsolidationRequestsProcessor.ReadConsolidationRequests(spec, _stateProvider, block).ToList(); + + Assert.That(ConsolidationRequests, Has.Count.EqualTo(10)); + + ConsolidationRequest ConsolidationRequestResult = ConsolidationRequests[0]; + + ConsolidationRequestResult.Should().BeEquivalentTo(ConsolidationRequest); + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs index 59844dba4c0..7c43f8a4e30 100644 --- a/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs @@ -112,6 +112,15 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase } ).ToArray(); + private static readonly Instruction[] PragueInstructions = + CancunInstructions.Union( + new Instruction[] + { + // Instruction.AUTH, + // Instruction.AUTHCALL + } + ).ToArray(); + private readonly Dictionary _validOpcodes = new() { @@ -127,7 +136,8 @@ private readonly Dictionary _validOpcodes {(ForkActivation)MainnetSpecProvider.LondonBlockNumber, LondonInstructions}, {MainnetSpecProvider.ShanghaiActivation, ShanghaiInstructions}, {MainnetSpecProvider.CancunActivation, CancunInstructions}, - {(long.MaxValue, ulong.MaxValue), CancunInstructions} + {MainnetSpecProvider.PragueActivation, PragueInstructions}, + {(long.MaxValue, ulong.MaxValue), PragueInstructions} }; private const string InvalidOpCodeErrorMessage = "BadInstruction"; diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index e097ec7c147..b93ceb38d31 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -92,6 +92,14 @@ protected GethLikeTxTrace ExecuteAndTrace(long blockNumber, long gasLimit, param return tracer.BuildResult(); } + protected GethLikeTxTrace ExecuteAndTrace(long gasLimit, params byte[] code) + { + GethLikeTxMemoryTracer tracer = new(GethTraceOptions.Default); + (Block block, Transaction transaction) = PrepareTx(Activation, gasLimit, code); + _processor.Execute(transaction, block.Header, tracer); + return tracer.BuildResult(); + } + protected GethLikeTxTrace ExecuteAndTraceToFile(Action dumpCallback, byte[] code, GethTraceOptions options) { GethLikeTxFileTracer tracer = new(dumpCallback, options); diff --git a/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs b/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs new file mode 100644 index 00000000000..b73564c0c8b --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Linq; +using FluentAssertions; +using Nethermind.Consensus.Requests; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Db; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Specs; +using Nethermind.State; +using Nethermind.Trie.Pruning; +using NSubstitute; +using NUnit.Framework; + + +namespace Nethermind.Evm.Test; + +public class WithdrawalRequestProcessorTests +{ + + private ISpecProvider _specProvider; + private IEthereumEcdsa _ethereumEcdsa; + private ITransactionProcessor _transactionProcessor; + private IWorldState _stateProvider; + + private ICodeInfoRepository _codeInfoRepository; + + private static readonly UInt256 AccountBalance = 1.Ether(); + + private readonly Address eip7002Account = Eip7002Constants.WithdrawalRequestPredeployAddress; + + [SetUp] + public void Setup() + { + _specProvider = MainnetSpecProvider.Instance; + MemDb stateDb = new(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); + _stateProvider.CreateAccount(eip7002Account, AccountBalance); + _stateProvider.Commit(_specProvider.GenesisSpec); + _stateProvider.CommitTree(0); + + _codeInfoRepository = new CodeInfoRepository(); + + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, _codeInfoRepository, LimboLogs.Instance); + + _transactionProcessor = Substitute.For(); + + _transactionProcessor.Execute(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(ci => + { + CallOutputTracer tracer = ci.Arg(); + tracer.ReturnValue = Bytes.FromHexString("a94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000070000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000affffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cffffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000effffffffffffffffa94f5374fce5edbc8e2a8697c15331677e6ebf0b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f0000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010ffffffffffffffff"); + return new TransactionResult(); + }); + + _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId, LimboLogs.Instance); + } + + + [Test] + public void ShouldProcessWithdrawalRequest() + { + + IReleaseSpec spec = Substitute.For(); + spec.WithdrawalRequestsEnabled.Returns(true); + spec.Eip7002ContractAddress.Returns(eip7002Account); + + Block block = Build.A.Block.TestObject; + + WithdrawalRequestsProcessor withdrawalRequestsProcessor = new(transactionProcessor: _transactionProcessor); + + var withdrawalRequest = new WithdrawalRequest() + { + SourceAddress = new Address(Bytes.FromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b")), + ValidatorPubkey = Bytes.FromHexString("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), + Amount = 0 + }; + + var withdrawalRequests = withdrawalRequestsProcessor.ReadWithdrawalRequests(spec, _stateProvider, block).ToList(); + + Assert.That(withdrawalRequests, Has.Count.EqualTo(16)); + + WithdrawalRequest withdrawalRequestResult = withdrawalRequests[0]; + + withdrawalRequestResult.Should().BeEquivalentTo(withdrawalRequest); + } +} diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 633974fc4ff..7177fd2d92a 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -168,13 +168,13 @@ public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) return codeInfo; } - public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) + public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec, bool isSystemEnv) { CodeInfo codeInfo = new(code); codeInfo.AnalyseInBackgroundIfRequired(); Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); - state.InsertCode(codeOwner, codeHash, code, spec); + state.InsertCode(codeOwner, codeHash, code, spec, isSystemEnv); _codeCache.Set(codeHash, codeInfo); } diff --git a/src/Nethermind/Nethermind.Evm/EvmStack.cs b/src/Nethermind/Nethermind.Evm/EvmStack.cs index 40875409b4b..0af3abc94d3 100644 --- a/src/Nethermind/Nethermind.Evm/EvmStack.cs +++ b/src/Nethermind/Nethermind.Evm/EvmStack.cs @@ -310,14 +310,9 @@ public readonly Span PeekWord256() return _bytes.Slice(head * WordSize, WordSize); } - public Address PopAddress() + public Address? PopAddress() { - if (Head-- == 0) - { - return null; - } - - return new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); + return Head-- == 0 ? null : new Address(_bytes.Slice(Head * WordSize + WordSize - AddressSize, AddressSize).ToArray()); } public ref byte PopBytesByRef() diff --git a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs index f64570c9430..cb7a163a7ae 100644 --- a/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs +++ b/src/Nethermind/Nethermind.Evm/ExecutionEnvironment.cs @@ -8,17 +8,33 @@ namespace Nethermind.Evm { - public readonly struct ExecutionEnvironment( - CodeInfo codeInfo, - Address executingAccount, - Address caller, - Address? codeSource, - ReadOnlyMemory inputData, - in TxExecutionContext txExecutionContext, - UInt256 transferValue, - UInt256 value, - int callDepth = 0) + public readonly struct ExecutionEnvironment { + public ExecutionEnvironment + ( + CodeInfo codeInfo, + Address executingAccount, + Address caller, + Address? codeSource, + ReadOnlyMemory inputData, + in TxExecutionContext txExecutionContext, + UInt256 transferValue, + UInt256 value, + bool isSystemExecutionEnv, + int callDepth = 0) + { + CodeInfo = codeInfo; + ExecutingAccount = executingAccount; + Caller = caller; + CodeSource = codeSource; + InputData = inputData; + TxExecutionContext = txExecutionContext; + TransferValue = transferValue; + Value = value; + CallDepth = callDepth; + IsSystemEnv = isSystemExecutionEnv; + } + /// /// Parsed bytecode for the current call. /// @@ -62,6 +78,10 @@ public readonly struct ExecutionEnvironment( public readonly UInt256 Value = value; /// If we call TX -> DELEGATECALL -> CALL -> STATICCALL then the call depth would be 3. - public readonly int CallDepth = callDepth; + public readonly int CallDepth; + + /// + /// this field keeps track of wether the execution envirement was initiated by a systemTx. + public readonly bool IsSystemEnv; } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs index 3c575400523..47e1d4c7b44 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs @@ -15,6 +15,6 @@ public interface ICodeInfoRepository { CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec); CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode); - void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec); + void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec, bool isSystemEnv); CodeInsertResult InsertFromAuthorizations(IWorldState worldState, AuthorizationTuple?[] authorizations, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs index 96ac8e27948..9d4622dc862 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/ITransactionProcessor.cs @@ -1,11 +1,42 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.Evm.Tracing; namespace Nethermind.Evm.TransactionProcessing; +[Flags] +public enum ExecutionOptions +{ + /// + /// Just accumulate the state + /// + None = 0, + + /// + /// Commit the state after execution + /// + Commit = 1, + + /// + /// Restore state after execution + /// + Restore = 2, + + /// + /// Skip potential fail checks + /// + NoValidation = Commit | 4, + + /// + /// Commit and later restore state also skip validation, use for CallAndRestore + /// + CommitAndRestore = Commit | Restore | NoValidation +} + public interface ITransactionProcessor { /// diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/SystemTxProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/SystemTxProcessor.cs new file mode 100644 index 00000000000..333ce0dcd88 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/SystemTxProcessor.cs @@ -0,0 +1,528 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Crypto; +using Nethermind.Evm.CodeAnalysis; +using Nethermind.Evm.Tracing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Specs; +using Nethermind.State; +using Nethermind.State.Tracing; +using static Nethermind.Core.Extensions.MemoryExtensions; + +using static Nethermind.Evm.VirtualMachine; + +namespace Nethermind.Evm.TransactionProcessing +{ + public class SystemTxProcessor : ITransactionProcessor + { + protected EthereumEcdsa Ecdsa { get; private init; } + protected ILogger Logger { get; private init; } + protected IReleaseSpec Spec { get; private init; } + protected IWorldState WorldState { get; private init; } + protected IVirtualMachine VirtualMachine { get; private init; } + + private readonly ICodeInfoRepository _codeInfoRepository; + public SystemTxProcessor( + IReleaseSpec? spec, + IWorldState? worldState, + IVirtualMachine? virtualMachine, + ICodeInfoRepository? codeInfoRepository, + EthereumEcdsa? ecdsa, + ILogger? logger) + { + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + Spec = spec ?? throw new ArgumentNullException(nameof(spec)); + WorldState = worldState ?? throw new ArgumentNullException(nameof(worldState)); + VirtualMachine = virtualMachine ?? throw new ArgumentNullException(nameof(virtualMachine)); + _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); + Ecdsa = ecdsa ?? throw new ArgumentNullException(nameof(ecdsa)); + } + public TransactionResult CallAndRestore(Transaction transaction, in BlockExecutionContext blCtx, ITxTracer txTracer) => + Execute(transaction, in blCtx, txTracer, ExecutionOptions.CommitAndRestore); + + public TransactionResult BuildUp(Transaction transaction, in BlockExecutionContext blCtx, ITxTracer txTracer) + { + // we need to treat the result of previous transaction as the original value of next transaction + // when we do not commit + WorldState.TakeSnapshot(true); + return Execute(transaction, in blCtx, txTracer, ExecutionOptions.None); + } + + public TransactionResult Execute(Transaction transaction, in BlockExecutionContext blCtx, ITxTracer txTracer) => + Execute(transaction, in blCtx, txTracer, ExecutionOptions.Commit); + + public TransactionResult Trace(Transaction transaction, in BlockExecutionContext blCtx, ITxTracer txTracer) => + Execute(transaction, in blCtx, txTracer, ExecutionOptions.NoValidation); + + protected virtual void UpdateSpecBasedOnSystemProcessor(BlockExecutionContext blCtx, out BlockHeader header, out IReleaseSpec spec) + { + header = blCtx.Header; + if (Spec.AuRaSystemCalls) + { + spec = new SystemTransactionReleaseSpec(Spec); + } + else + { + spec = Spec; + } + } + + protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionContext blCtx, ITxTracer tracer, ExecutionOptions opts) + { + UpdateSpecBasedOnSystemProcessor(blCtx, out BlockHeader header, out IReleaseSpec spec); + + // 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 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) || !spec.IsEip658Enabled; + + TransactionResult result; + if (!(result = ValidateStatic(tx, header, spec, tracer, opts, out long intrinsicGas))) return result; + + UInt256 effectiveGasPrice = tx.CalculateEffectiveGasPrice(spec.IsEip1559Enabled, header.BaseFeePerGas); + + UpdateMetrics(opts, effectiveGasPrice); + + bool deleteCallerAccount = RecoverSenderIfNeeded(tx, spec, opts, effectiveGasPrice); + + if (!(result = ValidateSender(tx, header, spec, tracer, opts))) return result; + if (!(result = BuyGas(tx, header, spec, tracer, opts, effectiveGasPrice, out UInt256 premiumPerGas, out UInt256 senderReservedGasPayment))) return result; + if (!(result = IncrementNonce(tx, header, spec, tracer, opts))) return result; + + if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance); + + ExecutionEnvironment env = BuildExecutionEnvironment(tx, blCtx, spec, effectiveGasPrice); + + long gasAvailable = tx.GasLimit - intrinsicGas; + ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode); + PayFees(tx, header, spec, tracer, substate, spentGas, premiumPerGas, statusCode); + + // Finalize + if (restore) + { + WorldState.Reset(); + if (deleteCallerAccount) + { + WorldState.DeleteAccount(tx.SenderAddress); + } + else + { + if (!opts.HasFlag(ExecutionOptions.NoValidation)) + WorldState.AddToBalance(tx.SenderAddress, senderReservedGasPayment, spec); + + WorldState.Commit(spec); + } + } + else if (commit) + { + WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullStateTracer.Instance); + } + + if (tracer.IsTracingReceipt) + { + Hash256 stateRoot = null; + if (!spec.IsEip658Enabled) + { + WorldState.RecalculateStateRoot(); + stateRoot = WorldState.StateRoot; + } + + if (statusCode == StatusCode.Failure) + { + byte[] output = (substate?.ShouldRevert ?? false) ? substate.Output.ToArray() : Array.Empty(); + tracer.MarkAsFailed(env.ExecutingAccount, spentGas, output, substate?.Error, stateRoot); + } + else + { + LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : Array.Empty(); + tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.ToArray(), logs, stateRoot); + } + } + + return TransactionResult.Ok; + } + + private static void UpdateMetrics(ExecutionOptions opts, UInt256 effectiveGasPrice) + { + if (opts == ExecutionOptions.Commit || opts == ExecutionOptions.None) + { + float gasPrice = (float)((double)effectiveGasPrice / 1_000_000_000.0); + Metrics.MinGasPrice = Math.Min(gasPrice, Metrics.MinGasPrice); + Metrics.MaxGasPrice = Math.Max(gasPrice, Metrics.MaxGasPrice); + + Metrics.BlockMinGasPrice = Math.Min(gasPrice, Metrics.BlockMinGasPrice); + Metrics.BlockMaxGasPrice = Math.Max(gasPrice, Metrics.BlockMaxGasPrice); + + Metrics.AveGasPrice = (Metrics.AveGasPrice * Metrics.Transactions + gasPrice) / (Metrics.Transactions + 1); + Metrics.EstMedianGasPrice += Metrics.AveGasPrice * 0.01f * float.Sign(gasPrice - Metrics.EstMedianGasPrice); + Metrics.Transactions++; + + Metrics.BlockAveGasPrice = (Metrics.BlockAveGasPrice * Metrics.BlockTransactions + gasPrice) / (Metrics.BlockTransactions + 1); + Metrics.BlockEstMedianGasPrice += Metrics.BlockAveGasPrice * 0.01f * float.Sign(gasPrice - Metrics.BlockEstMedianGasPrice); + Metrics.BlockTransactions++; + } + } + + /// + /// Validates the transaction, in a static manner (i.e. without accesing state/storage). + /// It basically ensures the transaction is well formed (i.e. no null values where not allowed, no overflows, etc). + /// As a part of validating the transaction the premium per gas will be calculated, to save computation this + /// is returned in an out parameter. + /// + /// The transaction to validate + /// The block containing the transaction. Only BaseFee is being used from the block atm. + /// The release spec with which the transaction will be executed + /// The transaction tracer + /// Options (Flags) to use for execution + /// Computed premium per gas + /// + protected virtual TransactionResult ValidateStatic(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts, out long intrinsicGas) + { + intrinsicGas = IntrinsicGasCalculator.Calculate(tx, spec); + + bool validate = !opts.HasFlag(ExecutionOptions.NoValidation); + + if (tx.SenderAddress is null) + { + TraceLogInvalidTx(tx, "SENDER_NOT_SPECIFIED"); + return "sender not specified"; + } + + if (validate && tx.Nonce >= ulong.MaxValue - 1) + { + // we are here if nonce is at least (ulong.MaxValue - 1). If tx is contract creation, + // it is max possible value. Otherwise, (ulong.MaxValue - 1) is allowed, but ulong.MaxValue not. + if (tx.IsContractCreation || tx.Nonce == ulong.MaxValue) + { + TraceLogInvalidTx(tx, "NONCE_OVERFLOW"); + return "nonce overflow"; + } + } + + if (tx.IsAboveInitCode(spec)) + { + TraceLogInvalidTx(tx, $"CREATE_TRANSACTION_SIZE_EXCEEDS_MAX_INIT_CODE_SIZE {tx.DataLength} > {spec.MaxInitCodeSize}"); + return "EIP-3860 - transaction size over max init code size"; + } + + return TransactionResult.Ok; + } + + // TODO Should we remove this already + protected bool RecoverSenderIfNeeded(Transaction tx, IReleaseSpec spec, ExecutionOptions opts, in UInt256 effectiveGasPrice) + { + bool commit = opts.HasFlag(ExecutionOptions.Commit) || !spec.IsEip658Enabled; + bool restore = opts.HasFlag(ExecutionOptions.Restore); + bool noValidation = opts.HasFlag(ExecutionOptions.NoValidation); + + bool deleteCallerAccount = false; + + Address sender = tx.SenderAddress; + if (sender is null || (!WorldState.AccountExists(sender) && !tx.IsGethStyleSystemCall(spec))) + { + if (Logger.IsDebug) Logger.Debug($"TX sender account does not exist {sender} - trying to recover it"); + + // hacky fix for the potential recovery issue + if (tx.Signature is not null) + tx.SenderAddress = Ecdsa.RecoverAddress(tx, !spec.ValidateChainId); + + if (sender != tx.SenderAddress) + { + if (Logger.IsWarn) + Logger.Warn($"TX recovery issue fixed - tx was coming with sender {sender} and the now it recovers to {tx.SenderAddress}"); + sender = tx.SenderAddress; + } + else + { + TraceLogInvalidTx(tx, $"SENDER_ACCOUNT_DOES_NOT_EXIST {sender}"); + if (!commit || noValidation || effectiveGasPrice.IsZero) + { + deleteCallerAccount = !commit || restore; + WorldState.CreateAccount(sender, in UInt256.Zero); + } + } + + if (sender is null) + { + ThrowInvalidDataException($"Failed to recover sender address on tx {tx.Hash} when previously recovered sender account did not exist."); + } + } + + return deleteCallerAccount; + } + + + protected virtual TransactionResult ValidateSender(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts) + { + bool validate = !opts.HasFlag(ExecutionOptions.NoValidation); + + if (validate && WorldState.IsInvalidContractSender(spec, tx.SenderAddress)) + { + TraceLogInvalidTx(tx, "SENDER_IS_CONTRACT"); + return "sender has deployed code"; + } + + return TransactionResult.Ok; + } + + protected virtual TransactionResult BuyGas(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts, + in UInt256 effectiveGasPrice, out UInt256 premiumPerGas, out UInt256 senderReservedGasPayment) + { + premiumPerGas = UInt256.Zero; + senderReservedGasPayment = UInt256.Zero; + bool validate = !opts.HasFlag(ExecutionOptions.NoValidation); + + if (validate) WorldState.SubtractFromBalance(tx.SenderAddress, senderReservedGasPayment, spec); + + return TransactionResult.Ok; + } + + protected virtual TransactionResult IncrementNonce(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts) + { + return TransactionResult.Ok; + } + + protected virtual ExecutionEnvironment BuildExecutionEnvironment( + Transaction tx, + in BlockExecutionContext blCtx, + IReleaseSpec spec, + in UInt256 effectiveGasPrice) + { + Address recipient = tx.GetRecipient(tx.IsContractCreation ? WorldState.GetNonce(tx.SenderAddress) : 0); + if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution"); + + TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes); + + CodeInfo codeInfo = tx.IsContractCreation ? new(tx.Data?.AsArray() ?? Array.Empty()) + : _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); + + byte[] inputData = tx.IsMessageCall ? tx.Data.AsArray() ?? Array.Empty() : Array.Empty(); + + return new ExecutionEnvironment + ( + txExecutionContext: in executionContext, + value: tx.Value, + transferValue: tx.Value, + caller: tx.SenderAddress, + codeSource: recipient, + executingAccount: recipient, + inputData: inputData, + codeInfo: codeInfo, + isSystemExecutionEnv: true + ); + } + + protected void ExecuteEvmCall( + Transaction tx, + BlockHeader header, + IReleaseSpec spec, + ITxTracer tracer, + ExecutionOptions opts, + in long gasAvailable, + in ExecutionEnvironment env, + out TransactionSubstate? substate, + out long spentGas, + out byte statusCode) + { + bool validate = !opts.HasFlag(ExecutionOptions.NoValidation); + + substate = null; + spentGas = tx.GasLimit; + statusCode = StatusCode.Failure; + + long unspentGas = gasAvailable; + + Snapshot snapshot = WorldState.TakeSnapshot(); + + // Fixes eth_estimateGas. + // If sender is SystemUser subtracting value will cause InsufficientBalanceException + if (validate) + WorldState.SubtractFromBalance(tx.SenderAddress, tx.Value, spec, true); + + try + { + if (tx.IsContractCreation) + { + // if transaction is a contract creation then recipient address is the contract deployment address + PrepareAccountForContractDeployment(env.ExecutingAccount, spec); + } + + ExecutionType executionType = tx.IsContractCreation ? ExecutionType.CREATE : ExecutionType.TRANSACTION; + + using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false)) + { + if (spec.UseTxAccessLists) + { + state.WarmUp(tx.AccessList); // eip-2930 + } + + if (spec.UseHotAndColdStorage) + { + state.WarmUp(tx.SenderAddress); // eip-2929 + state.WarmUp(env.ExecutingAccount); // eip-2929 + } + + if (spec.AddCoinbaseToTxAccessList) + { + state.WarmUp(header.GasBeneficiary); + } + + substate = !tracer.IsTracingActions + ? VirtualMachine.Run(state, WorldState, tracer) + : VirtualMachine.Run(state, WorldState, tracer); + + unspentGas = state.GasAvailable; + + if (tracer.IsTracingAccess) + { + tracer.ReportAccess(state.AccessedAddresses, state.AccessedStorageCells); + } + } + + if (substate.ShouldRevert || substate.IsError) + { + if (Logger.IsTrace) + Logger.Trace("Restoring state from before transaction"); + WorldState.Restore(snapshot); + } + else + { + // tks: there is similar code fo contract creation from init and from CREATE + // this may lead to inconsistencies (however it is tested extensively in blockchain tests) + if (tx.IsContractCreation) + { + long codeDepositGasCost = CodeDepositHandler.CalculateCost(substate.Output.Length, spec); + if (unspentGas < codeDepositGasCost && spec.ChargeForTopLevelCreate) + { + ThrowOutOfGasException(); + } + + if (CodeDepositHandler.CodeIsInvalid(spec, substate.Output)) + { + ThrowInvalidCodeException(); + } + + if (unspentGas >= codeDepositGasCost) + { + var code = substate.Output.ToArray(); + _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec, env.IsSystemEnv); + + unspentGas -= codeDepositGasCost; + } + } + + foreach (Address toBeDestroyed in substate.DestroyList) + { + if (Logger.IsTrace) + Logger.Trace($"Destroying account {toBeDestroyed}"); + + WorldState.ClearStorage(toBeDestroyed); + WorldState.DeleteAccount(toBeDestroyed); + + if (tracer.IsTracingRefunds) + tracer.ReportRefund(RefundOf.Destroy(spec.IsEip3529Enabled)); + } + + statusCode = StatusCode.Success; + } + + spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice); + } + catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not + { + if (Logger.IsTrace) Logger.Trace($"EVM EXCEPTION: {ex.GetType().Name}:{ex.Message}"); + WorldState.Restore(snapshot); + } + } + + protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, in TransactionSubstate substate, in long spentGas, in UInt256 premiumPerGas, in byte statusCode) + { + return; + } + + protected void PrepareAccountForContractDeployment(Address contractAddress, IReleaseSpec spec) + { + if (WorldState.AccountExists(contractAddress)) + { + CodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(WorldState, contractAddress, spec); + bool codeIsNotEmpty = codeInfo.MachineCode.Length != 0; + bool accountNonceIsNotZero = WorldState.GetNonce(contractAddress) != 0; + + // TODO: verify what should happen if code info is a precompile + // (but this would generally be a hash collision) + if (codeIsNotEmpty || accountNonceIsNotZero) + { + if (Logger.IsTrace) + { + Logger.Trace($"Contract collision at {contractAddress}"); + } + + ThrowTransactionCollisionException(); + } + + // we clean any existing storage (in case of a previously called self destruct) + WorldState.UpdateStorageRoot(contractAddress, Keccak.EmptyTreeHash); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void TraceLogInvalidTx(Transaction transaction, string reason) + { + if (Logger.IsTrace) Logger.Trace($"Invalid tx {transaction.Hash} ({reason})"); + } + + protected virtual long Refund(Transaction tx, BlockHeader header, IReleaseSpec spec, ExecutionOptions opts, + in TransactionSubstate substate, in long unspentGas, in UInt256 gasPrice) + { + long spentGas = tx.GasLimit; + if (!substate.IsError) + { + spentGas -= unspentGas; + long refund = substate.ShouldRevert + ? 0 + : RefundHelper.CalculateClaimableRefund(spentGas, + substate.Refund + substate.DestroyList.Count * RefundOf.Destroy(spec.IsEip3529Enabled), spec); + + if (Logger.IsTrace) + Logger.Trace("Refunding unused gas of " + unspentGas + " and refund of " + refund); + // If noValidation we didn't charge for gas, so do not refund + if (!opts.HasFlag(ExecutionOptions.NoValidation)) + WorldState.AddToBalance(tx.SenderAddress, (ulong)(unspentGas + refund) * gasPrice, spec, true); + spentGas -= refund; + } + + return spentGas; + } + + [DoesNotReturn] + [StackTraceHidden] + private static void ThrowInvalidDataException(string message) => throw new InvalidDataException(message); + + [DoesNotReturn] + [StackTraceHidden] + private static void ThrowInvalidCodeException() => throw new InvalidCodeException(); + + [DoesNotReturn] + [StackTraceHidden] + private static void ThrowOutOfGasException() => throw new OutOfGasException(); + + [DoesNotReturn] + [StackTraceHidden] + private static void ThrowTransactionCollisionException() => throw new TransactionCollisionException(); + } +} + + diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 0f1b6db5256..c40f5c2137c 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -34,35 +34,6 @@ public class TransactionProcessor : ITransactionProcessor protected IVirtualMachine VirtualMachine { get; private init; } private readonly ICodeInfoRepository _codeInfoRepository; - [Flags] - protected enum ExecutionOptions - { - /// - /// Just accumulate the state - /// - None = 0, - - /// - /// Commit the state after execution - /// - Commit = 1, - - /// - /// Restore state after execution - /// - Restore = 2, - - /// - /// Skip potential fail checks - /// - NoValidation = Commit | 4, - - /// - /// Commit and later restore state also skip validation, use for CallAndRestore - /// - CommitAndRestore = Commit | Restore | NoValidation - } - public TransactionProcessor( ISpecProvider? specProvider, IWorldState? worldState, @@ -103,10 +74,13 @@ public TransactionResult Trace(Transaction transaction, in BlockExecutionContext protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionContext blCtx, ITxTracer tracer, ExecutionOptions opts) { - BlockHeader header = blCtx.Header; - IReleaseSpec spec = SpecProvider.GetSpec(header); + GetSpecFromHeader(blCtx, out BlockHeader header, out IReleaseSpec spec); + if (tx.IsSystem()) - spec = new SystemTransactionReleaseSpec(spec); + { + ITransactionProcessor systemProcessor = new SystemTxProcessor(spec, WorldState, VirtualMachine, _codeInfoRepository, Ecdsa, Logger); + return systemProcessor.Execute(tx, blCtx, tracer); + } // restore is CallAndRestore - previous call, we will restore state after the execution bool restore = opts.HasFlag(ExecutionOptions.Restore); @@ -157,8 +131,8 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon { if (!opts.HasFlag(ExecutionOptions.NoValidation)) WorldState.AddToBalance(tx.SenderAddress, senderReservedGasPayment, spec); - if (!tx.IsSystem()) - WorldState.DecrementNonce(tx.SenderAddress); + + WorldState.DecrementNonce(tx.SenderAddress); WorldState.Commit(spec); } @@ -192,6 +166,12 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon return TransactionResult.Ok; } + private void GetSpecFromHeader(BlockExecutionContext blCtx, out BlockHeader header, out IReleaseSpec spec) + { + header = blCtx.Header; + spec = SpecProvider.GetSpec(header); + } + private static void UpdateMetrics(ExecutionOptions opts, UInt256 effectiveGasPrice) { if (opts is ExecutionOptions.Commit or ExecutionOptions.None && (effectiveGasPrice[2] | effectiveGasPrice[3]) == 0) @@ -256,19 +236,16 @@ protected virtual TransactionResult ValidateStatic(Transaction tx, BlockHeader h return "EIP-3860 - transaction size over max init code size"; } - if (!tx.IsSystem()) + if (tx.GasLimit < intrinsicGas) { - if (tx.GasLimit < intrinsicGas) - { - TraceLogInvalidTx(tx, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {tx.GasLimit} < {intrinsicGas}"); - return "gas limit below intrinsic gas"; - } + TraceLogInvalidTx(tx, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {tx.GasLimit} < {intrinsicGas}"); + return "gas limit below intrinsic gas"; + } - if (validate && tx.GasLimit > header.GasLimit - header.GasUsed) - { - TraceLogInvalidTx(tx, $"BLOCK_GAS_LIMIT_EXCEEDED {tx.GasLimit} > {header.GasLimit} - {header.GasUsed}"); - return "block gas limit exceeded"; - } + if (validate && tx.GasLimit > header.GasLimit - header.GasUsed) + { + TraceLogInvalidTx(tx, $"BLOCK_GAS_LIMIT_EXCEEDED {tx.GasLimit} > {header.GasLimit} - {header.GasUsed}"); + return "block gas limit exceeded"; } return TransactionResult.Ok; } @@ -337,7 +314,7 @@ protected virtual TransactionResult BuyGas(Transaction tx, BlockHeader header, I senderReservedGasPayment = UInt256.Zero; bool validate = !opts.HasFlag(ExecutionOptions.NoValidation); - if (!tx.IsSystem() && validate) + if (validate) { if (!tx.TryCalculatePremiumPerGas(header.BaseFeePerGas, out premiumPerGas)) { @@ -361,10 +338,13 @@ protected virtual TransactionResult BuyGas(Transaction tx, BlockHeader header, I TraceLogInvalidTx(tx, $"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({tx.SenderAddress})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {tx.MaxFeePerGas}"); return "insufficient MaxFeePerGas for sender balance"; } + if (tx.SupportsBlobs) { - overflows = UInt256.MultiplyOverflow(BlobGasCalculator.CalculateBlobGas(tx), (UInt256)tx.MaxFeePerBlobGas, out UInt256 maxBlobGasFee); - if (overflows || UInt256.AddOverflow(maxGasFee, maxBlobGasFee, out UInt256 multidimGasFee) || multidimGasFee > balanceLeft) + overflows = UInt256.MultiplyOverflow(BlobGasCalculator.CalculateBlobGas(tx), + (UInt256)tx.MaxFeePerBlobGas, out UInt256 maxBlobGasFee); + if (overflows || UInt256.AddOverflow(maxGasFee, maxBlobGasFee, out UInt256 multidimGasFee) || + multidimGasFee > balanceLeft) { TraceLogInvalidTx(tx, $"INSUFFICIENT_MAX_FEE_PER_BLOB_GAS_FOR_SENDER_BALANCE: ({tx.SenderAddress})_BALANCE = {senderBalance}"); return "insufficient sender balance"; @@ -372,13 +352,15 @@ protected virtual TransactionResult BuyGas(Transaction tx, BlockHeader header, I } } - overflows = UInt256.MultiplyOverflow((UInt256)tx.GasLimit, effectiveGasPrice, out senderReservedGasPayment); + overflows = UInt256.MultiplyOverflow((UInt256)tx.GasLimit, effectiveGasPrice, + out senderReservedGasPayment); if (!overflows && tx.SupportsBlobs) { overflows = !BlobGasCalculator.TryCalculateBlobGasPrice(header, tx, out UInt256 blobGasFee); if (!overflows) { - overflows = UInt256.AddOverflow(senderReservedGasPayment, blobGasFee, out senderReservedGasPayment); + overflows = UInt256.AddOverflow(senderReservedGasPayment, blobGasFee, + out senderReservedGasPayment); } } @@ -396,8 +378,6 @@ protected virtual TransactionResult BuyGas(Transaction tx, BlockHeader header, I protected virtual TransactionResult IncrementNonce(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts) { - if (tx.IsSystem()) return TransactionResult.Ok; - if (tx.Nonce != WorldState.GetNonce(tx.SenderAddress)) { TraceLogInvalidTx(tx, $"WRONG_TRANSACTION_NONCE: {tx.Nonce} (expected {WorldState.GetNonce(tx.SenderAddress)})"); @@ -437,7 +417,8 @@ protected ExecutionEnvironment BuildExecutionEnvironment( codeSource: recipient, executingAccount: recipient, inputData: inputData, - codeInfo: codeInfo + codeInfo: codeInfo, + isSystemExecutionEnv: false ); } @@ -466,8 +447,7 @@ protected void ExecuteEvmCall( // Fixes eth_estimateGas. // If sender is SystemUser subtracting value will cause InsufficientBalanceException - if (validate || !tx.IsSystem()) - WorldState.SubtractFromBalance(tx.SenderAddress, tx.Value, spec); + WorldState.SubtractFromBalance(tx.SenderAddress, tx.Value, spec); try { @@ -521,7 +501,7 @@ protected void ExecuteEvmCall( if (unspentGas >= codeDepositGasCost) { var code = substate.Output.ToArray(); - _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec); + _codeInfoRepository.InsertCode(WorldState, code, env.ExecutingAccount, spec, env.IsSystemEnv); unspentGas -= codeDepositGasCost; } @@ -550,7 +530,7 @@ protected void ExecuteEvmCall( WorldState.Restore(snapshot); } - if (validate && !tx.IsSystem()) + if (validate) header.GasUsed += spentGas; } @@ -583,22 +563,19 @@ private void WarmUp(Transaction tx, BlockHeader header, IReleaseSpec spec, Execu protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, in TransactionSubstate substate, in long spentGas, in UInt256 premiumPerGas, in byte statusCode) { - if (!tx.IsSystem()) + bool gasBeneficiaryNotDestroyed = substate?.DestroyList.Contains(header.GasBeneficiary) != true; + if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) { - bool gasBeneficiaryNotDestroyed = substate?.DestroyList.Contains(header.GasBeneficiary) != true; - if (statusCode == StatusCode.Failure || gasBeneficiaryNotDestroyed) - { - UInt256 fees = (UInt256)spentGas * premiumPerGas; - UInt256 burntFees = !tx.IsFree() ? (UInt256)spentGas * header.BaseFeePerGas : 0; + UInt256 fees = (UInt256)spentGas * premiumPerGas; + UInt256 burntFees = !tx.IsFree() ? (UInt256)spentGas * header.BaseFeePerGas : 0; - WorldState.AddToBalanceAndCreateIfNotExists(header.GasBeneficiary, fees, spec); + WorldState.AddToBalanceAndCreateIfNotExists(header.GasBeneficiary, fees, spec); - if (spec.IsEip1559Enabled && spec.Eip1559FeeCollector is not null && !burntFees.IsZero) - WorldState.AddToBalanceAndCreateIfNotExists(spec.Eip1559FeeCollector, burntFees, spec); + if (spec.IsEip1559Enabled && spec.Eip1559FeeCollector is not null && !burntFees.IsZero) + WorldState.AddToBalanceAndCreateIfNotExists(spec.Eip1559FeeCollector, burntFees, spec); - if (tracer.IsTracingFees) - tracer.ReportFees(fees, burntFees); - } + if (tracer.IsTracingFees) + tracer.ReportFees(fees, burntFees); } } @@ -658,18 +635,4 @@ protected virtual long Refund(Transaction tx, BlockHeader header, IReleaseSpec s [StackTraceHidden] private static void ThrowTransactionCollisionException() => throw new TransactionCollisionException(); } - - public readonly struct TransactionResult(string? error) - { - public static readonly TransactionResult Ok = new(); - public static readonly TransactionResult MalformedTransaction = new("malformed"); - [MemberNotNullWhen(true, nameof(Fail))] - [MemberNotNullWhen(false, nameof(Success))] - public string? Error { get; } = error; - public bool Fail => Error is not null; - public bool Success => Error is null; - public static implicit operator TransactionResult(string? error) => new(error); - public static implicit operator bool(TransactionResult result) => result.Success; - public override string ToString() => Error is not null ? $"Fail : {Error}" : "Success"; - } } diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionResult.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionResult.cs new file mode 100644 index 00000000000..b23f5706214 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionResult.cs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Evm.TransactionProcessing; +public readonly struct TransactionResult(string? error) +{ + public static readonly TransactionResult Ok = new(); + public static readonly TransactionResult MalformedTransaction = new("malformed"); + [MemberNotNullWhen(true, nameof(Fail))] + [MemberNotNullWhen(false, nameof(Success))] + public string? Error { get; } = error; + public bool Fail => Error is not null; + public bool Success => Error is null; + public static implicit operator TransactionResult(string? error) => new(error); + public static implicit operator bool(TransactionResult result) => result.Success; +} diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 64dd3236890..7a04e5d670a 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -29,6 +29,7 @@ namespace Nethermind.Evm; using System.Linq; using Int256; + public class VirtualMachine : IVirtualMachine { public const int MaxCallDepth = 1024; @@ -235,7 +236,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (typeof(TTracingActions) == typeof(IsTracing)) _txTracer.ReportActionError(callResult.ExceptionType); _state.Restore(currentState.Snapshot); - RevertParityTouchBugAccount(spec); + RevertParityTouchBugAccount(spec, state.Env.IsSystemEnv); if (currentState.IsTopLevel) { @@ -335,7 +336,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl if (gasAvailableForCodeDeposit >= codeDepositGasCost && !invalidCode) { ReadOnlyMemory code = callResult.Output; - codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec); + codeInfoRepository.InsertCode(_state, code, callCodeOwner, spec, state.Env.IsSystemEnv); currentState.GasAvailable -= codeDepositGasCost; @@ -413,7 +414,7 @@ public TransactionSubstate Run(EvmState state, IWorldState worl _state.Restore(currentState.Snapshot); - RevertParityTouchBugAccount(spec); + RevertParityTouchBugAccount(spec, state.Env.IsSystemEnv); if (txTracer.IsTracingInstructions) { @@ -444,13 +445,13 @@ public TransactionSubstate Run(EvmState state, IWorldState worl } } - private void RevertParityTouchBugAccount(IReleaseSpec spec) + private void RevertParityTouchBugAccount(IReleaseSpec spec, bool isSystemEnv) { if (_parityTouchBugAccount.ShouldDelete) { if (_state.AccountExists(_parityTouchBugAccount.Address)) { - _state.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, spec); + _state.AddToBalance(_parityTouchBugAccount.Address, UInt256.Zero, spec, isSystemEnv); } _parityTouchBugAccount.ShouldDelete = false; @@ -555,7 +556,7 @@ private CallResult ExecutePrecompile(EvmState state, IReleaseSpec spec) } else { - _state.AddToBalance(state.Env.ExecutingAccount, transferValue, spec); + _state.AddToBalance(state.Env.ExecutingAccount, transferValue, spec, state.Env.IsSystemEnv); } // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-161.md @@ -619,7 +620,7 @@ private CallResult ExecuteCall(EvmState vmState, ReadOnlyM } else { - _state.AddToBalance(env.ExecutingAccount, env.TransferValue, spec); + _state.AddToBalance(env.ExecutingAccount, env.TransferValue, spec, vmState.Env.IsSystemEnv); } if (vmState.ExecutionType.IsAnyCreate() && spec.ClearEmptyAccountWhenTouched) @@ -2081,6 +2082,7 @@ private EvmExceptionType InstructionCall( if (vmState.IsStatic && !transferValue.IsZero && instruction != Instruction.CALLCODE) return EvmExceptionType.StaticCallViolation; Address caller = instruction == Instruction.DELEGATECALL ? env.Caller : env.ExecutingAccount; + Address target = instruction == Instruction.CALL || instruction == Instruction.STATICCALL ? codeSource : env.ExecutingAccount; @@ -2130,8 +2132,7 @@ private EvmExceptionType InstructionCall( gasLimitUl += GasCostOf.CallStipend; } - if (env.CallDepth >= MaxCallDepth || - !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) + if (env.CallDepth >= MaxCallDepth || !transferValue.IsZero && _state.GetBalance(env.ExecutingAccount) < transferValue) { _returnDataBuffer = Array.Empty(); stack.PushZero(); @@ -2153,7 +2154,7 @@ private EvmExceptionType InstructionCall( } Snapshot snapshot = _state.TakeSnapshot(); - _state.SubtractFromBalance(caller, transferValue, spec); + _state.SubtractFromBalance(caller, transferValue, spec, env.IsSystemEnv); if (codeInfo.IsEmpty && typeof(TTracingInstructions) != typeof(IsTracing) && !_txTracer.IsTracingActions) { @@ -2175,7 +2176,8 @@ private EvmExceptionType InstructionCall( transferValue: transferValue, value: callValue, inputData: callData, - codeInfo: codeInfo + codeInfo: codeInfo, + isSystemExecutionEnv: env.IsSystemEnv ); if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Tx call gas {gasLimitUl}"); if (outputLength == 0) @@ -2301,13 +2303,13 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref } else if (!inheritor.Equals(executingAccount)) { - _state.AddToBalance(inheritor, result, spec); + _state.AddToBalance(inheritor, result, spec, vmState.Env.IsSystemEnv); } if (spec.SelfdestructOnlyOnSameTransaction && !createInSameTx && inheritor.Equals(executingAccount)) return EvmExceptionType.None; // don't burn eth when contract is not destroyed per EIP clarification - _state.SubtractFromBalance(executingAccount, result, spec); + _state.SubtractFromBalance(executingAccount, result, spec, vmState.Env.IsSystemEnv); return EvmExceptionType.None; } @@ -2414,7 +2416,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref _state.ClearStorage(contractAddress); } - _state.SubtractFromBalance(env.ExecutingAccount, value, spec); + _state.SubtractFromBalance(env.ExecutingAccount, value, spec, env.IsSystemEnv); // Do not add the initCode to the cache as it is // pointing to data in this tx and will become invalid @@ -2432,7 +2434,8 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref codeInfo: codeInfo, inputData: default, transferValue: value, - value: value + value: value, + isSystemExecutionEnv: env.IsSystemEnv ); EvmState callState = new( callGas, @@ -2748,30 +2751,13 @@ private void EndInstructionTraceError(long gasAvailable, EvmExceptionType evmExc _txTracer.ReportOperationError(evmExceptionType); } - private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) - { - ExecutionType executionType; - if (instruction == Instruction.CALL) + private static ExecutionType GetCallExecutionType(Instruction instruction, bool isPostMerge = false) => + instruction switch { - executionType = ExecutionType.CALL; - } - else if (instruction == Instruction.DELEGATECALL) - { - executionType = ExecutionType.DELEGATECALL; - } - else if (instruction == Instruction.STATICCALL) - { - executionType = ExecutionType.STATICCALL; - } - else if (instruction == Instruction.CALLCODE) - { - executionType = ExecutionType.CALLCODE; - } - else - { - throw new NotSupportedException($"Execution type is undefined for {instruction.GetName(isPostMerge)}"); - } - - return executionType; - } + Instruction.CALL => ExecutionType.CALL, + Instruction.DELEGATECALL => ExecutionType.DELEGATECALL, + Instruction.STATICCALL => ExecutionType.STATICCALL, + Instruction.CALLCODE => ExecutionType.CALLCODE, + _ => throw new NotSupportedException($"Execution type is undefined for {instruction.GetName(isPostMerge)}") + }; } diff --git a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs index dc0925282ad..a40d330386a 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/BlockForRpc.cs @@ -12,6 +12,7 @@ using Nethermind.Serialization.Rlp; using System.Text.Json.Serialization; using System.Runtime.CompilerServices; +using Nethermind.Core.ConsensusRequests; namespace Nethermind.Facade.Eth; @@ -82,6 +83,8 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s Uncles = block.Uncles.Select(o => o.Hash); Withdrawals = block.Withdrawals; WithdrawalsRoot = block.Header.WithdrawalsRoot; + Requests = block.Requests; + RequestsRoot = block.Header.RequestsRoot; } public Address Author { get; set; } @@ -139,4 +142,8 @@ public BlockForRpc(Block block, bool includeFullTransactionData, ISpecProvider s [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public Hash256? ParentBeaconBlockRoot { get; set; } + public IEnumerable? Requests { get; set; } + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public Hash256? RequestsRoot { get; set; } } diff --git a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs index 718d51d7395..7dbcf7c23ab 100644 --- a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs @@ -23,8 +23,8 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) => codeInfoRepository.GetOrAdd(codeHash, initCode); - public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) => - codeInfoRepository.InsertCode(state, code, codeOwner, spec); + public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec, bool isSystemEnv) => + codeInfoRepository.InsertCode(state, code, codeOwner, spec, isSystemEnv); public void SetCodeOverwrite( IWorldState worldState, diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs index 447aa8855dd..26a538c8294 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateReadOnlyBlocksProcessingEnv.cs @@ -112,5 +112,6 @@ public IBlockProcessor GetProcessor(bool validate) => StateProvider, NullReceiptStorage.Instance, new BlockhashStore(SpecProvider, StateProvider), + _transactionProcessor, _logManager); } diff --git a/src/Nethermind/Nethermind.Init/InitializeStateDb.cs b/src/Nethermind/Nethermind.Init/InitializeStateDb.cs index 92a230e99b1..8c574eaf38c 100644 --- a/src/Nethermind/Nethermind.Init/InitializeStateDb.cs +++ b/src/Nethermind/Nethermind.Init/InitializeStateDb.cs @@ -86,6 +86,7 @@ public Task Execute(CancellationToken cancellationToken) syncConfig.DownloadBodiesInFastSync = true; } + _api.NodeStorageFactory.DetectCurrentKeySchemeFrom(getApi.DbProvider.StateDb); IKeyValueStore codeDb = getApi.DbProvider.CodeDb; IKeyValueStoreWithBatching stateDb = getApi.DbProvider.StateDb; IPersistenceStrategy persistenceStrategy; diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index e3b3628f4e7..4cc16575599 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -238,6 +238,7 @@ protected virtual BlockProcessor CreateBlockProcessor(BlockCachePreWarmer? preWa worldState, _api.ReceiptStorage, new BlockhashStore(_api.SpecProvider!, worldState), + _api.TransactionProcessor, _api.LogManager, preWarmer: preWarmer ); diff --git a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs index 1eaddd43c83..f82c8ee6540 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Benchmark/EthModuleBenchmarks.cs @@ -95,7 +95,7 @@ TransactionProcessor transactionProcessor IBlockProcessor.IBlockTransactionsExecutor transactionsExecutor = new BlockProcessor.BlockValidationTransactionsExecutor(transactionProcessor, stateProvider); BlockProcessor blockProcessor = new(specProvider, Always.Valid, new RewardCalculator(specProvider), transactionsExecutor, - stateProvider, NullReceiptStorage.Instance, new BlockhashStore(specProvider, stateProvider), LimboLogs.Instance); + stateProvider, NullReceiptStorage.Instance, new BlockhashStore(specProvider, stateProvider), transactionProcessor, LimboLogs.Instance); EthereumEcdsa ecdsa = new(specProvider.ChainId, LimboLogs.Instance); BlockchainProcessor blockchainProcessor = new( diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs index 7f74b75145a..9d5fea089a8 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Proof/ProofRpcModuleTests.cs @@ -362,7 +362,7 @@ public async Task Can_call_with_storage_load() .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -375,7 +375,7 @@ public async Task Can_call_with_many_storage_loads() .Op(Instruction.SLOAD) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -388,7 +388,7 @@ public async Task Can_call_with_storage_write() .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -402,7 +402,7 @@ public async Task Can_call_with_extcodecopy() .Op(Instruction.EXTCODECOPY) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -416,7 +416,7 @@ public async Task Can_call_with_extcodecopy_to_system_account() .Op(Instruction.EXTCODECOPY) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2)); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -427,7 +427,7 @@ public async Task Can_call_with_extcodesize() .Op(Instruction.EXTCODESIZE) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -438,7 +438,7 @@ public async Task Can_call_with_extcodesize_to_system_account() .Op(Instruction.EXTCODESIZE) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2)); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -450,7 +450,7 @@ public async Task Can_call_with_extcodehash() .Op(Instruction.EXTCODEHASH) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -462,7 +462,7 @@ public async Task Can_call_with_extcodehash_to_system_account() .Op(Instruction.EXTCODEHASH) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2)); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -473,7 +473,7 @@ public async Task Can_call_with_just_basic_addresses() .Op(Instruction.STOP) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -486,7 +486,7 @@ public async Task Can_call_with_balance() .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -498,7 +498,7 @@ public async Task Can_call_with_self_balance() .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -510,7 +510,7 @@ public async Task Can_call_with_balance_of_system_account() .Op(Instruction.BALANCE) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2)); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -562,7 +562,7 @@ public async Task Can_call_with_delegate_call_to_system_account() .Op(Instruction.DELEGATECALL) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2)); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -598,7 +598,7 @@ public async Task Can_call_with_call_with_zero_value() .Op(Instruction.CALL) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -615,7 +615,7 @@ public async Task Can_call_with_static_call() .Op(Instruction.STATICCALL) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -632,7 +632,7 @@ public async Task Can_call_with_delegate_call() .Op(Instruction.DELEGATECALL) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(_createSystemAccount && _useNonZeroGasPrice ? 3 : 2)); + Assert.That(result.Accounts.Length, Is.EqualTo(_createSystemAccount ? 3 : 2)); } [TestCase] @@ -650,7 +650,7 @@ public async Task Can_call_with_call_with_non_zero_value() .Op(Instruction.CALL) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -663,7 +663,7 @@ public async Task Can_call_with_self_destruct() .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(2 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -691,7 +691,7 @@ public async Task Can_call_with_many_storage_writes() .Op(Instruction.SSTORE) .Done; CallResultWithProof result = await TestCallWithCode(code); - Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_useNonZeroGasPrice ? 1 : 0))); + Assert.That(result.Accounts.Length, Is.EqualTo(1 + (_createSystemAccount ? 1 : 0))); } [TestCase] @@ -909,7 +909,7 @@ private void AddAccount(WorldState stateProvider, Address account, UInt256 initi private void AddCode(WorldState stateProvider, Address account, byte[] code) { - stateProvider.InsertCode(account, code, MuirGlacier.Instance); + stateProvider.InsertCode(account, code, MuirGlacier.Instance, false); stateProvider.Commit(MainnetSpecProvider.Instance.GenesisSpec, NullStateTracer.Instance); } } diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs index 9df80dcb638..c8b507d6412 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Trace/ParityStyleTracerTests.cs @@ -76,6 +76,7 @@ public void Setup() stateProvider, NullReceiptStorage.Instance, new BlockhashStore(specProvider, stateProvider), + transactionProcessor, LimboLogs.Instance); RecoverSignatures txRecovery = new(new EthereumEcdsa(TestBlockchainIds.ChainId), NullTxPool.Instance, specProvider, LimboLogs.Instance); diff --git a/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs b/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs index cb62bfc5cb8..9fa1e0c60b4 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs @@ -14,6 +14,7 @@ using Nethermind.Consensus.Comparers; using Nethermind.Consensus.Processing; using Nethermind.Consensus.Producers; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -42,8 +43,9 @@ public class AuRaMergeEngineModuleTests : EngineModuleTests protected override MergeTestBlockchain CreateBaseBlockchain( IMergeConfig? mergeConfig = null, IPayloadPreparationService? mockedPayloadService = null, - ILogManager? logManager = null) - => new MergeAuRaTestBlockchain(mergeConfig, mockedPayloadService); + ILogManager? logManager = null, + IConsensusRequestsProcessor? mockedConsensusRequestsProcessor = null) + => new MergeAuRaTestBlockchain(mergeConfig, mockedPayloadService, logManager, mockedConsensusRequestsProcessor); protected override Hash256 ExpectedBlockHash => new("0x990d377b67dbffee4a60db6f189ae479ffb406e8abea16af55e0469b8524cf46"); @@ -56,10 +58,18 @@ int ErrorCode ) input) => base.forkchoiceUpdatedV2_should_validate_withdrawals(input); + [TestCase( + "0xe97d919a17fa5011ff3a08ffb07657ed9e1aaf5ff649888e5d7f605006caf598", + "0x91675165328fd9a5f25375eb1a468b65b927a1415afd18ff51eeef287ca71fae", + "0x692ba034d9dc8c4c2d7d172a2fb1f3773f8a250fde26501b99d2733a2b48e70b", + "0x3c6a8926870bdeff")] + public override Task Should_process_block_as_expected_V4(string latestValidHash, string blockHash, string stateRoot, string payloadId) + => base.Should_process_block_as_expected_V4(latestValidHash, blockHash, stateRoot, payloadId); + [TestCase( "0xe168b70ac8a6f7d90734010030801fbb2dcce03a657155c4024b36ba8d1e3926", - "0x3e604e45a9a74b66a7e03f828cc2597f0cb5f5e7dc50c9211be3a62fbcd6396d", - "0xdbd87b98a6be7d4e3f11ff8500c38a0736d9a5e7a47b5cb25628d37187a98cb9", + "0x310f4c8c949eb758faa4497e293bb18ef27a465af16553e5fc03752d3be19cc3", + "0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f", "0xcdd08163eccae523")] public override Task Should_process_block_as_expected_V2(string latestValidHash, string blockHash, string stateRoot, string payloadId) => base.Should_process_block_as_expected_V2(latestValidHash, blockHash, stateRoot, payloadId); @@ -91,9 +101,10 @@ class MergeAuRaTestBlockchain : MergeTestBlockchain { private AuRaNethermindApi? _api; - public MergeAuRaTestBlockchain(IMergeConfig? mergeConfig = null, IPayloadPreparationService? mockedPayloadPreparationService = null) - : base(mergeConfig, mockedPayloadPreparationService) + public MergeAuRaTestBlockchain(IMergeConfig? mergeConfig = null, IPayloadPreparationService? mockedPayloadPreparationService = null, ILogManager? logManager = null, IConsensusRequestsProcessor? mockedConsensusRequestsProcessor = null) + : base(mergeConfig, mockedPayloadPreparationService, logManager, mockedConsensusRequestsProcessor) { + ConsensusRequestsProcessor = mockedConsensusRequestsProcessor; SealEngineType = Core.SealEngineType.AuRa; } @@ -132,8 +143,10 @@ protected override IBlockProcessor CreateBlockProcessor() State, ReceiptStorage, new BlockhashStore(SpecProvider, State), + TxProcessor, LogManager, - WithdrawalProcessor); + WithdrawalProcessor, + consensusRequestsProcessor: ConsensusRequestsProcessor); return new TestBlockProcessorInterceptor(processor, _blockProcessingThrottle); } @@ -167,7 +180,8 @@ protected override IBlockProducer CreateTestBlockProducer(TxPoolTxSource txPoolT TxPool, transactionComparerProvider, blocksConfig, - LogManager); + LogManager, + ConsensusRequestsProcessor); BlockProducerEnv blockProducerEnv = blockProducerEnvFactory.Create(); diff --git a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProcessor.cs b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProcessor.cs index 1a55b725abd..21890e4e1f6 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProcessor.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProcessor.cs @@ -5,6 +5,7 @@ using Nethermind.Blockchain.Receipts; using Nethermind.Consensus.AuRa; using Nethermind.Consensus.AuRa.Validators; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Processing; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Transactions; @@ -13,6 +14,7 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; using Nethermind.State; @@ -20,8 +22,7 @@ namespace Nethermind.Merge.AuRa; public class AuRaMergeBlockProcessor : AuRaBlockProcessor { - public AuRaMergeBlockProcessor( - ISpecProvider specProvider, + public AuRaMergeBlockProcessor(ISpecProvider specProvider, IBlockValidator blockValidator, IRewardCalculator rewardCalculator, IBlockProcessor.IBlockTransactionsExecutor blockTransactionsExecutor, @@ -30,11 +31,13 @@ public AuRaMergeBlockProcessor( ILogManager logManager, IBlockTree blockTree, IWithdrawalProcessor withdrawalProcessor, - IAuRaValidator? validator, + ITransactionProcessor transactionProcessor, + IAuRaValidator? validator = null, ITxFilter? txFilter = null, AuRaContractGasLimitOverride? gasLimitOverride = null, ContractRewriter? contractRewriter = null, - IBlockCachePreWarmer? preWarmer = null + IBlockCachePreWarmer? preWarmer = null, + IConsensusRequestsProcessor? consensusRequestsProcessor = null ) : base( specProvider, blockValidator, @@ -45,11 +48,13 @@ public AuRaMergeBlockProcessor( logManager, blockTree, withdrawalProcessor, + transactionProcessor, validator, txFilter, gasLimitOverride, contractRewriter, - preWarmer + preWarmer, + consensusRequestsProcessor ) { } diff --git a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProducerEnvFactory.cs index f91dac19352..128b6a6098c 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/AuRaMergeBlockProducerEnvFactory.cs @@ -4,14 +4,13 @@ using Nethermind.Blockchain; using Nethermind.Blockchain.Receipts; using Nethermind.Config; -using Nethermind.Consensus.AuRa.Config; using Nethermind.Consensus.AuRa.InitializationSteps; using Nethermind.Consensus.Comparers; using Nethermind.Consensus.Processing; using Nethermind.Consensus.Producers; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Validators; -using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; @@ -24,6 +23,7 @@ namespace Nethermind.Merge.AuRa; public class AuRaMergeBlockProducerEnvFactory : BlockProducerEnvFactory { private readonly AuRaNethermindApi _auraApi; + private readonly IConsensusRequestsProcessor? _consensusRequestsProcessor; public AuRaMergeBlockProducerEnvFactory( AuRaNethermindApi auraApi, @@ -37,7 +37,8 @@ public AuRaMergeBlockProducerEnvFactory( ITxPool txPool, ITransactionComparerProvider transactionComparerProvider, IBlocksConfig blocksConfig, - ILogManager logManager) : base( + ILogManager logManager, + IConsensusRequestsProcessor? consensusRequestsProcessor = null) : base( worldStateManager, blockTree, specProvider, @@ -48,9 +49,11 @@ public AuRaMergeBlockProducerEnvFactory( txPool, transactionComparerProvider, blocksConfig, - logManager) + logManager, + consensusRequestsProcessor) { _auraApi = auraApi; + _consensusRequestsProcessor = consensusRequestsProcessor; } protected override BlockProcessor CreateBlockProcessor( @@ -77,9 +80,10 @@ protected override BlockProcessor CreateBlockProcessor( new AuraWithdrawalProcessor( withdrawalContractFactory.Create(readOnlyTxProcessingEnv.TransactionProcessor), logManager - ) - ), - null); + ) + ), + readOnlyTxProcessingEnv.TransactionProcessor, + consensusRequestsProcessor: _consensusRequestsProcessor); } protected override TxPoolTxSource CreateTxPoolTxSource( diff --git a/src/Nethermind/Nethermind.Merge.AuRa/InitializationSteps/InitializeBlockchainAuRaMerge.cs b/src/Nethermind/Nethermind.Merge.AuRa/InitializationSteps/InitializeBlockchainAuRaMerge.cs index cf4b4d4b6be..90015484baa 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa/InitializationSteps/InitializeBlockchainAuRaMerge.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa/InitializationSteps/InitializeBlockchainAuRaMerge.cs @@ -5,7 +5,9 @@ using Nethermind.Consensus.AuRa; using Nethermind.Consensus.AuRa.InitializationSteps; using Nethermind.Consensus.AuRa.Validators; +using Nethermind.Consensus.Withdrawals; using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Transactions; using Nethermind.Core; using Nethermind.Evm.TransactionProcessing; @@ -44,6 +46,7 @@ protected override AuRaBlockProcessor NewAuraBlockProcessor(ITxFilter txFilter, _api.BlockTree!, new AuraWithdrawalProcessor( withdrawalContractFactory.Create(transactionProcessor!), _api.LogManager), + _api.TransactionProcessor!, CreateAuRaValidator(), txFilter, GetGasLimitCalculator(), diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs new file mode 100644 index 00000000000..c16093dc3fb --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using MathNet.Numerics.Distributions; +using Nethermind.Consensus.Requests; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.State; +using Nethermind.State.Proofs; + +namespace Nethermind.Merge.Plugin.Test; + +public class ConsensusRequestsProcessorMock : IConsensusRequestsProcessor +{ + public static ConsensusRequest[] Requests = + [ + TestItem.DepositA_1Eth, + TestItem.DepositB_2Eth, + TestItem.WithdrawalRequestA, + TestItem.WithdrawalRequestB, + TestItem.ConsolidationRequestA, + TestItem.ConsolidationRequestB + ]; + + public void ProcessRequests(IReleaseSpec spec, IWorldState state, Block block, TxReceipt[] receipts) + { + if (block.IsGenesis) + return; + + block.Body.Requests = Requests; + Hash256 root = new RequestsTrie(Requests).RootHash; + block.Header.RequestsRoot = root; + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs index dd4de68d98d..5e6c8ab1c98 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs @@ -20,7 +20,8 @@ using Nethermind.Specs; using Nethermind.Specs.Forks; using Nethermind.State; -using Nethermind.Consensus.BeaconBlockRoot; +using Nethermind.Core.ConsensusRequests; +using Microsoft.CodeAnalysis; namespace Nethermind.Merge.Plugin.Test { @@ -28,7 +29,6 @@ namespace Nethermind.Merge.Plugin.Test public partial class EngineModuleTests { private static readonly DateTime Timestamp = DateTimeOffset.FromUnixTimeSeconds(1000).UtcDateTime; - private static readonly IBeaconBlockRootHandler _beaconBlockRootHandler = new BeaconBlockRootHandler(); private ITimestamper Timestamper { get; } = new ManualTimestamper(Timestamp); private void AssertExecutionStatusChanged(IBlockFinder blockFinder, Hash256 headBlockHash, Hash256 finalizedBlockHash, Hash256 safeBlockHash) @@ -124,7 +124,6 @@ private static ExecutionPayloadV3 CreateBlockRequestV3(MergeTestBlockchain chain blockRequestV3.TryGetBlock(out Block? block); Snapshot before = chain.State.TakeSnapshot(); - _beaconBlockRootHandler.ApplyContractStateChanges(block!, chain.SpecProvider.GenesisSpec, chain.State); var blockHashStore = new BlockhashStore(chain.SpecProvider, chain.State); blockHashStore.ApplyBlockhashStateChanges(block!.Header); chain.WithdrawalProcessor?.ProcessWithdrawals(block!, chain.SpecProvider.GenesisSpec); @@ -139,9 +138,63 @@ private static ExecutionPayloadV3 CreateBlockRequestV3(MergeTestBlockchain chain return blockRequestV3; } + private static ExecutionPayloadV4 CreateBlockRequestV4(MergeTestBlockchain chain, ExecutionPayload parent, Address miner, Withdrawal[]? withdrawals = null, + ulong? blobGasUsed = null, ulong? excessBlobGas = null, Transaction[]? transactions = null, Hash256? parentBeaconBlockRoot = null, ConsensusRequest[]? requests = null) + { + ExecutionPayloadV4 blockRequestV4 = CreateBlockRequestInternal(parent, miner, withdrawals, blobGasUsed, excessBlobGas, transactions: transactions, parentBeaconBlockRoot: parentBeaconBlockRoot, requests: requests); + blockRequestV4.TryGetBlock(out Block? block); + + Snapshot before = chain.State.TakeSnapshot(); + var blockHashStore = new BlockhashStore(chain.SpecProvider, chain.State); + blockHashStore.ApplyBlockhashStateChanges(block!.Header); + + chain.ConsensusRequestsProcessor?.ProcessRequests(chain.SpecProvider.GenesisSpec, chain.State, block!, Array.Empty()); + + chain.State.Commit(chain.SpecProvider.GenesisSpec); + chain.State.RecalculateStateRoot(); + blockRequestV4.StateRoot = chain.State.StateRoot; + chain.State.Restore(before); + + TryCalculateHash(blockRequestV4, out Hash256? hash); + blockRequestV4.BlockHash = hash; + return blockRequestV4; + } + private static T CreateBlockRequestInternal(ExecutionPayload parent, Address miner, Withdrawal[]? withdrawals = null, - ulong? blobGasUsed = null, ulong? excessBlobGas = null, Transaction[]? transactions = null, Hash256? parentBeaconBlockRoot = null) where T : ExecutionPayload, new() + ulong? blobGasUsed = null, ulong? excessBlobGas = null, Transaction[]? transactions = null, Hash256? parentBeaconBlockRoot = null, ConsensusRequest[]? requests = null + ) where T : ExecutionPayload, new() { + Deposit[]? deposits = null; + WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + + if (requests is not null) + { + (int depositCount, int withdrawalRequestCount, int consolidationRequestCount) = requests.GetTypeCounts(); + deposits = new Deposit[depositCount]; + withdrawalRequests = new WithdrawalRequest[withdrawalRequestCount]; + consolidationRequests = new ConsolidationRequest[consolidationRequestCount]; + int depositIndex = 0; + int withdrawalRequestIndex = 0; + int consolidationRequestIndex = 0; + for (int i = 0; i < requests.Length; ++i) + { + ConsensusRequest request = requests[i]; + if (request.Type == ConsensusRequestsType.Deposit) + { + deposits[depositIndex++] = (Deposit)request; + } + else if (request.Type == ConsensusRequestsType.WithdrawalRequest) + { + withdrawalRequests[withdrawalRequestIndex++] = (WithdrawalRequest)request; + } + else + { + consolidationRequests[consolidationRequestIndex++] = (ConsolidationRequest)request; + } + } + } + T blockRequest = new() { ParentHash = parent.BlockHash, @@ -157,6 +210,9 @@ private static T CreateBlockRequestInternal(ExecutionPayload parent, Address BlobGasUsed = blobGasUsed, ExcessBlobGas = excessBlobGas, ParentBeaconBlockRoot = parentBeaconBlockRoot, + DepositRequests = deposits, + WithdrawalRequests = withdrawalRequests, + ConsolidationRequests = consolidationRequests, }; blockRequest.SetTransactions(transactions ?? Array.Empty()); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.PayloadProduction.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.PayloadProduction.cs index da0502dba1e..3f4ee0c4213 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.PayloadProduction.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.PayloadProduction.cs @@ -343,8 +343,8 @@ public async Task TestTwoTransaction_SameContract_WithBlockImprovement() using SemaphoreSlim blockImprovementLock = new(0); - MergeTestBlockchain blockchain = CreateBaseBlockchain(null, null, LimboLogs.Instance); - blockchain.InitialStateMutator = (state) => + MergeTestBlockchain blockchain = CreateBaseBlockchain(logManager: LimboLogs.Instance); + blockchain.InitialStateMutator = state => { state.CreateAccount(new Address("0xBC2Fd1637C49839aDB7Bb57F9851EAE3194A90f7"), (UInt256)1200482917041833040, 1); }; @@ -472,7 +472,7 @@ public async Task Cannot_build_invalid_block_with_the_branch() // we build one more block on the same level Block block31B = chain.PostMergeBlockProducer!.PrepareEmptyBlock(block30.Header, payloadAttributes); - await rpc.engine_newPayloadV1(new ExecutionPayload(block31B)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(block31B)); // ...and we change the main chain, so main chain now is 30->31B, block improvement for block 32A is still in progress string? payloadId = rpc.engine_forkchoiceUpdatedV1( @@ -585,7 +585,7 @@ await rpc.engine_forkchoiceUpdatedV1( PostMergeBlockProducer blockProducer = chain.PostMergeBlockProducer!; Block emptyBlock = blockProducer.PrepareEmptyBlock(chain.BlockTree.Head!.Header, new PayloadAttributes { Timestamp = (ulong)DateTime.UtcNow.AddDays(5).Ticks, PrevRandao = TestItem.KeccakA, SuggestedFeeRecipient = Address.Zero }); - Task> result1 = await rpc.engine_newPayloadV1(new ExecutionPayload(emptyBlock)); + Task> result1 = await rpc.engine_newPayloadV1(ExecutionPayload.Create(emptyBlock)); result1.Result.Data.Status.Should().Be(PayloadStatus.Valid); } @@ -608,7 +608,7 @@ await rpc.engine_forkchoiceUpdatedV2( Withdrawals = [TestItem.WithdrawalA_1Eth] }; Block emptyBlock = blockProducer.PrepareEmptyBlock(chain.BlockTree.Head!.Header, payloadAttributes); - Task> result1 = await rpc.engine_newPayloadV2(new ExecutionPayload(emptyBlock)); + Task> result1 = await rpc.engine_newPayloadV2(ExecutionPayload.Create(emptyBlock)); result1.Result.Data.Status.Should().Be(PayloadStatus.Valid); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs index c499adefe37..2f5d7471043 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs @@ -14,6 +14,7 @@ using Nethermind.Consensus.Comparers; using Nethermind.Consensus.Processing; using Nethermind.Consensus.Producers; +using Nethermind.Consensus.Requests; using Nethermind.Consensus.Rewards; using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; @@ -25,14 +26,17 @@ using Nethermind.Crypto; using Nethermind.Db; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Facade.Eth; using Nethermind.HealthChecks; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Merge.Plugin.BlockProduction; using Nethermind.Merge.Plugin.GC; +using Nethermind.Merge.Plugin.handlers; using Nethermind.Merge.Plugin.Handlers; using Nethermind.Merge.Plugin.Synchronization; +using Nethermind.Merge.Plugin.Test.Synchronization; using Nethermind.Specs; using Nethermind.Specs.ChainSpecStyle; using Nethermind.Specs.Forks; @@ -52,19 +56,26 @@ public Task Setup() return KzgPolynomialCommitments.InitializeAsync(); } - protected virtual MergeTestBlockchain CreateBaseBlockchain(IMergeConfig? mergeConfig = null, - IPayloadPreparationService? mockedPayloadService = null, ILogManager? logManager = null) => - new(mergeConfig, mockedPayloadService, logManager); - - - protected async Task CreateBlockchain(IReleaseSpec? releaseSpec = null, IMergeConfig? mergeConfig = null, - IPayloadPreparationService? mockedPayloadService = null) - => await CreateBaseBlockchain(mergeConfig, mockedPayloadService) + protected virtual MergeTestBlockchain CreateBaseBlockchain( + IMergeConfig? mergeConfig = null, + IPayloadPreparationService? mockedPayloadService = null, + ILogManager? logManager = null, + IConsensusRequestsProcessor? mockedConsensusRequestsProcessor = null) => + new(mergeConfig, mockedPayloadService, logManager, mockedConsensusRequestsProcessor); + + + protected async Task CreateBlockchain( + IReleaseSpec? releaseSpec = null, + IMergeConfig? mergeConfig = null, + IPayloadPreparationService? mockedPayloadService = null, + ILogManager? logManager = null, + IConsensusRequestsProcessor? mockedConsensusRequestsProcessor = null) + => await CreateBaseBlockchain(mergeConfig, mockedPayloadService, logManager, mockedConsensusRequestsProcessor) .Build(new TestSingleReleaseSpecProvider(releaseSpec ?? London.Instance)); protected async Task CreateBlockchain(ISpecProvider specProvider, ILogManager? logManager = null) - => await CreateBaseBlockchain(null, null, logManager).Build(specProvider); + => await CreateBaseBlockchain(logManager: logManager).Build(specProvider); private IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncConfig? syncConfig = null, TimeSpan? newPayloadTimeout = null, int newPayloadCacheSize = 50) { @@ -94,6 +105,10 @@ private IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncConf chain.PayloadPreparationService!, chain.SpecProvider!, chain.LogManager), + new GetPayloadV4Handler( + chain.PayloadPreparationService!, + chain.SpecProvider!, + chain.LogManager), new NewPayloadHandler( chain.BlockValidator, chain.BlockTree, @@ -126,6 +141,8 @@ private IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncConf new BlocksConfig().SecondsPerSlot), new GetPayloadBodiesByHashV1Handler(chain.BlockTree, chain.LogManager), new GetPayloadBodiesByRangeV1Handler(chain.BlockTree, chain.LogManager), + new GetPayloadBodiesByHashV2Handler(chain.BlockTree, chain.LogManager), + new GetPayloadBodiesByRangeV2Handler(chain.BlockTree, chain.LogManager), new ExchangeTransitionConfigurationV1Handler(chain.PoSSwitcher, chain.LogManager), new ExchangeCapabilitiesHandler(capabilitiesProvider, chain.LogManager), chain.SpecProvider, @@ -163,13 +180,14 @@ public MergeTestBlockchain ThrottleBlockProcessor(int delayMs) return this; } - public MergeTestBlockchain(IMergeConfig? mergeConfig = null, IPayloadPreparationService? mockedPayloadPreparationService = null, ILogManager? logManager = null) + public MergeTestBlockchain(IMergeConfig? mergeConfig = null, IPayloadPreparationService? mockedPayloadPreparationService = null, ILogManager? logManager = null, IConsensusRequestsProcessor? mockedConsensusRequestsProcessor = null) { GenesisBlockBuilder = Core.Test.Builders.Build.A.Block.Genesis.Genesis.WithTimestamp(1UL); MergeConfig = mergeConfig ?? new MergeConfig() { TerminalTotalDifficulty = "0" }; PayloadPreparationService = mockedPayloadPreparationService; SyncPeerPool = Substitute.For(); LogManager = logManager ?? LogManager; + ConsensusRequestsProcessor = mockedConsensusRequestsProcessor; } protected override Task AddBlocksOnStart() => Task.CompletedTask; @@ -207,8 +225,8 @@ protected override IBlockProducer CreateTestBlockProducer(TxPoolTxSource txPoolT TxPool, transactionComparerProvider, blocksConfig, - LogManager); - + LogManager, + ConsensusRequestsProcessor); BlockProducerEnv blockProducerEnv = blockProducerEnvFactory.Create(); PostMergeBlockProducer? postMergeBlockProducer = blockProducerFactory.Create(blockProducerEnv); @@ -220,6 +238,8 @@ protected override IBlockProducer CreateTestBlockProducer(TxPoolTxSource txPoolT LogManager, TimeSpan.FromSeconds(MergeConfig.SecondsPerSlot), 50000); // by default we want to avoid cleanup payload effects in testing + + ConsensusRequestsProcessor ??= new ConsensusRequestsProcessor(TxProcessor); return new MergeBlockProducer(preMergeBlockProducer, postMergeBlockProducer, PoSSwitcher); } @@ -235,8 +255,10 @@ protected override IBlockProcessor CreateBlockProcessor() State, ReceiptStorage, new BlockhashStore(SpecProvider, State), + TxProcessor, LogManager, - WithdrawalProcessor); + WithdrawalProcessor, + consensusRequestsProcessor: ConsensusRequestsProcessor); return new TestBlockProcessorInterceptor(processor, _blockProcessingThrottle); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs index 4b5e9ad8f13..c3c2f884436 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Synchronization.cs @@ -55,7 +55,7 @@ public async Task forkChoiceUpdatedV1_unknown_block_initiates_syncing() .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - await rpc.engine_newPayloadV1(new ExecutionPayload(block)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(block)); // sync has not started yet chain.BeaconSync!.IsBeaconSyncHeadersFinished().Should().BeTrue(); chain.BeaconSync.IsBeaconSyncFinished(block.Header).Should().BeTrue(); @@ -225,7 +225,7 @@ public async Task forkChoiceUpdatedV1_unknown_block_parent_while_syncing_initiat .WithPostMergeFlag(true) .TestObject; - await rpc.engine_newPayloadV1(new ExecutionPayload(block)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(block)); // sync has not started yet chain.BeaconSync!.IsBeaconSyncHeadersFinished().Should().BeTrue(); chain.BeaconSync.IsBeaconSyncFinished(block.Header).Should().BeTrue(); @@ -259,7 +259,7 @@ public async Task forkChoiceUpdatedV1_unknown_block_parent_while_syncing_initiat pointers.LowestInsertedHeader = block.Header; AssertBlockTreePointers(chain.BlockTree, pointers); - await rpc.engine_newPayloadV1(new ExecutionPayload(nextUnconnectedBlock)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(nextUnconnectedBlock)); forkchoiceStateV1 = new(nextUnconnectedBlock.Hash!, startingHead, startingHead); forkchoiceUpdatedResult = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); forkchoiceUpdatedResult.Data.PayloadStatus.Status.Should() @@ -298,7 +298,7 @@ public async Task should_return_invalid_lvh_null_on_invalid_blocks_during_the_sy .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - ExecutionPayload startingNewPayload = new(block); + ExecutionPayload startingNewPayload = ExecutionPayload.Create(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); @@ -320,7 +320,7 @@ public async Task should_return_invalid_lvh_null_on_invalid_blocks_during_the_sy r.TryGetBlock(out Block? newBlock); newBlock!.Header.GasLimit = long.MaxValue; // incorrect gas limit newBlock.Header.Hash = newBlock.CalculateHash(); - ResultWrapper payloadStatus = await rpc.engine_newPayloadV1(new ExecutionPayload(newBlock)); + ResultWrapper payloadStatus = await rpc.engine_newPayloadV1(ExecutionPayload.Create(newBlock)); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatusV1.Invalid).ToUpper()); payloadStatus.Data.LatestValidHash.Should().BeNull(); } @@ -333,7 +333,7 @@ public async Task newPayloadV1_can_insert_blocks_from_cache_when_syncing() IEngineRpcModule rpc = CreateEngineModule(chain); Hash256 startingHead = chain.BlockTree.HeadHash; - ExecutionPayload parentBlockRequest = new(Build.A.Block.WithNumber(2).TestObject); + ExecutionPayload parentBlockRequest = ExecutionPayload.Create(Build.A.Block.WithNumber(2).TestObject); ExecutionPayload[] requests = CreateBlockRequestBranch(chain, parentBlockRequest, Address.Zero, 7); ResultWrapper payloadStatus; foreach (ExecutionPayload r in requests) @@ -391,7 +391,7 @@ public async Task first_new_payload_set_beacon_main_chain() .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - ExecutionPayload startingNewPayload = new(block); + ExecutionPayload startingNewPayload = ExecutionPayload.Create(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); ResultWrapper forkchoiceUpdatedResult = @@ -432,7 +432,7 @@ public async Task repeated_new_payloads_do_not_change_metadata() .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - ExecutionPayload startingNewPayload = new(block); + ExecutionPayload startingNewPayload = ExecutionPayload.Create(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); ResultWrapper forkchoiceUpdatedResult = @@ -490,7 +490,7 @@ public async Task Can_set_beacon_pivot_in_new_payload_if_null() newBlock2.CalculateHash(); chain.BeaconPivot!.BeaconPivotExists().Should().BeFalse(); - ResultWrapper result = await rpc.engine_newPayloadV1(new ExecutionPayload(newBlock2)); + ResultWrapper result = await rpc.engine_newPayloadV1(ExecutionPayload.Create(newBlock2)); result.Data.Status.Should().Be(PayloadStatus.Syncing); chain.BeaconPivot.BeaconPivotExists().Should().BeTrue(); } @@ -524,7 +524,7 @@ public async Task BeaconMainChain_is_correctly_set_when_block_was_not_processed( .WithStateRoot(new Hash256("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")).TestObject; newBlock2.CalculateHash(); - await rpc.engine_newPayloadV1(new ExecutionPayload(newBlock2)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(newBlock2)); await rpc.engine_forkchoiceUpdatedV1(new ForkchoiceStateV1(newBlock2.Hash!, newBlock2.Hash!, newBlock2.Hash!), null); chain.BlockTree.FindLevel(10)!.BlockInfos[0].Metadata.Should().Be(BlockMetadata.None); @@ -562,7 +562,7 @@ public async Task Repeated_block_do_not_change_metadata() newBlock2.CalculateHash(); await chain.BlockTree.SuggestBlockAsync(newBlock2!, BlockTreeSuggestOptions.FillBeaconBlock); - await rpc.engine_newPayloadV1(new ExecutionPayload(newBlock2)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(newBlock2)); Block? block = chain.BlockTree.FindBlock(newBlock2.GetOrCalculateHash(), BlockTreeLookupOptions.None); block?.TotalDifficulty.Should().NotBe((UInt256)0); BlockInfo? blockInfo = chain.BlockTree.FindLevel(newBlock2.Number!)?.BlockInfos[0]; @@ -590,7 +590,7 @@ public async Task second_new_payload_should_not_set_beacon_main_chain() .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - ExecutionPayload startingNewPayload = new(block); + ExecutionPayload startingNewPayload = ExecutionPayload.Create(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); ResultWrapper forkchoiceUpdatedResult = @@ -650,7 +650,7 @@ public async Task should_reorg_during_the_sync(int initialChainPayloadsCount, in .WithAuthor(Address.Zero) .WithPostMergeFlag(true) .TestObject; - ExecutionPayload startingNewPayload = new(block); + ExecutionPayload startingNewPayload = ExecutionPayload.Create(block); await rpc.engine_newPayloadV1(startingNewPayload); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); @@ -691,7 +691,7 @@ public async Task Blocks_from_cache_inserted_when_fast_headers_sync_finish_befor using MergeTestBlockchain chain = await CreateBlockchain(); Hash256 startingHead = chain.BlockTree.HeadHash; IEngineRpcModule rpc = CreateEngineModule(chain); - ExecutionPayload[] requests = CreateBlockRequestBranch(chain, new ExecutionPayload(chain.BlockTree.Head!), Address.Zero, 7); + ExecutionPayload[] requests = CreateBlockRequestBranch(chain, ExecutionPayload.Create(chain.BlockTree.Head!), Address.Zero, 7); ResultWrapper payloadStatus; for (int i = 4; i < requests.Length - 1; i++) @@ -739,7 +739,7 @@ public async Task Maintain_correct_pointers_for_beacon_sync_in_archive_sync() Hash256 startingHead = chain.BlockTree.HeadHash; // create 7 block gap int gap = 7; - ExecutionPayload headBlockRequest = new(chain.BlockTree.Head!); + ExecutionPayload headBlockRequest = ExecutionPayload.Create(chain.BlockTree.Head!); Block[] missingBlocks = new Block[gap]; for (int i = 0; i < gap; i++) { @@ -851,7 +851,7 @@ public async Task Blocks_before_pivots_should_not_be_added_if_node_has_never_bee IEngineRpcModule rpc = CreateEngineModule(chain, syncConfig); Block blockBeforePivot = syncedBlockTree.FindBlock(2, BlockTreeLookupOptions.None)!; - ExecutionPayload prePivotRequest = new(blockBeforePivot); + ExecutionPayload prePivotRequest = ExecutionPayload.Create(blockBeforePivot); ResultWrapper payloadStatus = await rpc.engine_newPayloadV1(prePivotRequest); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatusV1.Syncing).ToUpper()); chain.BlockTree.FindBlock(prePivotRequest.BlockHash).Should().BeNull(); @@ -876,7 +876,7 @@ public async Task Blocks_before_pivots_should_not_be_added_if_node_has_been_sync IEngineRpcModule rpc = CreateEngineModule(chain, syncConfig); Block blockBeforePivot = syncedBlockTree.FindBlock(2, BlockTreeLookupOptions.None)!; - ExecutionPayload prePivotRequest = new(blockBeforePivot); + ExecutionPayload prePivotRequest = ExecutionPayload.Create(blockBeforePivot); ResultWrapper payloadStatus = await rpc.engine_newPayloadV1(prePivotRequest); payloadStatus.Data.Status.Should().Be(nameof(PayloadStatus.Valid).ToUpper()); chain.BlockTree.FindBlock(prePivotRequest.BlockHash).Should().NotBeNull(); @@ -898,7 +898,7 @@ public async Task Maintain_correct_pointers_for_beacon_sync_in_fast_sync() // create block gap from fast sync pivot int gap = 7; ExecutionPayload[] requests = - CreateBlockRequestBranch(chain, new ExecutionPayload(syncedBlockTree.Head!), Address.Zero, gap); + CreateBlockRequestBranch(chain, ExecutionPayload.Create(syncedBlockTree.Head!), Address.Zero, gap); // setting up beacon pivot ExecutionPayload pivotRequest = CreateBlockRequest(chain, requests[^1], Address.Zero); ResultWrapper payloadStatus = await rpc.engine_newPayloadV1(pivotRequest); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs index 9f3f8db4439..0e6d59ef663 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V1.cs @@ -445,7 +445,7 @@ public async Task executePayloadV1_rejects_block_with_invalid_receiptsRoot() [Test] public async Task executePayloadV1_result_is_fail_when_blockchainprocessor_report_exception() { - using MergeTestBlockchain chain = await CreateBaseBlockchain(null, null) + using MergeTestBlockchain chain = await CreateBaseBlockchain() .Build(new TestSingleReleaseSpecProvider(London.Instance)); IEngineRpcModule rpc = CreateEngineModule(chain); @@ -481,7 +481,7 @@ public virtual async Task executePayloadV1_accepts_already_known_block(bool thro await chain.BlockTree.SuggestBlockAsync(block!); await bestBlockProcessed.WaitAsync(); - ExecutionPayload blockRequest = new(block); + ExecutionPayload blockRequest = ExecutionPayload.Create(block); ResultWrapper executePayloadResult = await rpc.engine_newPayloadV1(blockRequest); executePayloadResult.Data.Status.Should().Be(PayloadStatus.Valid); } @@ -641,13 +641,13 @@ public async Task forkChoiceUpdatedV1_no_common_branch_fails() Block parent = Build.A.Block.WithNumber(2).WithParentHash(TestItem.KeccakA).WithNonce(0).WithDifficulty(0).TestObject; Block block = Build.A.Block.WithNumber(3).WithParent(parent).WithNonce(0).WithDifficulty(0).TestObject; - await rpc.engine_newPayloadV1(new ExecutionPayload(parent)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(parent)); ForkchoiceStateV1 forkchoiceStateV1 = new(parent.Hash!, startingHead, startingHead); ResultWrapper forkchoiceUpdatedResult = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV1); forkchoiceUpdatedResult.Data.PayloadStatus.Status.Should().Be("SYNCING"); - await rpc.engine_newPayloadV1(new ExecutionPayload(block)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(block)); ForkchoiceStateV1 forkchoiceStateV11 = new(parent.Hash!, startingHead, startingHead); ResultWrapper forkchoiceUpdatedResult_1 = await rpc.engine_forkchoiceUpdatedV1(forkchoiceStateV11); @@ -668,7 +668,7 @@ public async Task forkChoiceUpdatedV1_block_still_processing() chain.ThrottleBlockProcessor(200); ResultWrapper newPayloadV1 = - await rpc.engine_newPayloadV1(new ExecutionPayload(block)); + await rpc.engine_newPayloadV1(ExecutionPayload.Create(block)); newPayloadV1.Data.Status.Should().Be("SYNCING"); ForkchoiceStateV1 forkchoiceStateV1 = new(block.Hash!, startingHead, startingHead); @@ -696,7 +696,7 @@ public async Task AlreadyKnown_not_cached_block_should_return_valid() .WithBeneficiary(Build.An.Address.TestObject) .TestObject; - (await rpc.engine_newPayloadV1(new ExecutionPayload(b4))).Data.Status.Should().Be(PayloadStatus.Valid); + (await rpc.engine_newPayloadV1(ExecutionPayload.Create(b4))).Data.Status.Should().Be(PayloadStatus.Valid); Block? b5 = Build.A.Block .WithNumber(b4.Number + 1) @@ -706,8 +706,8 @@ public async Task AlreadyKnown_not_cached_block_should_return_valid() .WithStateRoot(b4.StateRoot!) .TestObject; - (await rpc.engine_newPayloadV1(new ExecutionPayload(b5))).Data.Status.Should().Be(PayloadStatus.Valid); - (await rpc.engine_newPayloadV1(new ExecutionPayload(b5))).Data.Status.Should().Be(PayloadStatus.Valid); + (await rpc.engine_newPayloadV1(ExecutionPayload.Create(b5))).Data.Status.Should().Be(PayloadStatus.Valid); + (await rpc.engine_newPayloadV1(ExecutionPayload.Create(b5))).Data.Status.Should().Be(PayloadStatus.Valid); } [Test, NonParallelizable] @@ -741,8 +741,8 @@ public async Task Invalid_block_on_processing_wont_be_accepted_if_sent_twice_in_ .TestObject; chain.ThrottleBlockProcessor(1000); // throttle the block processor enough so that the block processing queue is never empty - (await rpc.engine_newPayloadV1(new ExecutionPayload(block))).Data.Status.Should().Be(PayloadStatus.Syncing); - (await rpc.engine_newPayloadV1(new ExecutionPayload(block))).Data.Status.Should().BeOneOf(PayloadStatus.Syncing); + (await rpc.engine_newPayloadV1(ExecutionPayload.Create(block))).Data.Status.Should().Be(PayloadStatus.Syncing); + (await rpc.engine_newPayloadV1(ExecutionPayload.Create(block))).Data.Status.Should().BeOneOf(PayloadStatus.Syncing); } [Test] @@ -849,10 +849,10 @@ public async Task executePayloadV1_on_top_of_terminal_block() .WithStateRoot(new Hash256("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) .WithDifficulty(0).WithNonce(0).TestObject; firstPoSBlock.CalculateHash(); - ExecutionPayload executionPayload = new(firstPoSBlock); + ExecutionPayload executionPayload = ExecutionPayload.Create(firstPoSBlock); ResultWrapper resultWrapper = await rpc.engine_newPayloadV1(executionPayload); resultWrapper.Data.Status.Should().Be(PayloadStatus.Valid); - new ExecutionPayload(chain.BlockTree.BestSuggestedBody!).Should().BeEquivalentTo(executionPayload); + ExecutionPayload.Create(chain.BlockTree.BestSuggestedBody!).Should().BeEquivalentTo(executionPayload); } [Test] @@ -894,7 +894,7 @@ public async Task executePayloadV1_on_top_of_not_processed_invalid_terminal_bloc .WithStateRoot(new Hash256("0x1ef7300d8961797263939a3d29bbba4ccf1702fabf02d8ad7a20b454edb6fd2f")) .WithDifficulty(0).WithNonce(0).TestObject; firstPoSBlock.CalculateHash(); - ExecutionPayload executionPayload = new(firstPoSBlock); + ExecutionPayload executionPayload = ExecutionPayload.Create(firstPoSBlock); ResultWrapper resultWrapper = await rpc.engine_newPayloadV1(executionPayload); resultWrapper.Data.Status.Should().Be(PayloadStatus.Invalid); resultWrapper.Data.LatestValidHash.Should().Be(Keccak.Zero); @@ -908,7 +908,7 @@ public async Task executePayloadV1_accepts_first_block() ExecutionPayload executionPayload = CreateBlockRequest(chain, CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD); ResultWrapper resultWrapper = await rpc.engine_newPayloadV1(executionPayload); resultWrapper.Data.Status.Should().Be(PayloadStatus.Valid); - new ExecutionPayload(chain.BlockTree.BestSuggestedBody!).Should().BeEquivalentTo(executionPayload); + ExecutionPayload.Create(chain.BlockTree.BestSuggestedBody!).Should().BeEquivalentTo(executionPayload); } [Test] @@ -1530,7 +1530,7 @@ public async Task Should_return_ClientVersionV1() [Test] public async Task Should_return_capabilities() { - using MergeTestBlockchain chain = await CreateBlockchain(Cancun.Instance); + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance); IEngineRpcModule rpcModule = CreateEngineModule(chain); IOrderedEnumerable expected = typeof(IEngineRpcModule).GetMethods() .Select(m => m.Name) diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V2.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V2.cs index 1e8855b494e..7eda65bf5cb 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V2.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V2.cs @@ -110,8 +110,7 @@ public virtual async Task Should_process_block_as_expected_V2(string latestValid }, Array.Empty(), Array.Empty(), - withdrawals - ); + withdrawals); GetPayloadV2Result expectedPayload = new(block, UInt256.Zero); response = await RpcTest.TestSerializedRequest(rpc, "engine_getPayloadV2", expectedPayloadId); @@ -125,7 +124,7 @@ public virtual async Task Should_process_block_as_expected_V2(string latestValid })); response = await RpcTest.TestSerializedRequest(rpc, "engine_newPayloadV2", - chain.JsonSerializer.Serialize(new ExecutionPayload(block))); + chain.JsonSerializer.Serialize(ExecutionPayload.Create(block))); successResponse = chain.JsonSerializer.Deserialize(response); successResponse.Should().NotBeNull(); @@ -478,7 +477,7 @@ await rpc.engine_forkchoiceUpdatedV2(new ForkchoiceStateV1(executionPayload1.Blo .WithWithdrawals(withdrawals.ToArray()) .TestObject; - ResultWrapper fcuResult = await rpc.engine_newPayloadV2(new ExecutionPayload(newBlock)); + ResultWrapper fcuResult = await rpc.engine_newPayloadV2(ExecutionPayload.Create(newBlock)); fcuResult.Data.Status.Should().Be(PayloadStatus.Valid); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs index ebc6b40d7c2..c97412ffb34 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs @@ -355,10 +355,13 @@ public async Task NewPayloadV3_should_verify_blob_versioned_hashes_again Substitute.For>(), Substitute.For>(), Substitute.For>(), + Substitute.For>(), newPayloadHandlerMock, Substitute.For(), - Substitute.For, IEnumerable>>(), + Substitute.For, IEnumerable>>(), Substitute.For(), + Substitute.For, IEnumerable>>(), + Substitute.For(), Substitute.For>(), Substitute.For, IEnumerable>>(), chain.SpecProvider, diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs new file mode 100644 index 00000000000..7ec6ba4f282 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs @@ -0,0 +1,505 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Consensus.Producers; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using Nethermind.JsonRpc; +using Nethermind.JsonRpc.Test; +using Nethermind.Merge.Plugin.Data; +using Nethermind.Specs.Forks; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Merge.Plugin.Test; + +public partial class EngineModuleTests +{ + [TestCase( + "0x948f67f47376af5d09cc39ec25a84c84774f14b2e80289064c2de73db33cc573", + "0xb8e06e1a99d81358edd0a581fef980aff00cc9c316da8119bec7a13a6e6fa167", + "0xa272b2f949e4a0e411c9b45542bd5d0ef3c311b5f26c4ed6b7a8d4f605a91154", + "0x96b752d22831ad92")] + public virtual async Task Should_process_block_as_expected_V4(string latestValidHash, string blockHash, + string stateRoot, string payloadId) + { + using MergeTestBlockchain chain = + await CreateBlockchain(Prague.Instance, new MergeConfig { TerminalTotalDifficulty = "0" }); + IEngineRpcModule rpc = CreateEngineModule(chain); + Hash256 startingHead = chain.BlockTree.HeadHash; + Hash256 prevRandao = Keccak.Zero; + Address feeRecipient = TestItem.AddressC; + ulong timestamp = Timestamper.UnixTime.Seconds; + var fcuState = new + { + headBlockHash = startingHead.ToString(), + safeBlockHash = startingHead.ToString(), + finalizedBlockHash = Keccak.Zero.ToString() + }; + Withdrawal[] withdrawals = new[] + { + new Withdrawal { Index = 1, AmountInGwei = 3, Address = TestItem.AddressB, ValidatorIndex = 2 } + }; + var payloadAttrs = new + { + timestamp = timestamp.ToHexString(true), + prevRandao = prevRandao.ToString(), + suggestedFeeRecipient = feeRecipient.ToString(), + withdrawals, + parentBeaconBLockRoot = Keccak.Zero + }; + string?[] @params = new string?[] + { + chain.JsonSerializer.Serialize(fcuState), chain.JsonSerializer.Serialize(payloadAttrs) + }; + string expectedPayloadId = payloadId; + + string response = await RpcTest.TestSerializedRequest(rpc, "engine_forkchoiceUpdatedV3", @params!); + JsonRpcSuccessResponse? successResponse = chain.JsonSerializer.Deserialize(response); + + successResponse.Should().NotBeNull(); + response.Should().Be(chain.JsonSerializer.Serialize(new JsonRpcSuccessResponse + { + Id = successResponse.Id, + Result = new ForkchoiceUpdatedV1Result + { + PayloadId = expectedPayloadId, + PayloadStatus = new PayloadStatusV1 + { + LatestValidHash = new(latestValidHash), + Status = PayloadStatus.Valid, + ValidationError = null + } + } + })); + + Hash256 expectedBlockHash = new(blockHash); + Block block = new( + new( + startingHead, + Keccak.OfAnEmptySequenceRlp, + feeRecipient, + UInt256.Zero, + 1, + chain.BlockTree.Head!.GasLimit, + timestamp, + Bytes.FromHexString("0x4e65746865726d696e64") // Nethermind + ) + { + BlobGasUsed = 0, + ExcessBlobGas = 0, + BaseFeePerGas = 0, + Bloom = Bloom.Empty, + GasUsed = 0, + Hash = expectedBlockHash, + MixHash = prevRandao, + ParentBeaconBlockRoot = Keccak.Zero, + ReceiptsRoot = chain.BlockTree.Head!.ReceiptsRoot!, + StateRoot = new(stateRoot), + }, + Array.Empty(), + Array.Empty(), + withdrawals); + GetPayloadV4Result expectedPayload = new(block, UInt256.Zero, new BlobsBundleV1(block)); + + response = await RpcTest.TestSerializedRequest(rpc, "engine_getPayloadV4", expectedPayloadId); + successResponse = chain.JsonSerializer.Deserialize(response); + + successResponse.Should().NotBeNull(); + response.Should().Be(chain.JsonSerializer.Serialize(new JsonRpcSuccessResponse + { + Id = successResponse.Id, + Result = expectedPayload + })); + + response = await RpcTest.TestSerializedRequest(rpc, "engine_newPayloadV4", + chain.JsonSerializer.Serialize(ExecutionPayloadV4.Create(block)), "[]", Keccak.Zero.ToString(true)); + successResponse = chain.JsonSerializer.Deserialize(response); + + successResponse.Should().NotBeNull(); + response.Should().Be(chain.JsonSerializer.Serialize(new JsonRpcSuccessResponse + { + Id = successResponse.Id, + Result = new PayloadStatusV1 + { + LatestValidHash = expectedBlockHash, + Status = PayloadStatus.Valid, + ValidationError = null + } + })); + + fcuState = new + { + headBlockHash = expectedBlockHash.ToString(true), + safeBlockHash = expectedBlockHash.ToString(true), + finalizedBlockHash = startingHead.ToString(true) + }; + @params = new[] { chain.JsonSerializer.Serialize(fcuState), null }; + + response = await RpcTest.TestSerializedRequest(rpc, "engine_forkchoiceUpdatedV3", @params!); + successResponse = chain.JsonSerializer.Deserialize(response); + + successResponse.Should().NotBeNull(); + response.Should().Be(chain.JsonSerializer.Serialize(new JsonRpcSuccessResponse + { + Id = successResponse.Id, + Result = new ForkchoiceUpdatedV1Result + { + PayloadId = null, + PayloadStatus = new PayloadStatusV1 + { + LatestValidHash = expectedBlockHash, + Status = PayloadStatus.Valid, + ValidationError = null + } + } + })); + } + + [TestCase(30)] + public async Task can_progress_chain_one_by_one_v4(int count) + { + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance); + IEngineRpcModule rpc = CreateEngineModule(chain); + Hash256 lastHash = (await ProduceBranchV4(rpc, chain, count, CreateParentBlockRequestOnHead(chain.BlockTree), true)) + .LastOrDefault()?.BlockHash ?? Keccak.Zero; + chain.BlockTree.HeadHash.Should().Be(lastHash); + Block? last = RunForAllBlocksInBranch(chain.BlockTree, chain.BlockTree.HeadHash, b => b.IsGenesis, true); + last.Should().NotBeNull(); + last!.IsGenesis.Should().BeTrue(); + } + + [TestCase(30)] + public async Task can_progress_chain_one_by_one_v4_with_requests(int count) + { + ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance, null, null, null, consensusRequestsProcessorMock); + IEngineRpcModule rpc = CreateEngineModule(chain); + Hash256 lastHash = (await ProduceBranchV4(rpc, chain, count, CreateParentBlockRequestOnHead(chain.BlockTree), true)) + .LastOrDefault()?.BlockHash ?? Keccak.Zero; + chain.BlockTree.HeadHash.Should().Be(lastHash); + Block? last = RunForAllBlocksInBranch(chain.BlockTree, chain.BlockTree.HeadHash, b => b.IsGenesis, true); + last.Should().NotBeNull(); + last!.IsGenesis.Should().BeTrue(); + + Block? head = chain.BlockTree.Head; + head!.Requests!.Length.Should().Be(ConsensusRequestsProcessorMock.Requests.Length); + } + + private async Task> ProduceBranchV4(IEngineRpcModule rpc, + MergeTestBlockchain chain, + int count, ExecutionPayload startingParentBlock, bool setHead, Hash256? random = null) + { + List blocks = new(); + ExecutionPayload parentBlock = startingParentBlock; + parentBlock.TryGetBlock(out Block? block); + UInt256? startingTotalDifficulty = block!.IsGenesis + ? block.Difficulty : chain.BlockFinder.FindHeader(block!.Header!.ParentHash!)!.TotalDifficulty; + BlockHeader parentHeader = block!.Header; + parentHeader.TotalDifficulty = startingTotalDifficulty + + parentHeader.Difficulty; + for (int i = 0; i < count; i++) + { + ExecutionPayloadV4? getPayloadResult = await BuildAndGetPayloadOnBranchV4(rpc, chain, parentHeader, + parentBlock.Timestamp + 12, + random ?? TestItem.KeccakA, Address.Zero); + PayloadStatusV1 payloadStatusResponse = (await rpc.engine_newPayloadV4(getPayloadResult, Array.Empty(), Keccak.Zero)).Data; + payloadStatusResponse.Status.Should().Be(PayloadStatus.Valid); + if (setHead) + { + Hash256 newHead = getPayloadResult!.BlockHash; + ForkchoiceStateV1 forkchoiceStateV1 = new(newHead, newHead, newHead); + ResultWrapper setHeadResponse = await rpc.engine_forkchoiceUpdatedV3(forkchoiceStateV1); + setHeadResponse.Data.PayloadStatus.Status.Should().Be(PayloadStatus.Valid); + setHeadResponse.Data.PayloadId.Should().Be(null); + } + + blocks.Add(getPayloadResult); + parentBlock = getPayloadResult; + parentBlock.TryGetBlock(out block!); + block.Header.TotalDifficulty = parentHeader.TotalDifficulty + block.Header.Difficulty; + parentHeader = block.Header; + } + + return blocks; + } + + private async Task BuildAndGetPayloadOnBranchV4( + IEngineRpcModule rpc, MergeTestBlockchain chain, BlockHeader parentHeader, + ulong timestamp, Hash256 random, Address feeRecipient) + { + PayloadAttributes payloadAttributes = + new() { Timestamp = timestamp, PrevRandao = random, SuggestedFeeRecipient = feeRecipient, ParentBeaconBlockRoot = Keccak.Zero, Withdrawals = [] }; + + // we're using payloadService directly, because we can't use fcU for branch + string payloadId = chain.PayloadPreparationService!.StartPreparingPayload(parentHeader, payloadAttributes)!; + + ResultWrapper getPayloadResult = + await rpc.engine_getPayloadV4(Bytes.FromHexString(payloadId)); + return getPayloadResult.Data!.ExecutionPayload!; + } + + [Test] + public async Task getPayloadBodiesByRangeV2_should_fail_when_too_many_payloads_requested() + { + using MergeTestBlockchain chain = await CreateBlockchain(); + IEngineRpcModule rpc = CreateEngineModule(chain); + Task>> result = + rpc.engine_getPayloadBodiesByRangeV2(1, 1025); + + result.Result.ErrorCode.Should().Be(MergeErrorCodes.TooLargeRequest); + } + + [Test] + public async Task getPayloadBodiesByHashV2_should_fail_when_too_many_payloads_requested() + { + using MergeTestBlockchain chain = await CreateBlockchain(); + IEngineRpcModule rpc = CreateEngineModule(chain); + Hash256[] hashes = Enumerable.Repeat(TestItem.KeccakA, 1025).ToArray(); + Task>> result = + rpc.engine_getPayloadBodiesByHashV2(hashes); + + result.Result.ErrorCode.Should().Be(MergeErrorCodes.TooLargeRequest); + } + + [Test] + public async Task getPayloadBodiesByRangeV2_should_fail_when_params_below_1() + { + using MergeTestBlockchain chain = await CreateBlockchain(); + IEngineRpcModule rpc = CreateEngineModule(chain); + Task>> result = + rpc.engine_getPayloadBodiesByRangeV2(0, 1); + + result.Result.ErrorCode.Should().Be(ErrorCodes.InvalidParams); + + result = await rpc.engine_getPayloadBodiesByRangeV2(1, 0); + + result.Result.ErrorCode.Should().Be(ErrorCodes.InvalidParams); + } + + [Test] + public async Task getPayloadBodiesByRangeV2_should_return_up_to_best_body_number() + { + IBlockTree? blockTree = Substitute.For(); + + blockTree.FindBlock(Arg.Any()) + .Returns(i => Build.A.Block.WithNumber(i.ArgAt(0)).TestObject); + blockTree.Head.Returns(Build.A.Block.WithNumber(5).TestObject); + + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance); + chain.BlockTree = blockTree; + + IEngineRpcModule rpc = CreateEngineModule(chain); + IEnumerable payloadBodies = + rpc.engine_getPayloadBodiesByRangeV2(1, 7).Result.Data; + + payloadBodies.Count().Should().Be(5); + } + + private static IEnumerable> GetPayloadRequestsTestCases() + { + yield return ConsensusRequestsProcessorMock.Requests; + } + + [TestCaseSource(nameof(GetPayloadRequestsTestCases))] + public virtual async Task + getPayloadBodiesByHashV2_should_return_payload_bodies_in_order_of_request_block_hashes_and_null_for_unknown_hashes( + ConsensusRequest[]? requests) + { + + Deposit[]? deposits = null; + WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + + if (requests is not null) + { + (deposits, withdrawalRequests, consolidationRequests) = requests.SplitRequests(); + } + + ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); + + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance, null, null, null, consensusRequestsProcessorMock); + IEngineRpcModule rpc = CreateEngineModule(chain); + ExecutionPayloadV4 executionPayload1 = await SendNewBlockV3(rpc, chain, requests); + Transaction[] txs = BuildTransactions( + chain, executionPayload1.BlockHash, TestItem.PrivateKeyA, TestItem.AddressB, 3, 0, out _, out _); + + chain.AddTransactions(txs); + + ExecutionPayloadV4 executionPayload2 = await BuildAndSendNewBlockV3(rpc, chain, true, Array.Empty()); + Hash256[] blockHashes = new Hash256[] + { + executionPayload1.BlockHash, TestItem.KeccakA, executionPayload2.BlockHash + }; + IEnumerable payloadBodies = + rpc.engine_getPayloadBodiesByHashV2(blockHashes).Result.Data; + ExecutionPayloadBodyV2Result?[] expected = { + new (Array.Empty(), Array.Empty() , deposits, withdrawalRequests, consolidationRequests), + null, + new (txs, Array.Empty(), deposits, withdrawalRequests, consolidationRequests), + }; + + payloadBodies.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering()); + } + + [TestCaseSource(nameof(GetPayloadRequestsTestCases))] + public virtual async Task + getPayloadBodiesByRangeV2_should_return_payload_bodies_in_order_of_request_range_and_null_for_unknown_indexes( + ConsensusRequest[]? requests) + { + + Deposit[]? deposits = null; + WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + + if (requests is not null) + { + (int depositCount, int withdrawalRequestCount, int consolidationRequestCount) = requests.GetTypeCounts(); + deposits = new Deposit[depositCount]; + withdrawalRequests = new WithdrawalRequest[withdrawalRequestCount]; + consolidationRequests = new ConsolidationRequest[consolidationRequestCount]; + int depositIndex = 0; + int withdrawalRequestIndex = 0; + int consolidationRequestIndex = 0; + for (int i = 0; i < requests.Length; ++i) + { + ConsensusRequest request = requests[i]; + if (request.Type == ConsensusRequestsType.Deposit) + { + deposits[depositIndex++] = (Deposit)request; + } + else if (request.Type == ConsensusRequestsType.WithdrawalRequest) + { + withdrawalRequests[withdrawalRequestIndex++] = (WithdrawalRequest)request; + } + else + { + consolidationRequests[consolidationRequestIndex++] = (ConsolidationRequest)request; + + } + } + } + + ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); + + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance, null, null, null, consensusRequestsProcessorMock); + IEngineRpcModule rpc = CreateEngineModule(chain); + ExecutionPayloadV4 executionPayload1 = await SendNewBlockV3(rpc, chain, requests); + Transaction[] txs = BuildTransactions( + chain, executionPayload1.BlockHash, TestItem.PrivateKeyA, TestItem.AddressB, 3, 0, out _, out _); + + chain.AddTransactions(txs); + + ExecutionPayloadV4 executionPayload2 = await BuildAndSendNewBlockV3(rpc, chain, true, Array.Empty()); + + await rpc.engine_forkchoiceUpdatedV3(new ForkchoiceStateV1(executionPayload2.BlockHash!, + executionPayload2.BlockHash!, executionPayload2.BlockHash!)); + + + IEnumerable payloadBodies = + rpc.engine_getPayloadBodiesByRangeV2(1, 3).Result.Data; + ExecutionPayloadBodyV2Result?[] expected = + { + new (txs, Array.Empty() , deposits, withdrawalRequests, consolidationRequests), + }; + + payloadBodies.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering()); + + + } + + [Test] + public async Task getPayloadBodiesByRangeV2_empty_response() + { + using MergeTestBlockchain chain = await CreateBlockchain(); + IEngineRpcModule rpc = CreateEngineModule(chain); + IEnumerable payloadBodies = + rpc.engine_getPayloadBodiesByRangeV2(1, 1).Result.Data; + ExecutionPayloadBodyV2Result?[] expected = Array.Empty(); + + payloadBodies.Should().BeEquivalentTo(expected); + } + + private async Task SendNewBlockV3(IEngineRpcModule rpc, MergeTestBlockchain chain, ConsensusRequest[]? requests) + { + ExecutionPayloadV4 executionPayload = CreateBlockRequestV4(chain, CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, Array.Empty(), 0, 0, Array.Empty(), parentBeaconBlockRoot: TestItem.KeccakA, requests: requests); + ResultWrapper executePayloadResult = await rpc.engine_newPayloadV4(executionPayload, new byte[0][], executionPayload.ParentBeaconBlockRoot); + + executePayloadResult.Data.Status.Should().Be(PayloadStatus.Valid); + + return executionPayload; + } + + private async Task BuildAndSendNewBlockV3( + IEngineRpcModule rpc, + MergeTestBlockchain chain, + bool waitForBlockImprovement, + Withdrawal[]? withdrawals) + { + Hash256 head = chain.BlockTree.HeadHash; + ulong timestamp = Timestamper.UnixTime.Seconds; + Hash256 random = Keccak.Zero; + Address feeRecipient = Address.Zero; + ExecutionPayloadV4 executionPayload = await BuildAndGetPayloadResultV3(rpc, chain, head, + Keccak.Zero, head, timestamp, random, feeRecipient, withdrawals, waitForBlockImprovement); + ResultWrapper executePayloadResult = + await rpc.engine_newPayloadV4(executionPayload, new byte[0][], executionPayload.ParentBeaconBlockRoot); + executePayloadResult.Data.Status.Should().Be(PayloadStatus.Valid); + return executionPayload; + } + + private async Task BuildAndGetPayloadResultV3( + IEngineRpcModule rpc, + MergeTestBlockchain chain, + Hash256 headBlockHash, + Hash256 finalizedBlockHash, + Hash256 safeBlockHash, + ulong timestamp, + Hash256 random, + Address feeRecipient, + Withdrawal[]? withdrawals, + bool waitForBlockImprovement = true) + { + using SemaphoreSlim blockImprovementLock = new SemaphoreSlim(0); + + if (waitForBlockImprovement) + { + chain.PayloadPreparationService!.BlockImproved += (s, e) => + { + blockImprovementLock.Release(1); + }; + } + + ForkchoiceStateV1 forkchoiceState = new ForkchoiceStateV1(headBlockHash, finalizedBlockHash, safeBlockHash); + PayloadAttributes payloadAttributes = new PayloadAttributes + { + Timestamp = timestamp, + PrevRandao = random, + SuggestedFeeRecipient = feeRecipient, + ParentBeaconBlockRoot = Keccak.Zero, + Withdrawals = withdrawals, + }; + + ResultWrapper result = rpc.engine_forkchoiceUpdatedV3(forkchoiceState, payloadAttributes).Result; + string? payloadId = result.Data.PayloadId; + + if (waitForBlockImprovement) + await blockImprovementLock.WaitAsync(10000); + + ResultWrapper getPayloadResult = + await rpc.engine_getPayloadV4(Bytes.FromHexString(payloadId!)); + + return getPayloadResult.Data!.ExecutionPayload!; + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/BlockProduction/Boost/BoostBlockImprovementContext.cs b/src/Nethermind/Nethermind.Merge.Plugin/BlockProduction/Boost/BoostBlockImprovementContext.cs index 03ca0a9a2f6..e90d8d8401b 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/BlockProduction/Boost/BoostBlockImprovementContext.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/BlockProduction/Boost/BoostBlockImprovementContext.cs @@ -55,7 +55,7 @@ public BoostBlockImprovementContext(Block currentBestBlock, CurrentBestBlock = block; BlockFees = _feesTracer.Fees; _stateReader.TryGetAccount(parentHeader.StateRoot!, payloadAttributes.SuggestedFeeRecipient, out account); - await _boostRelay.SendPayload(new BoostExecutionPayloadV1 { Block = new ExecutionPayload(block), Profit = account.Balance - balanceBefore }, cancellationToken); + await _boostRelay.SendPayload(new BoostExecutionPayloadV1 { Block = ExecutionPayload.Create(block), Profit = account.Balance - balanceBefore }, cancellationToken); } return CurrentBestBlock; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 3b3e8c41c1a..4e33c0ce23a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -12,36 +12,20 @@ using Nethermind.Serialization.Rlp; using Nethermind.State.Proofs; using System.Text.Json.Serialization; +using Nethermind.Core.ConsensusRequests; namespace Nethermind.Merge.Plugin.Data; +public interface IExecutionPayloadFactory where TExecutionPayload : ExecutionPayload +{ + static abstract TExecutionPayload Create(Block block); +} + /// /// Represents an object mapping the ExecutionPayload structure of the beacon chain spec. /// -public class ExecutionPayload : IForkValidator, IExecutionPayloadParams +public class ExecutionPayload : IForkValidator, IExecutionPayloadParams, IExecutionPayloadFactory { - public ExecutionPayload() { } // Needed for tests - - public ExecutionPayload(Block block) - { - BlockHash = block.Hash!; - ParentHash = block.ParentHash!; - FeeRecipient = block.Beneficiary!; - StateRoot = block.StateRoot!; - BlockNumber = block.Number; - GasLimit = block.GasLimit; - GasUsed = block.GasUsed; - ReceiptsRoot = block.ReceiptsRoot!; - LogsBloom = block.Bloom!; - PrevRandao = block.MixHash ?? Keccak.Zero; - ExtraData = block.ExtraData!; - Timestamp = block.Timestamp; - BaseFeePerGas = block.BaseFeePerGas; - Withdrawals = block.Withdrawals; - - SetTransactions(block.Transactions); - } - public UInt256 BaseFeePerGas { get; set; } public Hash256 BlockHash { get; set; } = Keccak.Zero; @@ -92,6 +76,26 @@ public byte[][] Transactions public Withdrawal[]? Withdrawals { get; set; } + /// + /// Gets or sets a collection of as defined in + /// EIP-6110. + /// + public virtual Deposit[]? DepositRequests { get; set; } + + + /// + /// Gets or sets a collection of as defined in + /// EIP-7002. + /// + public virtual WithdrawalRequest[]? WithdrawalRequests { get; set; } + + /// + /// Gets or sets a collection of as defined in + /// EIP-7251. + /// + public virtual ConsolidationRequest[]? ConsolidationRequests { get; set; } + + /// /// Gets or sets as defined in /// EIP-4844. @@ -113,13 +117,38 @@ public byte[][] Transactions [JsonIgnore] public Hash256? ParentBeaconBlockRoot { get; set; } + public static ExecutionPayload Create(Block block) => Create(block); + + protected static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayload, new() + { + TExecutionPayload executionPayload = new() + { + BlockHash = block.Hash!, + ParentHash = block.ParentHash!, + FeeRecipient = block.Beneficiary!, + StateRoot = block.StateRoot!, + BlockNumber = block.Number, + GasLimit = block.GasLimit, + GasUsed = block.GasUsed, + ReceiptsRoot = block.ReceiptsRoot!, + LogsBloom = block.Bloom!, + PrevRandao = block.MixHash ?? Keccak.Zero, + ExtraData = block.ExtraData!, + Timestamp = block.Timestamp, + BaseFeePerGas = block.BaseFeePerGas, + Withdrawals = block.Withdrawals, + }; + executionPayload.SetTransactions(block.Transactions); + return executionPayload; + } + /// /// Creates the execution block from payload. /// /// When this method returns, contains the execution block. /// A total difficulty of the block. /// true if block created successfully; otherwise, false. - public virtual bool TryGetBlock(out Block? block, UInt256? totalDifficulty = null) + public virtual bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? totalDifficulty = null) { try { @@ -195,7 +224,7 @@ public void SetTransactions(params Transaction[] transactions) ExecutionPayload IExecutionPayloadParams.ExecutionPayload => this; - public virtual ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error) + public ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error) { if (spec.IsEip4844Enabled) { @@ -203,12 +232,13 @@ public virtual ValidationResult ValidateParams(IReleaseSpec spec, int version, o return ValidationResult.Fail; } - int actualVersion = this switch + if (spec.RequestsEnabled) { - { BlobGasUsed: not null } or { ExcessBlobGas: not null } or { ParentBeaconBlockRoot: not null } => 3, - { Withdrawals: not null } => 2, - _ => 1 - }; + error = "ExecutionPayloadV4 expected"; + return ValidationResult.Fail; + } + + var actualVersion = GetExecutionPayloadVersion(); error = actualVersion switch { @@ -220,6 +250,14 @@ public virtual ValidationResult ValidateParams(IReleaseSpec spec, int version, o return error is null ? ValidationResult.Success : ValidationResult.Fail; } + private int GetExecutionPayloadVersion() => this switch + { + { DepositRequests: not null, WithdrawalRequests: not null } => 4, + { BlobGasUsed: not null } or { ExcessBlobGas: not null } or { ParentBeaconBlockRoot: not null } => 3, + { Withdrawals: not null } => 2, + _ => 1 + }; + public virtual bool ValidateFork(ISpecProvider specProvider) => !specProvider.GetSpec(BlockNumber, Timestamp).IsEip4844Enabled; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV1Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV1Result.cs index 2b20e438649..f6733147ad1 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV1Result.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV1Result.cs @@ -13,7 +13,7 @@ namespace Nethermind.Merge.Plugin.Data; public class ExecutionPayloadBodyV1Result { - public ExecutionPayloadBodyV1Result(IList transactions, IList? withdrawals) + public ExecutionPayloadBodyV1Result(IReadOnlyList transactions, IReadOnlyList? withdrawals) { ArgumentNullException.ThrowIfNull(transactions); @@ -28,8 +28,8 @@ public ExecutionPayloadBodyV1Result(IList transactions, IList Transactions { get; set; } + public IReadOnlyList Transactions { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public IList? Withdrawals { get; set; } + public IReadOnlyList? Withdrawals { get; set; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs new file mode 100644 index 00000000000..d929fcf6fd8 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using System.Text.Json.Serialization; + +namespace Nethermind.Merge.Plugin.Data; + +public class ExecutionPayloadBodyV2Result : ExecutionPayloadBodyV1Result +{ + public ExecutionPayloadBodyV2Result( + IReadOnlyList transactions, + IReadOnlyList? withdrawals, + IReadOnlyList? deposits, + IReadOnlyList? withdrawalsRequests, + IReadOnlyList? consolidationRequests + ) + : base(transactions, withdrawals) + { + DepositRequests = deposits; + WithdrawalRequests = withdrawalsRequests; + ConsolidationRequests = consolidationRequests; + } + + /// + /// Deposit requests . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public IReadOnlyList? DepositRequests { get; set; } + + /// + /// Withdrawal requests . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public IReadOnlyList? WithdrawalRequests { get; set; } + + /// + /// Consolidation requests . + /// + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public IReadOnlyList? ConsolidationRequests { get; set; } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs index ff236bab63d..f05e355b183 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs @@ -1,8 +1,10 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -11,27 +13,29 @@ namespace Nethermind.Merge.Plugin.Data; /// /// Represents an object mapping the ExecutionPayloadV3 structure of the beacon chain spec. /// -public class ExecutionPayloadV3 : ExecutionPayload +public class ExecutionPayloadV3 : ExecutionPayload, IExecutionPayloadFactory { - public ExecutionPayloadV3() { } // Needed for tests - - public ExecutionPayloadV3(Block block) : base(block) + protected new static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayloadV3, new() { - ParentBeaconBlockRoot = block.ParentBeaconBlockRoot; - BlobGasUsed = block.BlobGasUsed; - ExcessBlobGas = block.ExcessBlobGas; + TExecutionPayload executionPayload = ExecutionPayload.Create(block); + executionPayload.ParentBeaconBlockRoot = block.ParentBeaconBlockRoot; + executionPayload.BlobGasUsed = block.BlobGasUsed; + executionPayload.ExcessBlobGas = block.ExcessBlobGas; + return executionPayload; } - public override bool TryGetBlock(out Block? block, UInt256? totalDifficulty = null) + public new static ExecutionPayloadV3 Create(Block block) => Create(block); + + public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? totalDifficulty = null) { if (!base.TryGetBlock(out block, totalDifficulty)) { return false; } - block!.Header.ParentBeaconBlockRoot = ParentBeaconBlockRoot; - block!.Header.BlobGasUsed = BlobGasUsed; - block!.Header.ExcessBlobGas = ExcessBlobGas; + block.Header.ParentBeaconBlockRoot = ParentBeaconBlockRoot; + block.Header.BlobGasUsed = BlobGasUsed; + block.Header.ExcessBlobGas = ExcessBlobGas; return true; } @@ -43,12 +47,12 @@ public override bool ValidateFork(ISpecProvider specProvider) => /// EIP-4844. /// [JsonRequired] - public override ulong? BlobGasUsed { get; set; } + public sealed override ulong? BlobGasUsed { get; set; } /// /// Gets or sets as defined in /// EIP-4844. /// [JsonRequired] - public override ulong? ExcessBlobGas { get; set; } + public sealed override ulong? ExcessBlobGas { get; set; } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs new file mode 100644 index 00000000000..37fd1941352 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.InteropServices.JavaScript; +using System.Text.Json.Serialization; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Int256; +using Nethermind.State.Proofs; + +namespace Nethermind.Merge.Plugin.Data; + +/// +/// Represents an object mapping the ExecutionPayloadV4 structure of the beacon chain spec. +/// +public class ExecutionPayloadV4 : ExecutionPayloadV3, IExecutionPayloadFactory +{ + protected new static TExecutionPayload Create(Block block) where TExecutionPayload : ExecutionPayloadV4, new() + { + TExecutionPayload executionPayload = ExecutionPayloadV3.Create(block); + ConsensusRequest[]? blockRequests = block.Requests; + if (blockRequests is null) + { + executionPayload.DepositRequests = Array.Empty(); + executionPayload.WithdrawalRequests = Array.Empty(); + executionPayload.ConsolidationRequests = Array.Empty(); + } + else + { + (executionPayload.DepositRequests, executionPayload.WithdrawalRequests, executionPayload.ConsolidationRequests) = blockRequests.SplitRequests(); + } + + return executionPayload; + } + + public new static ExecutionPayloadV4 Create(Block block) => Create(block); + + public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? totalDifficulty = null) + { + if (!base.TryGetBlock(out block, totalDifficulty)) + { + return false; + } + + var depositsLength = DepositRequests?.Length ?? 0; + var withdrawalRequestsLength = WithdrawalRequests?.Length ?? 0; + var consolidationRequestsLength = ConsolidationRequests?.Length ?? 0; + var requestsCount = depositsLength + withdrawalRequestsLength + consolidationRequestsLength; + if (requestsCount > 0) + { + var requests = new ConsensusRequest[requestsCount]; + int i = 0; + for (; i < depositsLength; ++i) + { + requests[i] = DepositRequests![i]; + } + + for (; i < depositsLength + withdrawalRequestsLength; ++i) + { + requests[i] = WithdrawalRequests![i - depositsLength]; + } + + for (; i < requestsCount; ++i) + { + requests[i] = ConsolidationRequests![i - depositsLength - withdrawalRequestsLength]; + } + + block.Body.Requests = requests; + block.Header.RequestsRoot = new RequestsTrie(requests).RootHash; + } + else + { + block.Body.Requests = Array.Empty(); + block.Header.RequestsRoot = Keccak.EmptyTreeHash; + } + + return true; + } + + public override bool ValidateFork(ISpecProvider specProvider) => + specProvider.GetSpec(BlockNumber, Timestamp).DepositsEnabled + && specProvider.GetSpec(BlockNumber, Timestamp).WithdrawalRequestsEnabled + && specProvider.GetSpec(BlockNumber, Timestamp).ConsolidationRequestsEnabled; + + /// + /// Gets or sets as defined in + /// EIP-6110. + /// + [JsonRequired] + public sealed override Deposit[]? DepositRequests { get; set; } + + /// + /// Gets or sets as defined in + /// EIP-7002. + /// + [JsonRequired] + public sealed override WithdrawalRequest[]? WithdrawalRequests { get; set; } + + /// + /// Gets or sets as defined in + /// EIP-7251. + /// + [JsonRequired] + public sealed override ConsolidationRequest[]? ConsolidationRequests { get; set; } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs index 78fffe38a86..70bc1ac1762 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs @@ -8,19 +8,16 @@ namespace Nethermind.Merge.Plugin.Data; -public class GetPayloadV2Result : IForkValidator +public class GetPayloadV2Result(Block block, UInt256 blockFees) + : IForkValidator where TVersionedExecutionPayload : ExecutionPayload, IExecutionPayloadParams, IExecutionPayloadFactory { - public GetPayloadV2Result(Block block, UInt256 blockFees) - { - BlockValue = blockFees; - ExecutionPayload = new(block); - } + public UInt256 BlockValue { get; } = blockFees; - public UInt256 BlockValue { get; } - - public virtual ExecutionPayload ExecutionPayload { get; } + public virtual TVersionedExecutionPayload ExecutionPayload { get; } = TVersionedExecutionPayload.Create(block); public bool ValidateFork(ISpecProvider specProvider) => ExecutionPayload.ValidateFork(specProvider); public override string ToString() => $"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}}}"; } + +public class GetPayloadV2Result(Block block, UInt256 blockFees) : GetPayloadV2Result(block, blockFees); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs index 492d81ad71e..d2603f396c8 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs @@ -6,20 +6,16 @@ namespace Nethermind.Merge.Plugin.Data; -public class GetPayloadV3Result : GetPayloadV2Result +public class GetPayloadV3Result(Block block, UInt256 blockFees, BlobsBundleV1 blobsBundle) + : GetPayloadV2Result(block, blockFees) + where TVersionedExecutionPayload : ExecutionPayloadV3, IExecutionPayloadParams, IExecutionPayloadFactory { - public GetPayloadV3Result(Block block, UInt256 blockFees, BlobsBundleV1 blobsBundle) : base(block, blockFees) - { - ExecutionPayload = new(block); - BlobsBundle = blobsBundle; - } - - public BlobsBundleV1 BlobsBundle { get; } - - public override ExecutionPayloadV3 ExecutionPayload { get; } + public BlobsBundleV1 BlobsBundle { get; } = blobsBundle; public bool ShouldOverrideBuilder { get; } public override string ToString() => $"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}, BlobsBundle blobs count: {BlobsBundle.Blobs.Length}, ShouldOverrideBuilder {ShouldOverrideBuilder}}}"; } + +public class GetPayloadV3Result(Block block, UInt256 blockFees, BlobsBundleV1 blobsBundle) : GetPayloadV3Result(block, blockFees, blobsBundle); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs new file mode 100644 index 00000000000..4f13cf363bf --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Int256; + +namespace Nethermind.Merge.Plugin.Data; + +public class GetPayloadV4Result(Block block, UInt256 blockFees, BlobsBundleV1 blobsBundle) : GetPayloadV3Result(block, blockFees, blobsBundle); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs index 3afb9a42338..46abda52dde 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs @@ -19,27 +19,22 @@ public interface IExecutionPayloadParams public enum ValidationResult : byte { Success, Fail, Invalid }; -public class ExecutionPayloadV3Params : IExecutionPayloadParams +public class ExecutionPayloadParams( + TVersionedExecutionPayload executionPayload, + byte[]?[] blobVersionedHashes, + Hash256? parentBeaconBlockRoot) + : IExecutionPayloadParams where TVersionedExecutionPayload : ExecutionPayload { - private readonly ExecutionPayloadV3 _executionPayload; - private readonly byte[]?[] _blobVersionedHashes; - private readonly Hash256? _parentBeaconBlockRoot; + public TVersionedExecutionPayload ExecutionPayload => executionPayload; - public ExecutionPayloadV3Params(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot) - { - _executionPayload = executionPayload; - _blobVersionedHashes = blobVersionedHashes; - _parentBeaconBlockRoot = parentBeaconBlockRoot; - } - - public ExecutionPayload ExecutionPayload => _executionPayload; + ExecutionPayload IExecutionPayloadParams.ExecutionPayload => ExecutionPayload; public ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error) { - Transaction[]? transactions = null; + Transaction[]? transactions; try { - transactions = _executionPayload.GetTransactions(); + transactions = executionPayload.GetTransactions(); } catch (RlpException rlpException) { @@ -52,19 +47,19 @@ public ValidationResult ValidateParams(IReleaseSpec spec, int version, out strin .Where(t => t.BlobVersionedHashes is not null) .SelectMany(t => t.BlobVersionedHashes!); - if (!FlattenHashesFromTransactions(transactions).SequenceEqual(_blobVersionedHashes, Bytes.NullableEqualityComparer)) + if (!FlattenHashesFromTransactions(transactions).SequenceEqual(blobVersionedHashes, Bytes.NullableEqualityComparer)) { error = "Blob versioned hashes do not match"; return ValidationResult.Invalid; } - if (_parentBeaconBlockRoot is null) + if (parentBeaconBlockRoot is null) { error = "Parent beacon block root must be set"; return ValidationResult.Fail; } - _executionPayload.ParentBeaconBlockRoot = new Hash256(_parentBeaconBlockRoot); + executionPayload.ParentBeaconBlockRoot = new Hash256(parentBeaconBlockRoot); error = null; return ValidationResult.Success; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs index 4a28ae4ac0d..cb2d46cfb07 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs @@ -19,7 +19,7 @@ public Task> engine_forkchoiceUpdatedV3 => ForkchoiceUpdated(forkchoiceState, payloadAttributes, EngineApiVersions.Cancun); public Task> engine_newPayloadV3(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot) => - NewPayload(new ExecutionPayloadV3Params(executionPayload, blobVersionedHashes, parentBeaconBlockRoot), EngineApiVersions.Cancun); + NewPayload(new ExecutionPayloadParams(executionPayload, blobVersionedHashes, parentBeaconBlockRoot), EngineApiVersions.Cancun); public async Task> engine_getPayloadV3(byte[] payloadId) => await _getPayloadHandlerV3.HandleAsync(payloadId); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Prague.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Prague.cs new file mode 100644 index 00000000000..f0f033ab4d5 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Prague.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Threading.Tasks; +using Nethermind.Consensus; +using Nethermind.Core.Crypto; +using Nethermind.JsonRpc; +using Nethermind.Merge.Plugin.Data; +using Nethermind.Merge.Plugin.handlers; +using Nethermind.Merge.Plugin.Handlers; + +namespace Nethermind.Merge.Plugin; + +public partial class EngineRpcModule : IEngineRpcModule +{ + private readonly IAsyncHandler _getPayloadHandlerV4; + + private readonly IAsyncHandler, IEnumerable> _executionGetPayloadBodiesByHashV2Handler; + private readonly IGetPayloadBodiesByRangeV2Handler _executionGetPayloadBodiesByRangeV2Handler; + + public Task> engine_newPayloadV4(ExecutionPayloadV4 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot) => + NewPayload(new ExecutionPayloadParams(executionPayload, blobVersionedHashes, parentBeaconBlockRoot), EngineApiVersions.Prague); + + public async Task> engine_getPayloadV4(byte[] payloadId) => + await _getPayloadHandlerV4.HandleAsync(payloadId); + + public Task>> engine_getPayloadBodiesByHashV2(IReadOnlyList blockHashes) + => _executionGetPayloadBodiesByHashV2Handler.HandleAsync(blockHashes); + + public Task>> engine_getPayloadBodiesByRangeV2(long start, long count) + => _executionGetPayloadBodiesByRangeV2Handler.Handle(start, count); +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs index 5b9162d1e1c..9db57d25681 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs @@ -14,7 +14,7 @@ namespace Nethermind.Merge.Plugin; public partial class EngineRpcModule : IEngineRpcModule { - private readonly IAsyncHandler, IEnumerable> _executionGetPayloadBodiesByHashV1Handler; + private readonly IAsyncHandler, IEnumerable> _executionGetPayloadBodiesByHashV1Handler; private readonly IGetPayloadBodiesByRangeV1Handler _executionGetPayloadBodiesByRangeV1Handler; private readonly IAsyncHandler _getPayloadHandlerV2; @@ -24,7 +24,7 @@ public Task> engine_forkchoiceUpdatedV2 public Task> engine_getPayloadV2(byte[] payloadId) => _getPayloadHandlerV2.HandleAsync(payloadId); - public Task>> engine_getPayloadBodiesByHashV1(IList blockHashes) + public Task>> engine_getPayloadBodiesByHashV1(IReadOnlyList blockHashes) => _executionGetPayloadBodiesByHashV1Handler.HandleAsync(blockHashes); public Task>> engine_getPayloadBodiesByRangeV1(long start, long count) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.cs index 24aaf85a38b..413fde7ea0a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.cs @@ -24,10 +24,13 @@ public EngineRpcModule( IAsyncHandler getPayloadHandlerV1, IAsyncHandler getPayloadHandlerV2, IAsyncHandler getPayloadHandlerV3, + IAsyncHandler getPayloadHandlerV4, IAsyncHandler newPayloadV1Handler, IForkchoiceUpdatedHandler forkchoiceUpdatedV1Handler, - IAsyncHandler, IEnumerable> executionGetPayloadBodiesByHashV1Handler, + IAsyncHandler, IEnumerable> executionGetPayloadBodiesByHashV1Handler, IGetPayloadBodiesByRangeV1Handler executionGetPayloadBodiesByRangeV1Handler, + IAsyncHandler, IEnumerable> executionGetPayloadBodiesByHashV2Handler, + IGetPayloadBodiesByRangeV2Handler executionGetPayloadBodiesByRangeV2Handler, IHandler transitionConfigurationHandler, IHandler, IEnumerable> capabilitiesHandler, ISpecProvider specProvider, @@ -38,10 +41,13 @@ public EngineRpcModule( _getPayloadHandlerV1 = getPayloadHandlerV1; _getPayloadHandlerV2 = getPayloadHandlerV2; _getPayloadHandlerV3 = getPayloadHandlerV3; + _getPayloadHandlerV4 = getPayloadHandlerV4; _newPayloadV1Handler = newPayloadV1Handler; _forkchoiceUpdatedV1Handler = forkchoiceUpdatedV1Handler; _executionGetPayloadBodiesByHashV1Handler = executionGetPayloadBodiesByHashV1Handler; _executionGetPayloadBodiesByRangeV1Handler = executionGetPayloadBodiesByRangeV1Handler; + _executionGetPayloadBodiesByHashV2Handler = executionGetPayloadBodiesByHashV2Handler; + _executionGetPayloadBodiesByRangeV2Handler = executionGetPayloadBodiesByRangeV2Handler; _transitionConfigurationHandler = transitionConfigurationHandler; _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); _gcKeeper = gcKeeper; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs index 729abe4ce86..32849aec183 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs @@ -46,6 +46,13 @@ public EngineRpcCapabilitiesProvider(ISpecProvider specProvider) _capabilities[nameof(IEngineRpcModule.engine_forkchoiceUpdatedV3)] = (spec.IsEip4844Enabled, spec.IsEip4844Enabled); _capabilities[nameof(IEngineRpcModule.engine_newPayloadV3)] = (spec.IsEip4844Enabled, spec.IsEip4844Enabled); #endregion + + #region Prague + _capabilities[nameof(IEngineRpcModule.engine_getPayloadV4)] = (spec.RequestsEnabled, spec.RequestsEnabled); + _capabilities[nameof(IEngineRpcModule.engine_newPayloadV4)] = (spec.RequestsEnabled, spec.RequestsEnabled); + _capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByHashV2)] = (spec.RequestsEnabled, false); + _capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByRangeV2)] = (spec.RequestsEnabled, false); + #endregion } return _capabilities; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV1Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV1Handler.cs index 0e3f8cd63d2..10ea8cc9205 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV1Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV1Handler.cs @@ -12,11 +12,11 @@ namespace Nethermind.Merge.Plugin.Handlers; -public class GetPayloadBodiesByHashV1Handler : IAsyncHandler, IEnumerable> +public class GetPayloadBodiesByHashV1Handler : IAsyncHandler, IEnumerable> { - private const int MaxCount = 1024; - private readonly IBlockTree _blockTree; - private readonly ILogger _logger; + protected const int MaxCount = 1024; + protected readonly IBlockTree _blockTree; + protected readonly ILogger _logger; public GetPayloadBodiesByHashV1Handler(IBlockTree blockTree, ILogManager logManager) { @@ -24,27 +24,36 @@ public GetPayloadBodiesByHashV1Handler(IBlockTree blockTree, ILogManager logMana _logger = logManager.GetClassLogger(); } - public Task>> HandleAsync(IList blockHashes) + protected bool CheckHashCount(IReadOnlyList blockHashes, out string? error) { if (blockHashes.Count > MaxCount) { - var error = $"The number of requested bodies must not exceed {MaxCount}"; + error = $"The number of requested bodies must not exceed {MaxCount}"; - if (_logger.IsError) _logger.Error($"{nameof(GetPayloadBodiesByHashV1Handler)}: {error}"); + if (_logger.IsError) _logger.Error($"{GetType().Name}: {error}"); + return false; + } + error = null; + return true; + } - return ResultWrapper>.Fail(error, MergeErrorCodes.TooLargeRequest); + public Task>> HandleAsync(IReadOnlyList blockHashes) + { + if (!CheckHashCount(blockHashes, out string? error)) + { + return ResultWrapper>.Fail(error!, MergeErrorCodes.TooLargeRequest); } return Task.FromResult(ResultWrapper>.Success(GetRequests(blockHashes))); } - private IEnumerable GetRequests(IList blockHashes) + private IEnumerable GetRequests(IReadOnlyList blockHashes) { for (int i = 0; i < blockHashes.Count; i++) { Block? block = _blockTree.FindBlock(blockHashes[i]); - yield return (block is null ? null : new ExecutionPayloadBodyV1Result(block.Transactions, block.Withdrawals)); + yield return block is null ? null : new ExecutionPayloadBodyV1Result(block.Transactions, block.Withdrawals); } yield break; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs new file mode 100644 index 00000000000..61913d4caad --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.JsonRpc; +using Nethermind.Logging; +using Nethermind.Merge.Plugin.Data; + +namespace Nethermind.Merge.Plugin.Handlers; + + +public class GetPayloadBodiesByHashV2Handler(IBlockTree blockTree, ILogManager logManager) : GetPayloadBodiesByHashV1Handler(blockTree, logManager), IAsyncHandler, IEnumerable> +{ + public new Task>> HandleAsync(IReadOnlyList blockHashes) + { + if (!CheckHashCount(blockHashes, out string? error)) + { + return ResultWrapper>.Fail(error!, MergeErrorCodes.TooLargeRequest); + } + + return Task.FromResult(ResultWrapper>.Success(GetRequests(blockHashes))); + } + + private IEnumerable GetRequests(IReadOnlyList blockHashes) + { + for (int i = 0; i < blockHashes.Count; i++) + { + Block? block = _blockTree.FindBlock(blockHashes[i]); + + if (block is null) + { + yield return null; + continue; + } + + (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) = block!.Requests.SplitRequests(); + yield return new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests, consolidationRequests); + } + + yield break; + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs index 0ca3a7e5af7..c3098efb039 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV1Handler.cs @@ -13,10 +13,10 @@ namespace Nethermind.Merge.Plugin.Handlers; public class GetPayloadBodiesByRangeV1Handler : IGetPayloadBodiesByRangeV1Handler { - private const int MaxCount = 1024; + protected const int MaxCount = 1024; - private readonly IBlockTree _blockTree; - private readonly ILogger _logger; + protected readonly IBlockTree _blockTree; + protected readonly ILogger _logger; public GetPayloadBodiesByRangeV1Handler(IBlockTree blockTree, ILogManager logManager) { @@ -24,24 +24,37 @@ public GetPayloadBodiesByRangeV1Handler(IBlockTree blockTree, ILogManager logMan _logger = logManager.GetClassLogger(); } - public Task>> Handle(long start, long count) + protected bool CheckRangeCount(long start, long count, out string? error, out int errorCode) { if (start < 1 || count < 1) { - var error = $"'{nameof(start)}' and '{nameof(count)}' must be positive numbers"; + error = $"'{nameof(start)}' and '{nameof(count)}' must be positive numbers"; - if (_logger.IsError) _logger.Error($"{nameof(GetPayloadBodiesByRangeV1Handler)}: ${error}"); + if (_logger.IsError) _logger.Error($"{GetType().Name}: ${error}"); - return ResultWrapper>.Fail(error, ErrorCodes.InvalidParams); + errorCode = ErrorCodes.InvalidParams; + return false; } if (count > MaxCount) { - var error = $"The number of requested bodies must not exceed {MaxCount}"; + error = $"The number of requested bodies must not exceed {MaxCount}"; + + if (_logger.IsError) _logger.Error($"{GetType().Name}: {error}"); - if (_logger.IsError) _logger.Error($"{nameof(GetPayloadBodiesByRangeV1Handler)}: {error}"); + errorCode = MergeErrorCodes.TooLargeRequest; + return false; + } + error = null; + errorCode = 0; + return true; + } - return ResultWrapper>.Fail(error, MergeErrorCodes.TooLargeRequest); + public Task>> Handle(long start, long count) + { + if (!CheckRangeCount(start, count, out string? error, out int errorCode)) + { + return ResultWrapper>.Fail(error!, errorCode); } return ResultWrapper>.Success(GetRequests(start, count)); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs new file mode 100644 index 00000000000..221fc6c2a2a --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; +using Nethermind.JsonRpc; +using Nethermind.Logging; +using Nethermind.Merge.Plugin.Data; +using Nethermind.Merge.Plugin.Handlers; + +namespace Nethermind.Merge.Plugin.handlers; + +public class GetPayloadBodiesByRangeV2Handler(IBlockTree blockTree, ILogManager logManager) : GetPayloadBodiesByRangeV1Handler(blockTree, logManager), IGetPayloadBodiesByRangeV2Handler +{ + public new Task>> Handle(long start, long count) + { + if (!CheckRangeCount(start, count, out string? error, out int errorCode)) + { + return ResultWrapper>.Fail(error!, errorCode); + } + + return Task.FromResult(ResultWrapper>.Success(GetRequests(start, count))); + } + + + private IEnumerable GetRequests(long start, long count) + { + var headNumber = _blockTree.Head?.Number ?? 0; + for (long i = start, c = Math.Min(start + count - 1, headNumber); i <= c; i++) + { + Block? block = _blockTree.FindBlock(i); + + if (block is null) + { + yield return null; + continue; + } + + (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) = block!.Requests.SplitRequests(); + yield return new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests, consolidationRequests); + } + + yield break; + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs index 20a27945f9b..2cd2defba3e 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs @@ -23,13 +23,9 @@ namespace Nethermind.Merge.Plugin.Handlers; /// a payload has been cancelled due to the timeout then execution client must respond with error message. /// Execution client may stop the building process with the corresponding payload_id value after serving this call. /// -public class GetPayloadV1Handler : GetPayloadHandlerBase +public class GetPayloadV1Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) + : GetPayloadHandlerBase(1, payloadPreparationService, specProvider, logManager) { - public GetPayloadV1Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) : base( - 1, payloadPreparationService, specProvider, logManager) - { - } - protected override ExecutionPayload GetPayloadResultFromBlock(IBlockProductionContext context) => - new(context.CurrentBestBlock!); + ExecutionPayload.Create(context.CurrentBestBlock!); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs new file mode 100644 index 00000000000..3071c32f39a --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Specs; +using Nethermind.Logging; +using Nethermind.Merge.Plugin.BlockProduction; +using Nethermind.Merge.Plugin.Data; + +namespace Nethermind.Merge.Plugin.Handlers; + +/// +/// +/// engine_getpayloadv3 +/// +public class GetPayloadV4Handler : GetPayloadHandlerBase +{ + public GetPayloadV4Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) : base( + 4, payloadPreparationService, specProvider, logManager) + { + } + + protected override GetPayloadV4Result GetPayloadResultFromBlock(IBlockProductionContext context) => + new(context.CurrentBestBlock!, context.BlockFees, new BlobsBundleV1(context.CurrentBestBlock!)); +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IGetPayloadBodiesByRangeV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IGetPayloadBodiesByRangeV2Handler.cs new file mode 100644 index 00000000000..9cd35c7d626 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IGetPayloadBodiesByRangeV2Handler.cs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Threading.Tasks; +using Nethermind.JsonRpc; +using Nethermind.Merge.Plugin.Data; + +namespace Nethermind.Merge.Plugin.Handlers; + +public interface IGetPayloadBodiesByRangeV2Handler +{ + Task>> Handle(long start, long count); +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Prague.cs b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Prague.cs new file mode 100644 index 00000000000..7d62b4021b0 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Prague.cs @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Threading.Tasks; +using Nethermind.Consensus.Producers; +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 = "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_newPayloadV4(ExecutionPayloadV4 executionPayload, byte[]?[] blobVersionedHashes, Hash256? parentBeaconBlockRoot); + + [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)] + public Task> engine_getPayloadV4(byte[] payloadId); + + [JsonRpcMethod( + Description = "Returns an array of execution payload bodies for the list of provided block hashes.", + IsSharable = true, + IsImplemented = true)] + Task>> engine_getPayloadBodiesByHashV2(IReadOnlyList blockHashes); + + [JsonRpcMethod( + Description = "Returns an array of execution payload bodies for the provided number range", + IsSharable = true, + IsImplemented = true)] + Task>> engine_getPayloadBodiesByRangeV2(long start, long count); +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Shanghai.cs b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Shanghai.cs index c1b63e3d301..bde3c4c1146 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Shanghai.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Shanghai.cs @@ -29,7 +29,7 @@ public partial interface IEngineRpcModule : IRpcModule Description = "Returns an array of execution payload bodies for the list of provided block hashes.", IsSharable = true, IsImplemented = true)] - Task>> engine_getPayloadBodiesByHashV1(IList blockHashes); + Task>> engine_getPayloadBodiesByHashV1(IReadOnlyList blockHashes); [JsonRpcMethod( Description = "Returns an array of execution payload bodies for the provided number range", diff --git a/src/Nethermind/Nethermind.Merge.Plugin/InvalidChainTracker/InvalidBlockInterceptor.cs b/src/Nethermind/Nethermind.Merge.Plugin/InvalidChainTracker/InvalidBlockInterceptor.cs index 87cbb58f650..2c6dc8090b6 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/InvalidChainTracker/InvalidBlockInterceptor.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/InvalidChainTracker/InvalidBlockInterceptor.cs @@ -5,6 +5,7 @@ using Nethermind.Core; using Nethermind.Logging; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.Marshalling; namespace Nethermind.Merge.Plugin.InvalidChainTracker; @@ -148,7 +149,10 @@ private static bool ShouldNotTrackInvalidation(Block block) if (!BlockValidator.ValidateUnclesHashMatches(block, out _)) return true; - return !BlockValidator.ValidateWithdrawalsHashMatches(block, out _); + if (!BlockValidator.ValidateWithdrawalsHashMatches(block, out _)) + return true; + + return !BlockValidator.ValidateRequestsHashMatches(block, out _); } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs index 6bcc5eb7990..cfcafbd6866 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs @@ -26,6 +26,7 @@ using Nethermind.Merge.Plugin.BlockProduction; using Nethermind.Merge.Plugin.BlockProduction.Boost; using Nethermind.Merge.Plugin.GC; +using Nethermind.Merge.Plugin.handlers; using Nethermind.Merge.Plugin.Handlers; using Nethermind.Merge.Plugin.InvalidChainTracker; using Nethermind.Merge.Plugin.Synchronization; @@ -320,6 +321,7 @@ public Task InitRpcModules() new GetPayloadV1Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new GetPayloadV2Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new GetPayloadV3Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), + new GetPayloadV4Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new NewPayloadHandler( _api.BlockValidator, _api.BlockTree, @@ -352,6 +354,8 @@ public Task InitRpcModules() _api.Config().SimulateBlockProduction), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), + new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), + new GetPayloadBodiesByRangeV2Handler(_api.BlockTree, _api.LogManager), new ExchangeTransitionConfigurationV1Handler(_poSSwitcher, _api.LogManager), new ExchangeCapabilitiesHandler(_api.RpcCapabilitiesProvider, _api.LogManager), _api.SpecProvider, diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/ChainLevelHelper.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/ChainLevelHelper.cs index 3aed5334f49..9a3f7c1ca30 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/ChainLevelHelper.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/ChainLevelHelper.cs @@ -159,7 +159,7 @@ public bool TrySetNextBlocks(int maxCount, BlockDownloadContext context) { Block? block = _blockTree.FindBlock(hashesToRequest[i], BlockTreeLookupOptions.None); if (block is null) return false; - BlockBody blockBody = new(block.Transactions, block.Uncles, block?.Withdrawals); + BlockBody blockBody = new(block.Transactions, block.Uncles, block?.Withdrawals, block?.Requests); context.SetBody(i + offset, blockBody); } diff --git a/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs b/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs index d1542d166db..943c453b71c 100644 --- a/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs @@ -188,6 +188,7 @@ protected override BlockProcessor CreateBlockProcessor() State, ReceiptStorage, new BlockhashStore(SpecProvider, State), + TxProcessor, LogManager); _tracerFactory = new TracerFactory( diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs index d4bba370b4f..15f100c9136 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs @@ -5,6 +5,7 @@ using DotNetty.Buffers; using Nethermind.Core; using Nethermind.Core.Buffers; +using Nethermind.Core.ConsensusRequests; using Nethermind.Serialization.Rlp; namespace Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages @@ -58,6 +59,7 @@ private class BlockBodyDecoder : IRlpValueDecoder private readonly TxDecoder _txDecoder = new(); private readonly HeaderDecoder _headerDecoder = new(); private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); + private readonly ConsensusRequestDecoder _requestsDecoder = new(); public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) { @@ -68,6 +70,12 @@ public int GetBodyLength(BlockBody b) { if (b.Withdrawals is not null) { + if (b.Requests is not null) + { + return Rlp.LengthOfSequence(GetTxLength(b.Transactions)) + + Rlp.LengthOfSequence(GetUnclesLength(b.Uncles)) + Rlp.LengthOfSequence(GetWithdrawalsLength(b.Withdrawals)) + + Rlp.LengthOfSequence(GetRequestsLength(b.Requests)); + } return Rlp.LengthOfSequence(GetTxLength(b.Transactions)) + Rlp.LengthOfSequence(GetUnclesLength(b.Uncles)) + Rlp.LengthOfSequence(GetWithdrawalsLength(b.Withdrawals)); } @@ -90,6 +98,11 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) return withdrawals.Sum(t => _withdrawalDecoderDecoder.GetLength(t, RlpBehaviors.None)); } + private int GetRequestsLength(ConsensusRequest[] requests) + { + return requests.Sum(t => _requestsDecoder.GetLength(t, RlpBehaviors.None)); + } + public BlockBody? Decode(ref Rlp.ValueDecoderContext ctx, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { int sequenceLength = ctx.ReadSequenceLength(); @@ -104,12 +117,18 @@ private int GetWithdrawalsLength(Withdrawal[] withdrawals) Transaction[] transactions = ctx.DecodeArray(_txDecoder); BlockHeader[] uncles = ctx.DecodeArray(_headerDecoder); Withdrawal[]? withdrawals = null; + ConsensusRequest[]? requests = null; if (ctx.PeekNumberOfItemsRemaining(startingPosition + sequenceLength, 1) > 0) { withdrawals = ctx.DecodeArray(_withdrawalDecoderDecoder); } - return new BlockBody(transactions, uncles, withdrawals); + if (ctx.PeekNumberOfItemsRemaining(startingPosition + sequenceLength, 1) > 0) + { + requests = ctx.DecodeArray(_requestsDecoder); + } + + return new BlockBody(transactions, uncles, withdrawals, requests); } public void Serialize(RlpStream stream, BlockBody body) @@ -135,6 +154,15 @@ public void Serialize(RlpStream stream, BlockBody body) stream.Encode(withdrawal); } } + + if (body.Requests is not null) + { + stream.StartSequence(GetRequestsLength(body.Requests)); + foreach (ConsensusRequest? request in body.Requests) + { + stream.Encode(request); + } + } } } } diff --git a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs index 92ab38d4aad..738c90d4ddc 100644 --- a/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs +++ b/src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs @@ -107,6 +107,7 @@ protected override BlockProcessor CreateBlockProcessor(BlockCachePreWarmer? preW new BlockhashStore(_api.SpecProvider, _api.WorldState), _api.LogManager, _api.SpecHelper, + _api.TransactionProcessor, contractRewriter, new BlockProductionWithdrawalProcessor(new NullWithdrawalProcessor()), preWarmer: preWarmer); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBlockProcessor.cs b/src/Nethermind/Nethermind.Optimism/OptimismBlockProcessor.cs index 385e7217e3c..2c45d0404ad 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBlockProcessor.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBlockProcessor.cs @@ -11,6 +11,7 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Logging; using Nethermind.State; @@ -30,12 +31,13 @@ public OptimismBlockProcessor( IBlockhashStore? blockhashStore, ILogManager? logManager, IOptimismSpecHelper opSpecHelper, + ITransactionProcessor txProcessor, Create2DeployerContractRewriter contractRewriter, IWithdrawalProcessor? withdrawalProcessor = null, IBlockCachePreWarmer? preWarmer = null) : base(specProvider, blockValidator, rewardCalculator, blockTransactionsExecutor, - stateProvider, receiptStorage, blockhashStore, logManager, withdrawalProcessor, - ReceiptsRootCalculator.Instance, preWarmer) + stateProvider, receiptStorage, blockhashStore, txProcessor, logManager, withdrawalProcessor, + receiptsRootCalculator: ReceiptsRootCalculator.Instance, preWarmer: preWarmer) { ArgumentNullException.ThrowIfNull(stateProvider); _contractRewriter = contractRewriter; diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBlockProducerEnvFactory.cs b/src/Nethermind/Nethermind.Optimism/OptimismBlockProducerEnvFactory.cs index 1069b90a688..a8b548daed9 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBlockProducerEnvFactory.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBlockProducerEnvFactory.cs @@ -79,6 +79,7 @@ protected override BlockProcessor CreateBlockProcessor( new BlockhashStore(specProvider, readOnlyTxProcessingEnv.WorldState), logManager, specHelper, + readOnlyTxProcessingEnv.TransactionProcessor, new Create2DeployerContractRewriter(specHelper, _specProvider, _blockTree), new BlockProductionWithdrawalProcessor(new WithdrawalProcessor(readOnlyTxProcessingEnv.WorldState, logManager))); } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 997d05beb7c..23e4b9faaa7 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -27,6 +27,9 @@ using Nethermind.Specs.ChainSpecStyle; using Nethermind.Serialization.Rlp; using Nethermind.Optimism.Rpc; +using Nethermind.Core; +using Nethermind.JsonRpc.Modules.Eth; +using Nethermind.Merge.Plugin.handlers; namespace Nethermind.Optimism; @@ -224,6 +227,7 @@ public async Task InitRpcModules() new GetPayloadV1Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new GetPayloadV2Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new GetPayloadV3Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), + new GetPayloadV4Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager), new NewPayloadHandler( _api.BlockValidator, _api.BlockTree, @@ -256,6 +260,8 @@ public async Task InitRpcModules() _api.Config().SimulateBlockProduction), new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager), new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager), + new GetPayloadBodiesByHashV2Handler(_api.BlockTree, _api.LogManager), + new GetPayloadBodiesByRangeV2Handler(_api.BlockTree, _api.LogManager), new ExchangeTransitionConfigurationV1Handler(_api.PoSSwitcher, _api.LogManager), new ExchangeCapabilitiesHandler(_api.RpcCapabilitiesProvider, _api.LogManager), _api.SpecProvider, diff --git a/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs b/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs index 38d3a9071b0..d3e95e82ef1 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismReceiptStorageDecoder.cs @@ -311,7 +311,7 @@ public int GetLength(OptimismTxReceipt item, RlpBehaviors rlpBehaviors) TxReceipt IRlpStreamDecoder.Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors) { - return Decode(rlpStream, rlpBehaviors); ; + return Decode(rlpStream, rlpBehaviors); } public void Encode(RlpStream stream, TxReceipt item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Optimism/ReadOnlyChainProcessingEnv.cs b/src/Nethermind/Nethermind.Optimism/ReadOnlyChainProcessingEnv.cs index 96ee2ae5a7d..5e53485215b 100644 --- a/src/Nethermind/Nethermind.Optimism/ReadOnlyChainProcessingEnv.cs +++ b/src/Nethermind/Nethermind.Optimism/ReadOnlyChainProcessingEnv.cs @@ -65,6 +65,7 @@ IBlockProcessor.IBlockTransactionsExecutor transactionsExecutor new BlockhashStore(specProvider, scope.WorldState), logManager, opSpecHelper, + scope.TransactionProcessor, contractRewriter, withdrawalProcessor); } diff --git a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json index 3db3149a77d..da27c06e51c 100644 --- a/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json +++ b/src/Nethermind/Nethermind.Runner/Properties/launchSettings.json @@ -28,7 +28,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "Gnosis": { + "Gnosis": { "commandName": "Project", "commandLineArgs": "-c gnosis -dd .data", "environmentVariables": { @@ -93,7 +93,7 @@ }, "Holesky": { "commandName": "Project", - "commandLineArgs": "-c holesky -dd .data", + "commandLineArgs": "-c holesky -dd .data --JsonRpc.UnsecureDevNoRpcAuthentication true", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/src/Nethermind/Nethermind.Runner/configs/volta.cfg b/src/Nethermind/Nethermind.Runner/configs/volta.cfg index 3f1549a91cf..837c380940e 100644 --- a/src/Nethermind/Nethermind.Runner/configs/volta.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/volta.cfg @@ -33,4 +33,5 @@ "Merge": { "Enabled": false } -} \ No newline at end of file +} + diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 71a5256baf1..167d7ca4b2f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -5,6 +5,7 @@ using System.Buffers; using System.Collections.Generic; using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; namespace Nethermind.Serialization.Rlp { @@ -13,6 +14,7 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder private readonly HeaderDecoder _headerDecoder = new(); private readonly TxDecoder _txDecoder = new(); private readonly WithdrawalDecoder _withdrawalDecoder = new(); + private readonly ConsensusRequestDecoder _consensusRequestsDecoder = new(); public Block? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { @@ -52,8 +54,21 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder rlpStream.Check(unclesCheck); - List withdrawals = null; + List? withdrawals = DecodeWithdrawals(rlpStream, blockCheck); + List? requests = DecodeRequests(rlpStream, blockCheck); + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) + { + rlpStream.Check(blockCheck); + } + + return new(header, transactions, uncleHeaders, withdrawals, requests); + } + + + private List? DecodeWithdrawals(RlpStream rlpStream, int blockCheck) + { + List? withdrawals = null; if (rlpStream.Position != blockCheck) { bool lengthWasRead = true; @@ -81,15 +96,44 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder } } - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) + return withdrawals; + } + + private List? DecodeRequests(RlpStream rlpStream, int blockCheck) + { + List? requests = null; + if (rlpStream.Position != blockCheck) { - rlpStream.Check(blockCheck); + bool lengthWasRead = true; + try + { + rlpStream.PeekNextRlpLength(); + } + catch + { + lengthWasRead = false; + } + + if (lengthWasRead) + { + int requestsLength = rlpStream.ReadSequenceLength(); + int requestsCheck = rlpStream.Position + requestsLength; + requests = new(); + + while (rlpStream.Position < requestsCheck) + { + requests.Add(Rlp.Decode(rlpStream)); + } + + rlpStream.Check(requestsCheck); + } } - return new(header, transactions, uncleHeaders, withdrawals); + return requests; } - private (int Total, int Txs, int Uncles, int? Withdrawals) GetContentLength(Block item, RlpBehaviors rlpBehaviors) + + private (int Total, int Txs, int Uncles, int? Withdrawals, int? Requests) GetContentLength(Block item, RlpBehaviors rlpBehaviors) { int contentLength = _headerDecoder.GetLength(item.Header, rlpBehaviors); @@ -108,7 +152,16 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder contentLength += Rlp.LengthOfSequence(withdrawalsLength.Value); } - return (contentLength, txLength, unclesLength, withdrawalsLength); + int? consensusRequestsLength = null; + if (item.Requests is not null) + { + consensusRequestsLength = GetConsensusRequestsLength(item, rlpBehaviors); + + if (consensusRequestsLength.HasValue) + contentLength += Rlp.LengthOfSequence(consensusRequestsLength.Value); + } + + return (contentLength, txLength, unclesLength, withdrawalsLength, consensusRequestsLength); } private int GetUnclesLength(Block item, RlpBehaviors rlpBehaviors) @@ -148,6 +201,21 @@ private int GetTxLength(Block item, RlpBehaviors rlpBehaviors) return withdrawalLength; } + private int? GetConsensusRequestsLength(Block item, RlpBehaviors rlpBehaviors) + { + if (item.Requests is null) + return null; + + var consensusRequestsLength = 0; + + for (int i = 0, count = item.Requests.Length; i < count; i++) + { + consensusRequestsLength += _consensusRequestsDecoder.GetLength(item.Requests[i], rlpBehaviors); + } + + return consensusRequestsLength; + } + public int GetLength(Block? item, RlpBehaviors rlpBehaviors) { if (item is null) @@ -191,7 +259,20 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) decoderContext.Check(unclesCheck); - List withdrawals = null; + List? withdrawals = DecodeWithdrawals(ref decoderContext, blockCheck); + List? requests = DecodeRequests(ref decoderContext, blockCheck); + + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) + { + decoderContext.Check(blockCheck); + } + + return new(header, transactions, uncleHeaders, withdrawals, requests); + } + + private List? DecodeWithdrawals(ref Rlp.ValueDecoderContext decoderContext, int blockCheck) + { + List? withdrawals = null; if (decoderContext.Position != blockCheck) { @@ -207,12 +288,28 @@ public int GetLength(Block? item, RlpBehaviors rlpBehaviors) decoderContext.Check(withdrawalsCheck); } - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) + return withdrawals; + } + + private List? DecodeRequests(ref Rlp.ValueDecoderContext decoderContext, int blockCheck) + { + List? requests = null; + + if (decoderContext.Position != blockCheck) { - decoderContext.Check(blockCheck); + int requestsLength = decoderContext.ReadSequenceLength(); + int requestsCheck = decoderContext.Position + requestsLength; + requests = new(); + + while (decoderContext.Position < requestsCheck) + { + requests.Add(Rlp.Decode(ref decoderContext)); + } + + decoderContext.Check(requestsCheck); } - return new(header, transactions, uncleHeaders, withdrawals); + return requests; } public Rlp Encode(Block? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) @@ -235,7 +332,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? requestsLength) = GetContentLength(item, rlpBehaviors); stream.StartSequence(contentLength); stream.Encode(item.Header); stream.StartSequence(txsLength); @@ -259,6 +356,16 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl stream.Encode(item.Withdrawals[i]); } } + + if (requestsLength.HasValue) + { + stream.StartSequence(requestsLength.Value); + + for (int i = 0; i < item.Requests.Length; i++) + { + stream.Encode(item.Requests[i]); + } + } } public static ReceiptRecoveryBlock? DecodeToReceiptRecoveryBlock(MemoryManager? memoryManager, Memory memory, RlpBehaviors rlpBehaviors) @@ -287,6 +394,10 @@ public void Encode(RlpStream stream, Block? item, RlpBehaviors rlpBehaviors = Rl { decoderContext.SkipItem(); // Skip withdrawals } + if (decoderContext.Position != blockCheck) + { + decoderContext.SkipItem(); // Skip requests + } if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) { decoderContext.Check(blockCheck); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs new file mode 100644 index 00000000000..617f7423bf0 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Serialization.Rlp; + +public class ConsensusRequestDecoder : IRlpStreamDecoder, IRlpValueDecoder, IRlpObjectDecoder +{ + private readonly WithdrawalRequestDecoder _withdrawalRequestDecoder = new(); + private readonly DepositDecoder _depositDecoder = new(); + private readonly ConsolidationRequestDecoder _consolidationRequestDecoder = new(); + public int GetContentLength(ConsensusRequest item, RlpBehaviors rlpBehaviors) + { + int length = item.Type switch + { + ConsensusRequestsType.ConsolidationRequest => _consolidationRequestDecoder.GetContentLength((ConsolidationRequest)item, rlpBehaviors), + ConsensusRequestsType.WithdrawalRequest => _withdrawalRequestDecoder.GetContentLength((WithdrawalRequest)item, rlpBehaviors), + ConsensusRequestsType.Deposit => _depositDecoder.GetContentLength((Deposit)item, rlpBehaviors), + _ => throw new RlpException($"Unsupported consensus request type {item.Type}") + }; + return length; + } + + public int GetLength(ConsensusRequest item, RlpBehaviors rlpBehaviors) + { + int length = item.Type switch + { + ConsensusRequestsType.ConsolidationRequest => _consolidationRequestDecoder.GetLength((ConsolidationRequest)item, rlpBehaviors), + ConsensusRequestsType.WithdrawalRequest => _withdrawalRequestDecoder.GetLength((WithdrawalRequest)item, rlpBehaviors), + ConsensusRequestsType.Deposit => _depositDecoder.GetLength((Deposit)item, rlpBehaviors), + _ => throw new RlpException($"Unsupported consensus request type {item.Type}") + }; + + if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping) + { + return Rlp.LengthOf((byte)item.Type) + length; + } + + return Rlp.LengthOfSequence(Rlp.LengthOf((byte)item.Type) + length); + } + + public ConsensusRequest? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (rlpStream.IsNextItemNull()) + { + rlpStream.ReadByte(); + return null; + } + + if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None) + { + rlpStream.ReadPrefixAndContentLength(); + } + + ConsensusRequestsType consensusRequestsType = (ConsensusRequestsType)rlpStream.ReadByte(); + + ConsensusRequest result = consensusRequestsType switch + { + ConsensusRequestsType.ConsolidationRequest => _consolidationRequestDecoder.Decode(rlpStream, rlpBehaviors), + ConsensusRequestsType.WithdrawalRequest => _withdrawalRequestDecoder.Decode(rlpStream, rlpBehaviors), + ConsensusRequestsType.Deposit => _depositDecoder.Decode(rlpStream, rlpBehaviors), + + _ => throw new RlpException($"Unsupported consensus request type {consensusRequestsType}") + }; + + return result; + } + + public ConsensusRequest Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (decoderContext.IsNextItemNull()) + { + decoderContext.ReadByte(); + return null; + } + + if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None) + { + decoderContext.ReadPrefixAndContentLength(); + } + + ConsensusRequestsType consensusRequestsType = (ConsensusRequestsType)decoderContext.ReadByte(); + + ConsensusRequest result = consensusRequestsType switch + { + ConsensusRequestsType.ConsolidationRequest => _consolidationRequestDecoder.Decode(ref decoderContext, rlpBehaviors), + ConsensusRequestsType.WithdrawalRequest => _withdrawalRequestDecoder.Decode(ref decoderContext, rlpBehaviors), + ConsensusRequestsType.Deposit => _depositDecoder.Decode(ref decoderContext, rlpBehaviors), + _ => throw new RlpException($"Unsupported consensus request type {consensusRequestsType}") + }; + + return result; + } + + public void Encode(RlpStream stream, ConsensusRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int contentLength = GetContentLength(item, rlpBehaviors); + int sequenceLength = Rlp.LengthOfSequence(contentLength); + + if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None) + { + stream.StartByteArray(sequenceLength + 1, false); + } + + stream.WriteByte((byte)item.Type); + switch (item.Type) + { + case ConsensusRequestsType.ConsolidationRequest: + _consolidationRequestDecoder.Encode(stream, (ConsolidationRequest)item, rlpBehaviors); + break; + case ConsensusRequestsType.WithdrawalRequest: + _withdrawalRequestDecoder.Encode(stream, (WithdrawalRequest)item, rlpBehaviors); + break; + case ConsensusRequestsType.Deposit: + _depositDecoder.Encode(stream, (Deposit)item, rlpBehaviors); + break; + } + } + + public Rlp Encode(ConsensusRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + RlpStream rlpStream = new RlpStream(GetLength(item, rlpBehaviors)); + Encode(rlpStream, item, rlpBehaviors); + return new Rlp(rlpStream.Data.ToArray()); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs new file mode 100644 index 00000000000..3e978a7e02e --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Serialization.Rlp; + +public class ConsolidationRequestDecoder : IRlpStreamDecoder, IRlpValueDecoder, IRlpObjectDecoder +{ + public int GetLength(ConsolidationRequest item, RlpBehaviors rlpBehaviors) => + Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + + public int GetContentLength(ConsolidationRequest item, RlpBehaviors rlpBehaviors) => + Rlp.LengthOf(item.SourceAddress) + Rlp.LengthOf(item.SourcePubkey) + + Rlp.LengthOf(item.TargetPubkey); + + public ConsolidationRequest Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int _ = rlpStream.ReadSequenceLength(); + Address sourceAddress = rlpStream.DecodeAddress(); + ArgumentNullException.ThrowIfNull(sourceAddress); + byte[] SourcePubkey = rlpStream.DecodeByteArray(); + byte[] TargetPubkey = rlpStream.DecodeByteArray(); + return new ConsolidationRequest() + { + SourceAddress = sourceAddress, + SourcePubkey = SourcePubkey, + TargetPubkey = TargetPubkey + }; + } + + public ConsolidationRequest Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int _ = decoderContext.ReadSequenceLength(); + Address sourceAddress = decoderContext.DecodeAddress(); + ArgumentNullException.ThrowIfNull(sourceAddress); + byte[] SourcePubkey = decoderContext.DecodeByteArray(); + byte[] TargetPubkey = decoderContext.DecodeByteArray(); + return new ConsolidationRequest() + { + SourceAddress = sourceAddress, + SourcePubkey = SourcePubkey, + TargetPubkey = TargetPubkey + }; + } + + public void Encode(RlpStream stream, ConsolidationRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int contentLength = GetContentLength(item, rlpBehaviors); + stream.StartSequence(contentLength); + stream.Encode(item.SourceAddress); + stream.Encode(item.SourcePubkey); + stream.Encode(item.TargetPubkey); + } + + public Rlp Encode(ConsolidationRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); + + Encode(rlpStream, item, rlpBehaviors); + + return new Rlp(rlpStream.Data.ToArray()); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/DepositDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/DepositDecoder.cs new file mode 100644 index 00000000000..9fdc2a3bd1e --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/DepositDecoder.cs @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Serialization.Rlp; + +public class DepositDecoder : IRlpStreamDecoder, IRlpValueDecoder +{ + public static DepositDecoder Instance { get; } = new(); + + public Deposit? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (rlpStream.IsNextItemNull()) + { + rlpStream.ReadByte(); + + return null; + } + + rlpStream.ReadSequenceLength(); + + return new() + { + Pubkey = rlpStream.DecodeByteArray(), + WithdrawalCredentials = rlpStream.DecodeByteArray(), + Amount = rlpStream.DecodeULong(), + Signature = rlpStream.DecodeByteArray(), + Index = rlpStream.DecodeULong(), + }; + } + + public Deposit? Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (decoderContext.IsNextItemNull()) + { + decoderContext.ReadByte(); + + return null; + } + + decoderContext.ReadSequenceLength(); + + return new() + { + Pubkey = decoderContext.DecodeByteArray(), + WithdrawalCredentials = decoderContext.DecodeByteArray(), + Amount = decoderContext.DecodeULong(), + Signature = decoderContext.DecodeByteArray(), + Index = decoderContext.DecodeULong(), + }; + } + + public void Encode(RlpStream stream, Deposit? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (item is null) + { + stream.EncodeNullObject(); + return; + } + + var contentLength = GetContentLength(item, rlpBehaviors); + + stream.StartSequence(contentLength); + stream.Encode(item.Pubkey); + stream.Encode(item.WithdrawalCredentials); + stream.Encode(item.Amount); + stream.Encode(item.Signature); + stream.Encode(item.Index.Value); + } + + public Rlp Encode(Deposit? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + var stream = new RlpStream(GetLength(item, rlpBehaviors)); + + Encode(stream, item, rlpBehaviors); + + return new(stream.Data.ToArray()); + } + + public int GetContentLength(Deposit item, RlpBehaviors rlpBehaviors) => + Rlp.LengthOf(item.Pubkey) + + Rlp.LengthOf(item.WithdrawalCredentials) + + Rlp.LengthOf(item.Amount) + + Rlp.LengthOf(item.Signature) + + Rlp.LengthOf(item.Index); + + public int GetLength(Deposit item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs index 80e1a500525..64353dfa7c5 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/HeaderDecoder.cs @@ -73,7 +73,7 @@ public class HeaderDecoder : IRlpValueDecoder, IRlpStreamDecoder 0 && decoderContext.PeekPrefixAndContentLength().ContentLength == Hash256.Size) { @@ -85,10 +85,15 @@ public class HeaderDecoder : IRlpValueDecoder, IRlpStreamDecoder= 4 && decoderContext.Position != headerCheck) { blockHeader.ParentBeaconBlockRoot = decoderContext.DecodeKeccak(); } + + if (itemsRemaining >= 5 && decoderContext.Position != headerCheck) + { + blockHeader.RequestsRoot = decoderContext.DecodeKeccak(); + } } @@ -160,7 +165,7 @@ public class HeaderDecoder : IRlpValueDecoder, IRlpStreamDecoder 0 && rlpStream.PeekPrefixAndContentLength().ContentLength == Hash256.Size) { @@ -172,10 +177,15 @@ public class HeaderDecoder : IRlpValueDecoder, IRlpStreamDecoder= 4 && rlpStream.Position != headerCheck) { blockHeader.ParentBeaconBlockRoot = rlpStream.DecodeKeccak(); } + + if (itemsRemaining >= 5 && rlpStream.Position != headerCheck) + { + blockHeader.RequestsRoot = rlpStream.DecodeKeccak(); + } } if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) @@ -245,6 +255,11 @@ public void Encode(RlpStream rlpStream, BlockHeader? header, RlpBehaviors rlpBeh { rlpStream.Encode(header.ParentBeaconBlockRoot); } + + if (header.RequestsRoot is not null) + { + rlpStream.Encode(header.RequestsRoot); + } } public Rlp Encode(BlockHeader? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) @@ -286,7 +301,8 @@ private static int GetContentLength(BlockHeader? item, RlpBehaviors rlpBehaviors + (item.WithdrawalsRoot is null && item.BlobGasUsed is null && item.ExcessBlobGas is null ? 0 : Rlp.LengthOfKeccakRlp) + (item.ParentBeaconBlockRoot is null ? 0 : Rlp.LengthOfKeccakRlp) + (item.BlobGasUsed is null ? 0 : Rlp.LengthOf(item.BlobGasUsed.Value)) - + (item.ExcessBlobGas is null ? 0 : Rlp.LengthOf(item.ExcessBlobGas.Value)); + + (item.ExcessBlobGas is null ? 0 : Rlp.LengthOf(item.ExcessBlobGas.Value)) + + (item.RequestsRoot is null ? 0 : Rlp.LengthOf(item.RequestsRoot)); if (notForSealing) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs index fdf473c883f..c7804d7eeb6 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs @@ -16,6 +16,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Int256; +using Nethermind.Core.ConsensusRequests; namespace Nethermind.Serialization.Rlp { @@ -26,6 +27,7 @@ public class RlpStream private static readonly BlockInfoDecoder _blockInfoDecoder = new(); private static readonly TxDecoder _txDecoder = new(); private static readonly WithdrawalDecoder _withdrawalDecoder = new(); + private static readonly ConsensusRequestDecoder _requestsDecoder = new(); private static readonly LogEntryDecoder _logEntryDecoder = LogEntryDecoder.Instance; private readonly CappedArray _data; @@ -87,6 +89,7 @@ public void Encode(Transaction value, RlpBehaviors rlpBehaviors = RlpBehaviors.N } public void Encode(Withdrawal value) => _withdrawalDecoder.Encode(this, value); + public void Encode(ConsensusRequest value) => _requestsDecoder.Encode(this, value); public void Encode(LogEntry value) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs new file mode 100644 index 00000000000..bc928f87980 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; + +namespace Nethermind.Serialization.Rlp; + +public class WithdrawalRequestDecoder : IRlpStreamDecoder, IRlpValueDecoder, IRlpObjectDecoder +{ + public int GetLength(WithdrawalRequest item, RlpBehaviors rlpBehaviors) => + Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); + + public int GetContentLength(WithdrawalRequest item, RlpBehaviors rlpBehaviors) => + Rlp.LengthOf(item.SourceAddress) + Rlp.LengthOf(item.ValidatorPubkey) + + Rlp.LengthOf(item.Amount); + + public WithdrawalRequest Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int _ = rlpStream.ReadSequenceLength(); + Address sourceAddress = rlpStream.DecodeAddress(); + ArgumentNullException.ThrowIfNull(sourceAddress); + byte[] ValidatorPubkey = rlpStream.DecodeByteArray(); + ulong amount = rlpStream.DecodeULong(); + return new WithdrawalRequest() + { + SourceAddress = sourceAddress, + ValidatorPubkey = ValidatorPubkey, + Amount = amount + }; + } + + public WithdrawalRequest Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int _ = decoderContext.ReadSequenceLength(); + Address sourceAddress = decoderContext.DecodeAddress(); + ArgumentNullException.ThrowIfNull(sourceAddress); + byte[] ValidatorPubkey = decoderContext.DecodeByteArray(); + ulong amount = decoderContext.DecodeULong(); + return new WithdrawalRequest() + { + SourceAddress = sourceAddress, + ValidatorPubkey = ValidatorPubkey, + Amount = amount + }; + } + + public void Encode(RlpStream stream, WithdrawalRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int contentLength = GetContentLength(item, rlpBehaviors); + stream.StartSequence(contentLength); + stream.Encode(item.SourceAddress); + stream.Encode(item.ValidatorPubkey); + stream.Encode(item.Amount); + } + + public Rlp Encode(WithdrawalRequest item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); + + Encode(rlpStream, item, rlpBehaviors); + + return new Rlp(rlpStream.Data.ToArray()); + } +} diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index 5c847cdc750..fbfb6d81b8a 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -290,6 +290,9 @@ private static void VerifyGnosisShanghaiSpecifics(IReleaseSpec preShanghaiSpec, preShanghaiSpec.IsEip170Enabled.Should().Be(false); postShanghaiSpec.IsEip170Enabled.Should().Be(true); + + preShanghaiSpec.AuRaSystemCalls.Should().Be(true); + postShanghaiSpec.AuRaSystemCalls.Should().Be(true); } private static void VerifyGnosisPreShanghaiSpecifics(ISpecProvider specProvider) @@ -441,7 +444,8 @@ private static void CompareSpecs(IReleaseSpec expectedSpec, IReleaseSpec actualS .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.IsEip170Enabled)) .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.IsEip1283Enabled)) .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.LimitCodeSize)) - .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.UseConstantinopleNetGasMetering))) + .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.UseConstantinopleNetGasMetering)) + .Where(p => !isGnosis || p.Name != nameof(IReleaseSpec.AuRaSystemCalls))) { Assert.That(propertyInfo.GetValue(actualSpec), Is.EqualTo(propertyInfo.GetValue(expectedSpec)), activation + "." + propertyInfo.Name); @@ -739,6 +743,7 @@ void TestTransitions(ForkActivation activation, Action changes) r.MaximumUncleCount = 2; r.WithdrawalTimestamp = ulong.MaxValue; r.Eip4844TransitionTimestamp = ulong.MaxValue; + r.AuRaSystemCalls = false; }); TestTransitions((ForkActivation)1L, r => diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 619f1a4a3e6..eae1c7db7a9 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -113,7 +113,6 @@ public OverridableReleaseSpec(IReleaseSpec spec) public bool IsEip4844Enabled => _spec.IsEip4844Enabled; public bool IsRip7212Enabled => _spec.IsRip7212Enabled; public bool IsEip3607Enabled { get; set; } - public bool IsEip158IgnoredAccount(Address address) { return _spec.IsEip158IgnoredAccount(address); @@ -145,6 +144,7 @@ public ulong Eip4844TransitionTimestamp _overridenEip4844TransitionTimeStamp = value; } } + public bool AuRaSystemCalls => _spec.AuRaSystemCalls; public bool IsEip1153Enabled => _spec.IsEip1153Enabled; public bool IsEip3651Enabled => _spec.IsEip3651Enabled; @@ -155,7 +155,11 @@ public ulong Eip4844TransitionTimestamp public bool IsEip5656Enabled => _spec.IsEip5656Enabled; public bool IsEip6780Enabled => _spec.IsEip6780Enabled; public bool IsEip4788Enabled => _spec.IsEip4788Enabled; - public Address Eip4788ContractAddress => _spec.Eip4788ContractAddress; + public Address? Eip4788ContractAddress => _spec.Eip4788ContractAddress; + public bool IsEip7002Enabled => _spec.IsEip7002Enabled; + public Address Eip7002ContractAddress => _spec.Eip7002ContractAddress; + public bool IsEip7251Enabled => _spec.IsEip7251Enabled; + public Address Eip7251ContractAddress => _spec.Eip7251ContractAddress; public bool IsEip2935Enabled => _spec.IsEip2935Enabled; public bool IsEip7709Enabled => _spec.IsEip7709Enabled; public Address Eip2935ContractAddress => _spec.Eip2935ContractAddress; @@ -163,5 +167,9 @@ public ulong Eip4844TransitionTimestamp public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; + + public bool IsEip6110Enabled => _spec.IsEip6110Enabled; + + public Address DepositContractAddress => _spec.DepositContractAddress; } } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index 9761eefaca6..18e0c545ff3 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -117,6 +117,12 @@ public class ChainParameters public ulong? Eip6780TransitionTimestamp { get; set; } public ulong? Eip4788TransitionTimestamp { get; set; } public Address Eip4788ContractAddress { get; set; } + public ulong? Eip6110TransitionTimestamp { get; set; } + public Address DepositContractAddress { get; set; } + public ulong? Eip7002TransitionTimestamp { get; set; } + public Address Eip7002ContractAddress { get; set; } + public ulong? Eip7251TransitionTimestamp { get; set; } + public Address Eip7251ContractAddress { get; set; } public ulong? Eip2935TransitionTimestamp { get; set; } public Address Eip2935ContractAddress { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index b098d0793ee..5790733e9ce 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -7,6 +7,7 @@ using System.Numerics; using System.Reflection; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Specs; using Nethermind.Int256; using Nethermind.Logging; @@ -251,11 +252,21 @@ private static ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseSt releaseSpec.IsEip6780Enabled = (chainSpec.Parameters.Eip6780TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsEip4788Enabled = (chainSpec.Parameters.Eip4788TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.Eip4788ContractAddress = chainSpec.Parameters.Eip4788ContractAddress; + releaseSpec.AuRaSystemCalls = chainSpec.SealEngineType == SealEngineType.AuRa; releaseSpec.IsEip2935Enabled = (chainSpec.Parameters.Eip2935TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.Eip2935ContractAddress = chainSpec.Parameters.Eip2935ContractAddress; releaseSpec.IsEip7702Enabled = (chainSpec.Parameters.Eip7702TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.IsEip6110Enabled = (chainSpec.Parameters.Eip6110TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.DepositContractAddress = chainSpec.Parameters.DepositContractAddress; + + releaseSpec.IsEip7002Enabled = (chainSpec.Parameters.Eip7002TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.Eip7002ContractAddress = chainSpec.Parameters.Eip7002ContractAddress; + + releaseSpec.IsEip7251Enabled = (chainSpec.Parameters.Eip7251TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.Eip7251ContractAddress = chainSpec.Parameters.Eip7251ContractAddress; + return releaseSpec; } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index c3ef15e6a1b..f1950b65229 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -11,6 +11,7 @@ using System.Text.Json; using Nethermind.Config; using Nethermind.Core; +using Nethermind.Core.ConsensusRequests; using Nethermind.Core.Crypto; using Nethermind.Int256; using Nethermind.Serialization.Json; @@ -155,6 +156,13 @@ bool GetForInnerPathExistence(KeyValuePair o) => Eip1559BaseFeeInitialValue = chainSpecJson.Params.Eip1559BaseFeeInitialValue ?? Eip1559Constants.DefaultForkBaseFee, Eip1559BaseFeeMaxChangeDenominator = chainSpecJson.Params.Eip1559BaseFeeMaxChangeDenominator ?? Eip1559Constants.DefaultBaseFeeMaxChangeDenominator, + + Eip6110TransitionTimestamp = chainSpecJson.Params.Eip6110TransitionTimestamp, + DepositContractAddress = chainSpecJson.Params.DepositContractAddress ?? Eip6110Constants.MainnetDepositContractAddress, + Eip7002TransitionTimestamp = chainSpecJson.Params.Eip7002TransitionTimestamp, + Eip7002ContractAddress = chainSpecJson.Params.Eip7002ContractAddress ?? Eip7002Constants.WithdrawalRequestPredeployAddress, + Eip7251TransitionTimestamp = chainSpecJson.Params.Eip7251TransitionTimestamp, + Eip7251ContractAddress = chainSpecJson.Params.Eip7251ContractAddress ?? Eip7251Constants.ConsolidationRequestPredeployAddress, Eip1559FeeCollector = chainSpecJson.Params.Eip1559FeeCollector, Eip1559FeeCollectorTransition = chainSpecJson.Params.Eip1559FeeCollectorTransition, Eip1559BaseFeeMinValueTransition = chainSpecJson.Params.Eip1559BaseFeeMinValueTransition, @@ -414,9 +422,16 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec 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; if (withdrawalsEnabled) genesisHeader.WithdrawalsRoot = Keccak.EmptyTreeHash; + var requestsEnabled = depositsEnabled || withdrawalRequestsEnabled || consolidationRequestsEnabled; + if (requestsEnabled) + genesisHeader.RequestsRoot = Keccak.EmptyTreeHash; ; + bool isEip4844Enabled = chainSpecJson.Params.Eip4844TransitionTimestamp is not null && genesisHeader.Timestamp >= chainSpecJson.Params.Eip4844TransitionTimestamp; if (isEip4844Enabled) { @@ -430,13 +445,25 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec genesisHeader.ParentBeaconBlockRoot = Keccak.Zero; } + if (requestsEnabled) + { + genesisHeader.ReceiptsRoot = Keccak.EmptyTreeHash; + } + genesisHeader.AuRaStep = step; genesisHeader.AuRaSignature = auRaSignature; if (withdrawalsEnabled) - chainSpec.Genesis = new Block(genesisHeader, Array.Empty(), Array.Empty(), Array.Empty()); + chainSpec.Genesis = new Block( + genesisHeader, + Array.Empty(), + Array.Empty(), + Array.Empty(), + requestsEnabled ? Array.Empty() : null); else + { chainSpec.Genesis = new Block(genesisHeader); + } } private static void LoadAllocations(ChainSpecJson chainSpecJson, ChainSpec chainSpec) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index 8f16193e8e7..c001f20213a 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -147,6 +147,12 @@ internal class ChainSpecParamsJson public ulong? Eip4844MaxBlobGasPerBlock { get; set; } public UInt256? Eip4844MinBlobGasPrice { get; set; } public ulong? Eip4844TargetBlobGasPerBlock { get; set; } + public ulong? Eip6110TransitionTimestamp { get; set; } + public Address DepositContractAddress { get; set; } + public ulong? Eip7002TransitionTimestamp { get; set; } + public Address Eip7002ContractAddress { get; set; } + public ulong? Eip7251TransitionTimestamp { get; set; } + public Address Eip7251ContractAddress { get; set; } public ulong? Rip7212TransitionTimestamp { get; set; } public ulong? Eip7702TransitionTimestamp { get; set; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs index 4180bb4cd52..81bd5d764fe 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs @@ -26,6 +26,12 @@ protected Olympic() Eip1559TransitionBlock = long.MaxValue; ValidateChainId = true; ValidateReceipts = true; + + // The below addresses are added for all forks, but the given EIPs can be enabled at a specific timestamp or block. + Eip7251ContractAddress = Eip7251Constants.ConsolidationRequestPredeployAddress; + Eip7002ContractAddress = Eip7002Constants.WithdrawalRequestPredeployAddress; + DepositContractAddress = Eip6110Constants.MainnetDepositContractAddress; + AuRaSystemCalls = false; } public static IReleaseSpec Instance => LazyInitializer.EnsureInitialized(ref _instance, () => new Olympic()); diff --git a/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs b/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs index c9822e42f86..7fd7e8c348d 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs @@ -17,6 +17,9 @@ protected Prague() IsEip2537Enabled = true; IsEip2935Enabled = true; IsEip7702Enabled = true; + IsEip6110Enabled = true; + IsEip7002Enabled = true; + IsEip7251Enabled = true; IsRip7212Enabled = true; Eip2935ContractAddress = Eip2935Constants.BlockHashHistoryAddress; } diff --git a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs index ce8148f311a..6470a8a26b7 100644 --- a/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs @@ -26,6 +26,7 @@ public class MainnetSpecProvider : ISpecProvider public const ulong BeaconChainGenesisTimestamp = 0x5fc63057; public const ulong ShanghaiBlockTimestamp = 0x64373057; public const ulong CancunBlockTimestamp = 0x65F1B057; + //TODO correct this timestamp! public const ulong PragueBlockTimestamp = ulong.MaxValue - 2; public const ulong OsakaBlockTimestamp = ulong.MaxValue - 1; diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 6220b300830..0c3d36cb1a4 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -69,6 +69,7 @@ public ReleaseSpec Clone() public long Eip1559TransitionBlock { get; set; } public ulong WithdrawalTimestamp { get; set; } public ulong Eip4844TransitionTimestamp { get; set; } + public bool AuRaSystemCalls { get; set; } public Address Eip1559FeeCollector { get; set; } public UInt256? Eip1559BaseFeeMinValue { get; set; } public UInt256 ForkBaseFee { get; set; } = Eip1559Constants.DefaultForkBaseFee; @@ -85,6 +86,10 @@ public ReleaseSpec Clone() public bool IsEip6780Enabled { get; set; } public bool IsEip4788Enabled { get; set; } public bool IsEip7702Enabled { get; set; } + public bool IsEip7002Enabled { get; set; } + public Address Eip7002ContractAddress { get; set; } + public bool IsEip7251Enabled { get; set; } + public Address Eip7251ContractAddress { get; set; } private Address _eip4788ContractAddress; public Address Eip4788ContractAddress @@ -93,6 +98,14 @@ public Address Eip4788ContractAddress set => _eip4788ContractAddress = value; } + public bool IsEip6110Enabled { get; set; } + + private Address _depositContractAddress; + public Address DepositContractAddress + { + get => IsEip6110Enabled ? _depositContractAddress : null; + set => _depositContractAddress = value; + } public bool IsEip2935Enabled { get; set; } public bool IsEip7709Enabled { get; set; } @@ -102,5 +115,7 @@ public Address Eip2935ContractAddress get => IsEip2935Enabled ? _eip2935ContractAddress : null; set => _eip2935ContractAddress = value; } - }; + + + } } diff --git a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs index 15f641da85e..8691ed86768 100644 --- a/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/SystemTransactionReleaseSpec.cs @@ -108,17 +108,13 @@ public SystemTransactionReleaseSpec(IReleaseSpec spec) public bool IsEip3541Enabled => _spec.IsEip3541Enabled; public bool IsEip3607Enabled => _spec.IsEip3607Enabled; - public bool IsEip158IgnoredAccount(Address address) { return _spec.IsEip158IgnoredAccount(address); } - public long Eip1559TransitionBlock => _spec.Eip1559TransitionBlock; public ulong WithdrawalTimestamp => _spec.WithdrawalTimestamp; - public ulong Eip4844TransitionTimestamp => _spec.Eip4844TransitionTimestamp; - public Address Eip1559FeeCollector => _spec.Eip1559FeeCollector; public bool IsEip1153Enabled => _spec.IsEip1153Enabled; public bool IsEip3651Enabled => _spec.IsEip3651Enabled; @@ -129,6 +125,12 @@ public bool IsEip158IgnoredAccount(Address address) public bool IsEip6780Enabled => _spec.IsEip6780Enabled; public bool IsEip4788Enabled => _spec.IsEip4788Enabled; public Address Eip4788ContractAddress => _spec.Eip4788ContractAddress; + + public bool AuRaSystemCalls => false; + public bool IsEip7002Enabled => _spec.IsEip7002Enabled; + public Address Eip7002ContractAddress => _spec.Eip7002ContractAddress; + public bool IsEip7251Enabled => _spec.IsEip7251Enabled; + public Address Eip7251ContractAddress => _spec.Eip7251ContractAddress; public bool IsEip2935Enabled => _spec.IsEip2935Enabled; public bool IsEip7709Enabled => _spec.IsEip7709Enabled; public Address Eip2935ContractAddress => _spec.Eip2935ContractAddress; @@ -136,5 +138,9 @@ public bool IsEip158IgnoredAccount(Address address) public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; + + public bool IsEip6110Enabled => _spec.IsEip6110Enabled; + + public Address DepositContractAddress => _spec.DepositContractAddress; } } diff --git a/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs index ee924de5b4e..8197dae5530 100644 --- a/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs @@ -69,7 +69,7 @@ public void Eip_158_touch_zero_value_system_account_is_not_deleted() provider.Commit(Homestead.Instance); var releaseSpec = new ReleaseSpec() { IsEip158Enabled = true }; - provider.InsertCode(systemUser, System.Text.Encoding.UTF8.GetBytes(""), releaseSpec); + provider.InsertCode(systemUser, System.Text.Encoding.UTF8.GetBytes(""), releaseSpec, false); provider.Commit(releaseSpec); provider.GetAccount(systemUser).Should().NotBeNull(); @@ -182,7 +182,7 @@ public void Restore_in_the_middle() provider.CreateAccount(_address1, 1); provider.AddToBalance(_address1, 1, Frontier.Instance); provider.IncrementNonce(_address1); - provider.InsertCode(_address1, new byte[] { 1 }, Frontier.Instance); + provider.InsertCode(_address1, new byte[] { 1 }, Frontier.Instance, false); provider.UpdateStorageRoot(_address1, Hash2); Assert.That(provider.GetNonce(_address1), Is.EqualTo(UInt256.One)); diff --git a/src/Nethermind/Nethermind.State.Test/StatsCollectorTests.cs b/src/Nethermind/Nethermind.State.Test/StatsCollectorTests.cs index 21c6b823c78..bcdc414e3c1 100644 --- a/src/Nethermind/Nethermind.State.Test/StatsCollectorTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StatsCollectorTests.cs @@ -31,10 +31,10 @@ public void Can_collect_stats([Values(false, true)] bool parallel) WorldState stateProvider = new(trieStore, codeDb, LimboLogs.Instance); stateProvider.CreateAccount(TestItem.AddressA, 1); - stateProvider.InsertCode(TestItem.AddressA, new byte[] { 1, 2, 3 }, Istanbul.Instance); + stateProvider.InsertCode(TestItem.AddressA, new byte[] { 1, 2, 3 }, Istanbul.Instance, false); stateProvider.CreateAccount(TestItem.AddressB, 1); - stateProvider.InsertCode(TestItem.AddressB, new byte[] { 1, 2, 3, 4 }, Istanbul.Instance); + stateProvider.InsertCode(TestItem.AddressB, new byte[] { 1, 2, 3, 4 }, Istanbul.Instance, false); for (int i = 0; i < 1000; i++) { diff --git a/src/Nethermind/Nethermind.State/IWorldState.cs b/src/Nethermind/Nethermind.State/IWorldState.cs index 9c78813ce93..e90d806dc41 100644 --- a/src/Nethermind/Nethermind.State/IWorldState.cs +++ b/src/Nethermind/Nethermind.State/IWorldState.cs @@ -86,13 +86,13 @@ public interface IWorldState : IJournal, IReadOnlyStateProvider void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce = default); void CreateAccountIfNotExists(Address address, in UInt256 balance, in UInt256 nonce = default); - void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false); + void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isSystemCall = false, bool isGenesis = false); - void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); + void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, bool isSystemCall = false); void AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec); - void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); + void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, bool isSystemCall = false); void UpdateStorageRoot(Address address, Hash256 storageRoot); diff --git a/src/Nethermind/Nethermind.State/Proofs/PatriciaTrieT.cs b/src/Nethermind/Nethermind.State/Proofs/PatriciaTrieT.cs index 4b28e2617d5..66be88ea5e6 100644 --- a/src/Nethermind/Nethermind.State/Proofs/PatriciaTrieT.cs +++ b/src/Nethermind/Nethermind.State/Proofs/PatriciaTrieT.cs @@ -22,13 +22,14 @@ public abstract class PatriciaTrie : PatriciaTree /// true to maintain an in-memory database for proof computation; /// otherwise, false. /// - public PatriciaTrie(T[]? list, bool canBuildProof, ICappedArrayPool? bufferPool = null) + protected PatriciaTrie(T[]? list, bool canBuildProof, ICappedArrayPool? bufferPool = null) : base(canBuildProof ? new MemDb() : NullDb.Instance, EmptyTreeHash, false, false, NullLogManager.Instance, bufferPool: bufferPool) { CanBuildProof = canBuildProof; if (list?.Length > 0) { + // ReSharper disable once VirtualMemberCallInConstructor Initialize(list); UpdateRootHash(); } diff --git a/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs new file mode 100644 index 00000000000..63b04be3326 --- /dev/null +++ b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.Buffers; +using Nethermind.Core.ConsensusRequests; +using Nethermind.Core.Crypto; +using Nethermind.Serialization.Rlp; +using Nethermind.State.Trie; +using Nethermind.Trie; + +namespace Nethermind.State.Proofs; + +public class RequestsTrie : PatriciaTrie +{ + private static readonly ConsensusRequestDecoder _codec = new(); + + public RequestsTrie(ConsensusRequest[]? requests, bool canBuildProof = false, ICappedArrayPool? bufferPool = null) + : base(requests, canBuildProof, bufferPool) + { + } + + protected override void Initialize(ConsensusRequest[] requests) + { + var key = 0; + + foreach (ConsensusRequest exit in requests) + { + Set(Rlp.Encode(key++).Bytes, _codec.Encode(exit, RlpBehaviors.SkipTypedWrapping).Bytes); + } + } + + public static Hash256 CalculateRoot(ConsensusRequest[] requests) + { + using TrackingCappedArrayPool cappedArray = new(requests.Length * 4); + Hash256 rootHash = new RequestsTrie(requests, canBuildProof: false, bufferPool: cappedArray).RootHash; + return rootHash; + } +} diff --git a/src/Nethermind/Nethermind.State/Proofs/WithdrawalTrie.cs b/src/Nethermind/Nethermind.State/Proofs/WithdrawalTrie.cs index 144eaa018ee..c9bf10a1142 100644 --- a/src/Nethermind/Nethermind.State/Proofs/WithdrawalTrie.cs +++ b/src/Nethermind/Nethermind.State/Proofs/WithdrawalTrie.cs @@ -17,7 +17,7 @@ public class WithdrawalTrie : PatriciaTrie /// /// The withdrawals to build the trie of. - public WithdrawalTrie(Withdrawal[] withdrawals, bool canBuildProof = false) + public WithdrawalTrie(Withdrawal[]? withdrawals, bool canBuildProof = false) : base(withdrawals, canBuildProof) => ArgumentNullException.ThrowIfNull(withdrawals); protected override void Initialize(Withdrawal[] withdrawals) diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 81cd907963b..d1e09026156 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -119,8 +119,10 @@ public UInt256 GetBalance(Address address) return account?.Balance ?? UInt256.Zero; } - public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false, bool isSystemCall = false) { + if (isSystemCall && address == Address.SystemUser) return; + _needsStateRootUpdate = true; // Don't reinsert if already inserted. This can be the case when the same @@ -168,14 +170,14 @@ public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory c } } - private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSubtracting) + private void SetNewBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSubtracting, bool isSystemCall = false) { _needsStateRootUpdate = true; Account GetThroughCacheCheckExists() { Account result = GetThroughCache(address); - if (result is null) + if (result is null && !isSystemCall) { if (_logger.IsError) _logger.Error("Updating balance of a non-existing account"); throw new InvalidOperationException("Updating balance of a non-existing account"); @@ -193,6 +195,8 @@ Account GetThroughCacheCheckExists() if (releaseSpec.IsEip158Enabled && !isSubtracting) { Account touched = GetThroughCacheCheckExists(); + if (touched is null) return; + if (_logger.IsTrace) _logger.Trace($" Touch {address} (balance)"); if (touched.IsEmpty) { @@ -204,6 +208,7 @@ Account GetThroughCacheCheckExists() } Account account = GetThroughCacheCheckExists(); + if (account is null) return; if (isSubtracting && account.Balance < balanceChange) { @@ -217,16 +222,16 @@ Account GetThroughCacheCheckExists() PushUpdate(address, changedAccount); } - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec) + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSystemCall = false) { _needsStateRootUpdate = true; - SetNewBalance(address, balanceChange, releaseSpec, true); + SetNewBalance(address, balanceChange, releaseSpec, true, isSystemCall); } - public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec) + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec releaseSpec, bool isSystemCall = false) { _needsStateRootUpdate = true; - SetNewBalance(address, balanceChange, releaseSpec, false); + SetNewBalance(address, balanceChange, releaseSpec, false, isSystemCall); } /// @@ -758,7 +763,7 @@ private void PushUpdate(Address address, Account account) private void PushTouch(Address address, Account account, IReleaseSpec releaseSpec, bool isZero) { - if (isZero && releaseSpec.IsEip158IgnoredAccount(address)) return; + if (isZero && releaseSpec.IsEip158IgnoredAccount(address) && releaseSpec.AuRaSystemCalls) return; Push(ChangeType.Touch, address, account); } diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index 60f5492cd60..aa8fd4f170a 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -144,23 +144,21 @@ public void CreateAccount(Address address, in UInt256 balance, in UInt256 nonce { _stateProvider.CreateAccount(address, balance, nonce); } - - public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) + public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isSystemCall, bool isGenesis = false) { - _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis); + _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis, isSystemCall); } - - public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + public void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, bool isSystemCall = false) { - _stateProvider.AddToBalance(address, balanceChange, spec); + _stateProvider.AddToBalance(address, balanceChange, spec, isSystemCall); } public void AddToBalanceAndCreateIfNotExists(Address address, in UInt256 balanceChange, IReleaseSpec spec) { _stateProvider.AddToBalanceAndCreateIfNotExists(address, balanceChange, spec); } - public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec) + public void SubtractFromBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec, bool isSystemCall = false) { - _stateProvider.SubtractFromBalance(address, balanceChange, spec); + _stateProvider.SubtractFromBalance(address, balanceChange, spec, isSystemCall); } public void UpdateStorageRoot(Address address, Hash256 storageRoot) { diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs index ecd34afc769..59168a2a9a7 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs @@ -302,6 +302,7 @@ private SyncTestContext CreateSyncManager(int index) stateProvider, receiptStorage, new BlockhashStore(specProvider, stateProvider), + txProcessor, logManager); RecoverSignatures step = new(ecdsa, txPool, specProvider, logManager); @@ -324,6 +325,7 @@ private SyncTestContext CreateSyncManager(int index) devState, receiptStorage, new BlockhashStore(specProvider, devState), + devTxProcessor, logManager); BlockchainProcessor devChainProcessor = new(tree, devBlockProcessor, step, stateReader, logManager, diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs index 6509942731e..c2f2e08128e 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SynchronizerTests.cs @@ -38,6 +38,7 @@ using Nethermind.Trie.Pruning; using NSubstitute; using NUnit.Framework; +using Nethermind.Synchronization.SnapSync; using Nethermind.Trie; namespace Nethermind.Synchronization.Test diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index 3ab8c9d1127..32daed46b5d 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -1323,7 +1323,7 @@ public void Accept( // but we know that we have multiple optimizations and assumptions on trees ExpectAccounts = visitingOptions.ExpectAccounts, MaxDegreeOfParallelism = visitingOptions.MaxDegreeOfParallelism, - IsStorage = storageAddr is not null + IsStorage = storageAddr != null }; if (storageAddr is not null) diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TinyTreePath.cs b/src/Nethermind/Nethermind.Trie/Pruning/TinyTreePath.cs index c15a5a9f906..de1d3bcc3bc 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TinyTreePath.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TinyTreePath.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Nethermind.Core.Crypto; diff --git a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs index 6c6a433d6da..557e519a36e 100644 --- a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs +++ b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Trie.Pruning;