diff --git a/.github/workflows/build-tools.yml b/.github/workflows/build-tools.yml new file mode 100644 index 00000000000..1f25cb17a86 --- /dev/null +++ b/.github/workflows/build-tools.yml @@ -0,0 +1,33 @@ +name: Build tools + +on: + merge_group: + types: [checks_requested] + pull_request: + branches: [master] + push: + branches: [master] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + config: [release] + project: + - DocGen/DocGen.sln + - HiveCompare/HiveCompare.sln + - HiveConsensusWorkflowGenerator/HiveConsensusWorkflowGenerator.csproj + - Nethermind.Tools.Kute/Nethermind.Tools.Kute.csproj + - SendBlobs/SendBlobs.sln + - TxParser/TxParser.csproj + steps: + - name: Check out repository + uses: actions/checkout@v4 + - name: Set up .NET + uses: actions/setup-dotnet@v4 + - name: Build ${{ matrix.project }} + working-directory: tools + run: dotnet build ./${{ matrix.project }} -c ${{ matrix.config }} diff --git a/.github/workflows/sync-supported-chains.yml b/.github/workflows/sync-supported-chains.yml index 0d8363e0053..3ca8427a262 100644 --- a/.github/workflows/sync-supported-chains.yml +++ b/.github/workflows/sync-supported-chains.yml @@ -85,7 +85,7 @@ jobs: - name: Setup Go environment uses: actions/setup-go@v5 with: - go-version: '1.21.0' + go-version: 'stable' check-latest: true cache: true diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index e988db3e551..2d7ed8126a1 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -41,15 +41,15 @@ jobs: global-json-file: n/global.json - name: Build DocGen working-directory: n - run: dotnet build tools/docgen/DocGen.csproj -c release -o docgen + run: dotnet build tools/DocGen/DocGen.csproj -c release -o DocGen - name: Generate docs - run: n/docgen/DocGen $GITHUB_WORKSPACE/d --config --jsonrpc --metrics + run: n/DocGen/DocGen $GITHUB_WORKSPACE/d --config --jsonrpc --metrics - name: Tag a new version if: github.event_name == 'release' && !github.event.release.prerelease working-directory: d run: | npm i - npm run docusaurus docs:version v${{ github.event.release.tag_name }} + npm run docusaurus docs:version ${{ github.event.release.tag_name }} - name: Create a pull request working-directory: d env: diff --git a/src/Nethermind/Chains/hive.json b/src/Nethermind/Chains/hive.json index 805c6aaa175..0e0d1f800a6 100644 --- a/src/Nethermind/Chains/hive.json +++ b/src/Nethermind/Chains/hive.json @@ -26,7 +26,7 @@ "eip155Transition": "0x0", "maxCodeSizeTransition": "0x0", "maxCodeSize": 24576, - "maximumExtraDataSize": 102400, + "maximumExtraDataSize": "0x400", "eip140Transition": "0x0", "eip211Transition": "0x0", "eip214Transition": "0x0", @@ -51,57 +51,45 @@ "eip3529Transition": "0x0", "eip3541Transition": "0x0", "eip3198Transition": "0x0", - "MergeForkIdTransition": "0x64", - "networkID": "0x7", - "chainID": "0x7" + "eip3651TransitionTimestamp": "0x0", + "eip3855TransitionTimestamp": "0x0", + "eip3860TransitionTimestamp": "0x0", + "eip4895TransitionTimestamp": "0x0", + "eip4844TransitionTimestamp": "0x0", + "eip4788TransitionTimestamp": "0x0", + "eip1153TransitionTimestamp": "0x0", + "eip5656TransitionTimestamp": "0x0", + "eip6780TransitionTimestamp": "0x0", + "eip7702TransitionTimestamp": "0x0", + "chainID": "0x1" }, "genesis": { "seal": { "ethereum": { - "nonce": "0x0000000000000000" + "nonce": "0x0000000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000" } }, - "difficulty": "0x30000", + "difficulty": "0x00", "author": "0x0000000000000000000000000000000000000000", - "timestamp": "0x1234", - "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "gasLimit": "0x2fefd8" + "timestamp": "0x00", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "extraData": "0x00", + "gasLimit": "0x016345785d8a0000", + "baseFeePerGas": "0x07" }, "accounts": { - "0xcf49fda3be353c69b41ed96333cd24302da4556f": { - "balance": "0x123450000000000000000" - }, - "0x0161e041aad467a890839d5b08b138c1e6373072": { - "balance": "0x123450000000000000000" - }, - "0x87da6a8c6e9eff15d703fc2773e32f6af8dbe301": { - "balance": "0x123450000000000000000" - }, - "0xb97de4b8c857e4f6bc354f226dc3249aaee49209": { - "balance": "0x123450000000000000000" - }, - "0xc5065c9eeebe6df2c2284d046bfc906501846c51": { - "balance": "0x123450000000000000000" - }, - "0x0000000000000000000000000000000000000314": { - "balance": "0x0", - "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029", - "storage": { - "0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234", - "0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01" - } - }, - "0x0000000000000000000000000000000000000315": { - "balance": "0x9999999999999999999999999999999", - "code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029" - }, - "0x0000000000000000000000000000000000000316": { - "balance": "0x0", - "code": "0x444355" - }, - "0x0000000000000000000000000000000000000317": { - "balance": "0x0", - "code": "0x600160003555" + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02": { + "nonce": "0x01", + "balance": "0x00", + "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "storage": {} + }, + "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { + "nonce": "0x00", + "balance": "0x1d6329f1c35ca4bfabb9f5610000000000", + "code": "0x", + "storage": {} }, "0x0000000000000000000000000000000000000001": { "builtin": { @@ -206,4 +194,4 @@ } } } -} \ No newline at end of file +} 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.Test.Base/AuthorizationListJson.cs b/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs new file mode 100644 index 00000000000..a2b8b6cfcbc --- /dev/null +++ b/src/Nethermind/Ethereum.Test.Base/AuthorizationListJson.cs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; + +namespace Ethereum.Test.Base; +public class AuthorizationListJson +{ + public ulong ChainId { get; set; } + public Address Address { get; set; } + public ulong Nonce { get; set; } + public ulong V { get; set; } + public byte[] R { get; set; } + public byte[] S { get; set; } + public Address Signer { get; set; } +} diff --git a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs index 120c146c6d3..47487e7e212 100644 --- a/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs +++ b/src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs @@ -137,12 +137,16 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer) Block block = Build.A.Block.WithTransactions(test.Transaction).WithHeader(header).TestObject; - bool isValid = _txValidator.IsWellFormed(test.Transaction, spec) && IsValidBlock(block, specProvider); + ValidationResult txIsValid = _txValidator.IsWellFormed(test.Transaction, spec); - if (isValid) + if (txIsValid) { transactionProcessor.Execute(test.Transaction, new BlockExecutionContext(header), txTracer); } + else + { + _logger.Info($"Skipping invalid tx with error: {txIsValid.Error}"); + } stopwatch.Stop(); diff --git a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs index 8ac509a9672..7d41065cd52 100644 --- a/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs +++ b/src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs @@ -127,6 +127,7 @@ public static Transaction Convert(PostStateJson postStateJson, TransactionJson t { Transaction transaction = new(); + transaction.Type = transactionJson.Type; transaction.Value = transactionJson.Value[postStateJson.Indexes.Value]; transaction.GasLimit = transactionJson.GasLimit[postStateJson.Indexes.Gas]; transaction.GasPrice = transactionJson.GasPrice ?? transactionJson.MaxPriorityFeePerGas ?? 0; @@ -157,6 +158,23 @@ public static Transaction Convert(PostStateJson postStateJson, TransactionJson t if (transaction.BlobVersionedHashes?.Length > 0) transaction.Type = TxType.Blob; + if (transactionJson.AuthorizationList is not null) + { + transaction.AuthorizationList = + transactionJson.AuthorizationList + .Select(i => new AuthorizationTuple( + i.ChainId, + i.Address, + i.Nonce, + i.V, + i.R, + i.S)).ToArray(); + if (transaction.AuthorizationList.Any()) + { + transaction.Type = TxType.SetCode; + } + } + return transaction; } @@ -291,6 +309,7 @@ public static IEnumerable Convert(string 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/Ethereum.Test.Base/LegacyTransactionJson.cs b/src/Nethermind/Ethereum.Test.Base/LegacyTransactionJson.cs index db932e48913..0d93af017d5 100644 --- a/src/Nethermind/Ethereum.Test.Base/LegacyTransactionJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/LegacyTransactionJson.cs @@ -14,6 +14,7 @@ public class LegacyTransactionJson public UInt256 Nonce { get; set; } public Address To { get; set; } public UInt256 Value { get; set; } + public string Sender { get; set; } public byte[] R { get; set; } public byte[] S { get; set; } public ulong V { get; set; } diff --git a/src/Nethermind/Ethereum.Test.Base/TransactionJson.cs b/src/Nethermind/Ethereum.Test.Base/TransactionJson.cs index 59658f8e876..83c4884f835 100644 --- a/src/Nethermind/Ethereum.Test.Base/TransactionJson.cs +++ b/src/Nethermind/Ethereum.Test.Base/TransactionJson.cs @@ -8,6 +8,8 @@ namespace Ethereum.Test.Base { public class TransactionJson { + public TxType Type { get; set; } + public Address Sender { get; set; } public byte[][]? Data { get; set; } public long[]? GasLimit { get; set; } public UInt256? GasPrice { get; set; } @@ -19,6 +21,7 @@ public class TransactionJson public byte[]? SecretKey { get; set; } public AccessListItemJson[]?[]? AccessLists { get; set; } public AccessListItemJson[]? AccessList { get; set; } + public AuthorizationListJson[]? AuthorizationList { get; set; } public byte[]?[]? BlobVersionedHashes { get; set; } public UInt256? MaxFeePerBlobGas { get; set; } } diff --git a/src/Nethermind/Nethermind.Benchmark/Core/RecoverSignaturesBenchmark.cs b/src/Nethermind/Nethermind.Benchmark/Core/RecoverSignaturesBenchmark.cs new file mode 100644 index 00000000000..0b7655985fe --- /dev/null +++ b/src/Nethermind/Nethermind.Benchmark/Core/RecoverSignaturesBenchmark.cs @@ -0,0 +1,203 @@ +using BenchmarkDotNet.Attributes; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Core; +using Nethermind.Crypto; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using Nethermind.Specs; +using System; +using System.Collections.Generic; +using Nethermind.Consensus.Processing; +using Nethermind.TxPool; +using Nethermind.Int256; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading.Tasks; + +namespace Nethermind.Benchmarks.Core +{ + [MemoryDiagnoser] + public class RecoverSignaturesBenchmark + { + private ISpecProvider _specProvider = MainnetSpecProvider.Instance; + + private static EthereumEcdsa _ethereumEcdsa; + private static RecoverSignatures _sut; + + private Block _block100TxWith100AuthSigs; + private Block _block100TxWith10AuthSigs; + private Block _block100TxWith1AuthSigs; + private Block _block3TxWith1AuthSigs; + private Block _block10TxWith0AuthSigs; + private Block _block10TxWith10AuthSigs; + + private static PrivateKey[] _privateKeys = Enumerable.Range(0, 1000) + .Select(i => Build.A.PrivateKey.TestObject) + .ToArray(); + + [GlobalSetup] + public void GlobalSetup() + { + _ethereumEcdsa = new(_specProvider.ChainId); + _sut = new(_ethereumEcdsa, NullTxPool.Instance, _specProvider, NullLogManager.Instance); + + var rnd = new Random(); + + _block100TxWith100AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(100, 100)) + .TestObject; + _block100TxWith10AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(100, 10)) + .TestObject; + + _block100TxWith1AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(100, 1)) + .TestObject; + + _block10TxWith10AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(10, 10)) + .TestObject; + + _block3TxWith1AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(3, 1)) + .TestObject; + + _block10TxWith0AuthSigs = Build.A.Block + .WithHeader(new BlockHeader() + { + Timestamp = ulong.MaxValue, + Number = long.MaxValue + }) + .WithTransactions(CreateTransactions(10, 0)) + .TestObject; + + Transaction[] CreateTransactions(int txCount, int authPerTx) + { + var list = new List(); + for (int i = 0; i < txCount; i++) + { + PrivateKey signer = _privateKeys[i]; + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithAuthorizationCode( + Enumerable.Range(0, authPerTx).Select(y => + { + PrivateKey authority = _privateKeys[i + y + _privateKeys.Length / 2]; + return CreateAuthorizationTuple( + authority, + (ulong)rnd.NextInt64(), + Address.Zero, + (ulong)rnd.NextInt64()); + }).ToArray() + ) + .SignedAndResolved(signer) + .WithSenderAddress(null) + .TestObject; + list.Add(tx); + } + return list.ToArray(); + } + + static AuthorizationTuple CreateAuthorizationTuple(PrivateKey signer, ulong chainId, Address codeAddress, ulong nonce) + { + AuthorizationTupleDecoder decoder = new(); + RlpStream rlp = decoder.EncodeWithoutSignature(chainId, codeAddress, nonce); + Span code = stackalloc byte[rlp.Length + 1]; + code[0] = Eip7702Constants.Magic; + rlp.Data.AsSpan().CopyTo(code.Slice(1)); + + Signature sig = _ethereumEcdsa.Sign(signer, Keccak.Compute(code)); + + return new AuthorizationTuple(chainId, codeAddress, nonce, sig); + } + } + + [IterationCleanup] + public void IterationCleanup() + { + ResetSigs(_block100TxWith100AuthSigs); + ResetSigs(_block100TxWith10AuthSigs); + ResetSigs(_block100TxWith1AuthSigs); + ResetSigs(_block10TxWith10AuthSigs); + ResetSigs(_block10TxWith0AuthSigs); + ResetSigs(_block3TxWith1AuthSigs); + + void ResetSigs(Block block) + { + Parallel.ForEach(block.Transactions, (t) => + { + t.SenderAddress = null; + t.Hash = null; + Parallel.ForEach(t.AuthorizationList, (tuple) => + { + tuple.Authority = null; + }); + }); + } + } + + [Benchmark] + public void Recover100TxSignatureswith100AuthoritySignatures() + { + _sut.RecoverData(_block100TxWith100AuthSigs); + } + + [Benchmark] + public void Recover100TxSignatureswith10AuthoritySignatures() + { + _sut.RecoverData(_block100TxWith10AuthSigs); + } + + [Benchmark] + public void Recover100TxSignaturesWith1AuthoritySignatures() + { + _sut.RecoverData(_block100TxWith1AuthSigs); + } + + [Benchmark] + public void Recover10TxSignaturesWith10AuthoritySignatures() + { + _sut.RecoverData(_block10TxWith10AuthSigs); + } + + [Benchmark] + public void Recover3TxSignaturesWith1AuthoritySignatures() + { + _sut.RecoverData(_block3TxWith1AuthSigs); + } + + [Benchmark] + public void Recover10TxSignaturesWith0AuthoritySignatures() + { + _sut.RecoverData(_block10TxWith0AuthSigs); + } + } +} diff --git a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs index 840c63e6f9f..88274cfd17b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/ReorgTests.cs @@ -48,16 +48,16 @@ public void Setup() .WithSpecProvider(specProvider) .TestObject; + CodeInfoRepository codeInfoRepository = new(); TxPool.TxPool txPool = new( ecdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(specProvider, _blockTree, stateProvider), + new ChainHeadInfoProvider(specProvider, _blockTree, stateProvider, codeInfoRepository), new TxPoolConfig(), new TxValidator(specProvider.ChainId), LimboLogs.Instance, transactionComparerProvider.GetDefaultComparer()); BlockhashProvider blockhashProvider = new(_blockTree, specProvider, stateProvider, LimboLogs.Instance); - CodeInfoRepository codeInfoRepository = new(); VirtualMachine virtualMachine = new( blockhashProvider, specProvider, diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs index c7cd07b95e1..f52adce9ba7 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Numerics; using FluentAssertions; +using Nethermind.Consensus.Messages; using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -58,7 +59,7 @@ public void Zero_r_is_not_valid() txValidator.IsWellFormed(tx, MuirGlacier.Instance).AsBool().Should().BeFalse(); } - private static byte CalculateV() => (byte)EthereumEcdsa.CalculateV(TestBlockchainIds.ChainId); + private static byte CalculateV() => (byte)EthereumEcdsaExtensions.CalculateV(TestBlockchainIds.ChainId); [Test, MaxTime(Timeout.MaxTestTime)] public void Zero_s_is_not_valid() @@ -522,6 +523,129 @@ public void IsWellFormed_BlobTxHasProofOverTheSizeLimit_ReturnFalse() Assert.That(txValidator.IsWellFormed(tx, Cancun.Instance).AsBool(), Is.False); } + [Test] + public void IsWellFormed_CreateTxInSetCode_ReturnsFalse() + { + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(TxType.SetCode) + .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, [], [])) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .SignedAndResolved() + .WithTo(null); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.Multiple(() => + { + ValidationResult validationResult = txValidator.IsWellFormed(tx, Prague.Instance); + Assert.That(validationResult.AsBool(), Is.False); + Assert.That(validationResult.Error, Is.EqualTo(TxErrorMessages.NotAllowedCreateTransaction)); + }); + } + + [Test] + public void IsWellFormed_AuthorizationListTxInPragueSpec_ReturnsTrue() + { + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressA) + .WithAuthorizationCode(new AuthorizationTuple(0, TestItem.AddressA, 0, 0, [], [])) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .SignedAndResolved(); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.True); + } + + [Test] + public void IsWellFormed_EmptyAuthorizationList_ReturnsFalse() + { + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressA) + .WithAuthorizationCode([]) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .SignedAndResolved(); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.False); + } + [Test] + public void IsWellFormed_NullAuthorizationList_ReturnsFalse() + { + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressA) + .WithAuthorizationCode((AuthorizationTuple[])null!) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .SignedAndResolved(); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.False); + } + + private static object[] BadSignatures = + { + new object[] { 1ul, (UInt256)1, Secp256K1Curve.HalfNPlusOne, false}, + new object[] { 1ul, UInt256.Zero, Secp256K1Curve.HalfN, true }, + new object[] { 0ul, UInt256.Zero, UInt256.Zero, true }, + }; + [TestCaseSource(nameof(BadSignatures))] + public void IsWellFormed_AuthorizationTupleHasBadSignature_ReturnsFalse(ulong yParity, UInt256 r, UInt256 s, bool expected) + { + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressA) + .WithAuthorizationCode(new AuthorizationTuple(0, Address.Zero, 0, new Signature(r, s, yParity + Signature.VOffset))) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .SignedAndResolved(); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).AsBool, Is.EqualTo(expected)); + } + + private static IEnumerable NonSetCodeTypes() => + Enum.GetValues().Where(t => t != TxType.SetCode && t != TxType.DepositTx); + + [TestCaseSource(nameof(NonSetCodeTypes))] + public void IsWellFormed_NonSetCodeTxHasAuthorizationList_ReturnsFalse(TxType type) + { + var x = Enum.GetValues().Where(t => t != TxType.SetCode); + TransactionBuilder txBuilder = Build.A.Transaction + .WithType(type) + .WithTo(TestItem.AddressA) + .WithMaxFeePerGas(100000) + .WithGasLimit(1000000) + .WithChainId(TestBlockchainIds.ChainId) + .WithShardBlobTxTypeAndFieldsIfBlobTx() + .WithAuthorizationCode(new AuthorizationTuple(TestBlockchainIds.ChainId, TestItem.AddressA, 0, new Signature(new byte[65]))) + .SignedAndResolved(); + + Transaction tx = txBuilder.TestObject; + TxValidator txValidator = new(TestBlockchainIds.ChainId); + + Assert.That(txValidator.IsWellFormed(tx, Prague.Instance).Error, Is.EqualTo(TxErrorMessages.NotAllowedAuthorizationList)); + } + private static byte[] MakeArray(int count, params byte[] elements) => elements.Take(Math.Min(count, elements.Length)) .Concat(new byte[Math.Max(0, count - elements.Length)]) diff --git a/src/Nethermind/Nethermind.Blockchain/ChainHeadInfoProvider.cs b/src/Nethermind/Nethermind.Blockchain/ChainHeadInfoProvider.cs index f843345bc38..3c90e3dc736 100644 --- a/src/Nethermind/Nethermind.Blockchain/ChainHeadInfoProvider.cs +++ b/src/Nethermind/Nethermind.Blockchain/ChainHeadInfoProvider.cs @@ -17,28 +17,31 @@ namespace Nethermind.Blockchain { public class ChainHeadInfoProvider : IChainHeadInfoProvider { - public ChainHeadInfoProvider(ISpecProvider specProvider, IBlockTree blockTree, IStateReader stateReader) - : this(new ChainHeadSpecProvider(specProvider, blockTree), blockTree, new ChainHeadReadOnlyStateProvider(blockTree, stateReader)) + public ChainHeadInfoProvider(ISpecProvider specProvider, IBlockTree blockTree, IStateReader stateReader, ICodeInfoRepository codeInfoRepository) + : this(new ChainHeadSpecProvider(specProvider, blockTree), blockTree, new ChainHeadReadOnlyStateProvider(blockTree, stateReader), codeInfoRepository) { } - public ChainHeadInfoProvider(ISpecProvider specProvider, IBlockTree blockTree, IAccountStateProvider stateProvider) - : this(new ChainHeadSpecProvider(specProvider, blockTree), blockTree, stateProvider) + public ChainHeadInfoProvider(ISpecProvider specProvider, IBlockTree blockTree, IReadOnlyStateProvider stateProvider, ICodeInfoRepository codeInfoRepository) + : this(new ChainHeadSpecProvider(specProvider, blockTree), blockTree, stateProvider, codeInfoRepository) { } - public ChainHeadInfoProvider(IChainHeadSpecProvider specProvider, IBlockTree blockTree, IAccountStateProvider stateProvider) + public ChainHeadInfoProvider(IChainHeadSpecProvider specProvider, IBlockTree blockTree, IReadOnlyStateProvider stateProvider, ICodeInfoRepository codeInfoRepository) { SpecProvider = specProvider; - AccountStateProvider = stateProvider; + ReadOnlyStateProvider = stateProvider; HeadNumber = blockTree.BestKnownNumber; + CodeInfoRepository = codeInfoRepository; blockTree.BlockAddedToMain += OnHeadChanged; } public IChainHeadSpecProvider SpecProvider { get; } - public IAccountStateProvider AccountStateProvider { get; } + public IReadOnlyStateProvider ReadOnlyStateProvider { get; } + + public ICodeInfoRepository CodeInfoRepository { get; } public long HeadNumber { get; private set; } diff --git a/src/Nethermind/Nethermind.Cli/NodeManager.cs b/src/Nethermind/Nethermind.Cli/NodeManager.cs index c392a499e59..75cfe3df20f 100644 --- a/src/Nethermind/Nethermind.Cli/NodeManager.cs +++ b/src/Nethermind/Nethermind.Cli/NodeManager.cs @@ -69,11 +69,9 @@ public async Task PostJint(string method, params object[] parameters) } else { - Stopwatch stopwatch = new(); - stopwatch.Start(); + long startTime = Stopwatch.GetTimestamp(); object? result = await _currentClient.Post(method, parameters); - stopwatch.Stop(); - decimal totalMicroseconds = stopwatch.ElapsedTicks * (1_000_000m / Stopwatch.Frequency); + decimal totalMicroseconds = (decimal)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds; Colorful.Console.WriteLine($"Request complete in {totalMicroseconds}μs"); if (result is bool boolResult) @@ -121,11 +119,9 @@ public async Task PostJint(string method, params object[] parameters) } else { - Stopwatch stopwatch = new(); - stopwatch.Start(); + long starting = Stopwatch.GetTimestamp(); result = await _currentClient.Post(method, parameters); - stopwatch.Stop(); - decimal totalMicroseconds = stopwatch.ElapsedTicks * (1_000_000m / Stopwatch.Frequency); + decimal totalMicroseconds = (decimal)Stopwatch.GetElapsedTime(starting).TotalMicroseconds; Colorful.Console.WriteLine($"Request complete in {totalMicroseconds}μs"); } } diff --git a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs index 2769e365735..83e2f3b5372 100644 --- a/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs +++ b/src/Nethermind/Nethermind.Clique.Test/CliqueBlockProducerTests.cs @@ -107,9 +107,10 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f ITransactionComparerProvider transactionComparerProvider = new TransactionComparerProvider(specProvider, blockTree); + CodeInfoRepository codeInfoRepository = new(); TxPool.TxPool txPool = new(_ethereumEcdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(GoerliSpecProvider.Instance), blockTree, stateProvider), + new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(GoerliSpecProvider.Instance), blockTree, stateProvider, codeInfoRepository), new TxPoolConfig(), new TxValidator(goerliSpecProvider.ChainId), _logManager, @@ -127,7 +128,6 @@ public On CreateNode(PrivateKey privateKey, bool withGenesisAlreadyProcessed = f _genesis.Header.Hash = _genesis.Header.CalculateHash(); _genesis3Validators.Header.Hash = _genesis3Validators.Header.CalculateHash(); - CodeInfoRepository codeInfoRepository = new(); TransactionProcessor transactionProcessor = new(goerliSpecProvider, stateProvider, new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, nodeLogManager), codeInfoRepository, @@ -424,9 +424,8 @@ private void WaitForNumber(PrivateKey nodeKey, long number) { if (_logger.IsInfo) _logger.Info($"WAITING ON {nodeKey.Address} FOR BLOCK {number}"); SpinWait spinWait = new(); - Stopwatch stopwatch = new(); - stopwatch.Start(); - while (stopwatch.ElapsedMilliseconds < _timeout) + long startTime = Stopwatch.GetTimestamp(); + while (Stopwatch.GetElapsedTime(startTime).TotalMilliseconds < _timeout) { spinWait.SpinOnce(); if (_blockTrees[nodeKey].Head.Number >= number) diff --git a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs index f7a929174d6..72b5446a97d 100644 --- a/src/Nethermind/Nethermind.Config/IBlocksConfig.cs +++ b/src/Nethermind/Nethermind.Config/IBlocksConfig.cs @@ -37,10 +37,10 @@ public interface IBlocksConfig : IConfig [ConfigItem(Description = "Whether to pre-warm the state when processing blocks. This can lead to an up to 2x speed-up in the main loop block processing.", DefaultValue = "True")] bool PreWarmStateOnBlockProcessing { get; set; } - [ConfigItem(Description = "Block Production timeout, in milliseconds.", DefaultValue = "4000")] + [ConfigItem(Description = "The block production timeout, in milliseconds.", DefaultValue = "4000")] int BlockProductionTimeoutMs { get; set; } - [ConfigItem(Description = "Genesis block load timeout, in milliseconds.", DefaultValue = "40000")] + [ConfigItem(Description = "The genesis block load timeout, in milliseconds.", DefaultValue = "40000")] int GenesisTimeoutMs { get; set; } byte[] GetExtraDataBytes(); diff --git a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs index cf7dbf413fc..d2a3952e8f0 100644 --- a/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs +++ b/src/Nethermind/Nethermind.Consensus.AuRa/InitializationSteps/InitializeBlockchainAuRa.cs @@ -24,6 +24,7 @@ using Nethermind.Consensus.Validators; using Nethermind.Consensus.Withdrawals; using Nethermind.Core; +using Nethermind.Evm; using Nethermind.Init.Steps; using Nethermind.Logging; using Nethermind.State; @@ -256,24 +257,25 @@ private IComparer CreateTxPoolTxComparer(TxPriorityContract? txPrio return CreateTxPoolTxComparer(); } - protected override TxPool.TxPool CreateTxPool() + protected override TxPool.TxPool CreateTxPool(CodeInfoRepository codeInfoRepository) { // This has to be different object than the _processingReadOnlyTransactionProcessorSource as this is in separate thread - var txPriorityContract = TxAuRaFilterBuilders.CreateTxPrioritySources(_api); - var localDataSource = _api.TxPriorityContractLocalDataSource; + TxPriorityContract txPriorityContract = TxAuRaFilterBuilders.CreateTxPrioritySources(_api); + TxPriorityContract.LocalDataSource? localDataSource = _api.TxPriorityContractLocalDataSource; ReportTxPriorityRules(txPriorityContract, localDataSource); - var minGasPricesContractDataStore = TxAuRaFilterBuilders.CreateMinGasPricesDataStore(_api, txPriorityContract, localDataSource); + DictionaryContractDataStore? minGasPricesContractDataStore + = TxAuRaFilterBuilders.CreateMinGasPricesDataStore(_api, txPriorityContract, localDataSource); ITxFilter txPoolFilter = TxAuRaFilterBuilders.CreateAuRaTxFilterForProducer(_api, minGasPricesContractDataStore); return new TxPool.TxPool( - _api.EthereumEcdsa, + _api.EthereumEcdsa!, _api.BlobTxStorage ?? NullBlobTxStorage.Instance, - new ChainHeadInfoProvider(_api.SpecProvider, _api.BlockTree, _api.StateReader), + new ChainHeadInfoProvider(_api.SpecProvider!, _api.BlockTree!, _api.StateReader!, codeInfoRepository), NethermindApi.Config(), - _api.TxValidator, + _api.TxValidator!, _api.LogManager, CreateTxPoolTxComparer(txPriorityContract, localDataSource), _api.TxGossipPolicy, diff --git a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs index 470c9ed17f5..f85842a83ca 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs @@ -501,7 +501,7 @@ ILogManager logManager selectedTxs, Array.Empty(), spec.WithdrawalsEnabled ? Enumerable.Empty() : null, - spec.ConsensusRequestsEnabled ? 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.Test/CensorshipDetectorTests.cs b/src/Nethermind/Nethermind.Consensus.Test/CensorshipDetectorTests.cs index 4e8ba989451..a3ead8f5cff 100644 --- a/src/Nethermind/Nethermind.Consensus.Test/CensorshipDetectorTests.cs +++ b/src/Nethermind/Nethermind.Consensus.Test/CensorshipDetectorTests.cs @@ -16,6 +16,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Db; +using Nethermind.Evm; using Nethermind.Logging; using Nethermind.Specs; using Nethermind.Specs.Forks; @@ -256,7 +257,7 @@ private TxPool.TxPool CreatePool(bool eip1559Enabled = true) return new( _ethereumEcdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(_specProvider, _blockTree, _stateProvider), + new ChainHeadInfoProvider(_specProvider, _blockTree, _stateProvider, new CodeInfoRepository()), new TxPoolConfig(), new TxValidator(_specProvider.ChainId), _logManager, diff --git a/src/Nethermind/Nethermind.Consensus.Test/RecoverSignaturesTest.cs b/src/Nethermind/Nethermind.Consensus.Test/RecoverSignaturesTest.cs new file mode 100644 index 00000000000..8b9825e0c94 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus.Test/RecoverSignaturesTest.cs @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Test.Builders; +using Nethermind.Core; +using NUnit.Framework; +using Nethermind.Consensus.Processing; +using NSubstitute; +using Nethermind.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Logging; +using Nethermind.TxPool; +using System.Linq; +using Nethermind.Core.Crypto; + +namespace Nethermind.Consensus.Test; +[TestFixture] +public class RecoverSignaturesTest +{ + private static readonly IEthereumEcdsa _ecdsa = new EthereumEcdsa(BlockchainIds.GenericNonRealNetwork); + + [Test] + public void RecoverData_SenderIsNotRecoveredAndNotInPool_SenderAndAuthorityIsRecovered() + { + PrivateKey signer = TestItem.PrivateKeyA; + PrivateKey authority = TestItem.PrivateKeyB; + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithAuthorizationCode(_ecdsa.Sign(authority, 0, Address.Zero, 0)) + .SignedAndResolved(signer) + .WithSenderAddress(null) + .TestObject; + + Block block = Build.A.Block + .WithTransactions([tx]) + .TestObject; + + ISpecProvider specProvider = Substitute.For(); + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsAuthorizationListEnabled.Returns(true); + specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); + RecoverSignatures sut = new( + _ecdsa, + NullTxPool.Instance, + specProvider, + Substitute.For()); + + sut.RecoverData(block); + + Assert.That(tx.SenderAddress, Is.EqualTo(signer.Address)); + Assert.That(tx.AuthorizationList.First().Authority, Is.EqualTo(authority.Address)); + } + + [Test] + public void RecoverData_TxIsInPool_SenderAndAuthoritiesIsSetToSameAsInPool() + { + PrivateKey signer = TestItem.PrivateKeyA; + PrivateKey poolSender = TestItem.PrivateKeyB; + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithAuthorizationCode + ([ + new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[65]), null), + new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[65]), null), + ]) + .SignedAndResolved(signer) + .WithSenderAddress(null) + .TestObject; + + Block block = Build.A.Block + .WithTransactions([tx]) + .TestObject; + + Transaction txInPool = Build.A.Transaction + .WithType(TxType.SetCode) + .WithAuthorizationCode + ([ + new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[65]), poolSender.Address), + new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[65]), poolSender.Address), + ]) + .SignedAndResolved(poolSender) + .TestObject; + ITxPool txPool = Substitute.For(); + txPool + .TryGetPendingTransaction(Arg.Any(), out Arg.Any()) + .Returns(x => + { + x[1] = txInPool; + return true; + }); + ISpecProvider specProvider = Substitute.For(); + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsAuthorizationListEnabled.Returns(true); + specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); + RecoverSignatures sut = new( + _ecdsa, + txPool, + specProvider, + Substitute.For()); + + sut.RecoverData(block); + + Assert.That(tx.SenderAddress, Is.EqualTo(poolSender.Address)); + Assert.That(tx.AuthorizationList.Select(a => a.Authority), Is.All.EqualTo(poolSender.Address)); + } +} diff --git a/src/Nethermind/Nethermind.Consensus.Test/TargetAdjustedGasLimitCalculatorTests.cs b/src/Nethermind/Nethermind.Consensus.Test/TargetAdjustedGasLimitCalculatorTests.cs index 2f96501987d..e049e77b6e2 100644 --- a/src/Nethermind/Nethermind.Consensus.Test/TargetAdjustedGasLimitCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Consensus.Test/TargetAdjustedGasLimitCalculatorTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only +using FluentAssertions; using Nethermind.Config; using Nethermind.Core; using Nethermind.Core.Test.Builders; @@ -29,5 +30,21 @@ public void Is_bump_on_1559_eip_block() long actualValue = targetedAdjustedGasLimitCalculator.GetGasLimit(header); Assert.That(actualValue, Is.EqualTo(gasLimit * Eip1559Constants.DefaultElasticityMultiplier)); } + + [TestCase(30_000_000, 100_000_000, 30029295)] + public void Is_calculating_correct_gasLimit(long currentGasLimit, long targetGasLimit, long expectedGasLimit) + { + int blockNumber = 20_000_000; + long gasLimit = currentGasLimit; + TestSpecProvider specProvider = new(Prague.Instance); + TargetAdjustedGasLimitCalculator targetedAdjustedGasLimitCalculator = new(specProvider, + new BlocksConfig() + { + TargetBlockGasLimit = targetGasLimit + }); + BlockHeader header = Build.A.BlockHeader.WithNumber(blockNumber - 1).WithGasLimit(gasLimit).TestObject; + long actualValue = targetedAdjustedGasLimitCalculator.GetGasLimit(header); + actualValue.Should().Be(expectedGasLimit); + } } } diff --git a/src/Nethermind/Nethermind.Consensus/GC/GCScheduler.cs b/src/Nethermind/Nethermind.Consensus/GC/GCScheduler.cs index c66daea0de1..ddb2d5caee4 100644 --- a/src/Nethermind/Nethermind.Consensus/GC/GCScheduler.cs +++ b/src/Nethermind/Nethermind.Consensus/GC/GCScheduler.cs @@ -6,6 +6,8 @@ using System.Runtime; using System.Threading; using System.Threading.Tasks; +using Nethermind.Core.Extensions; +using Nethermind.Core.Memory; namespace Nethermind.Consensus; @@ -180,7 +182,8 @@ public bool GCCollect(int generation, GCCollectionMode mode, bool blocking, bool // Reset the block counter after GC _countToGC = MaxBlocksWithoutGC; System.GC.Collect(generation, mode, blocking: blocking, compacting: compacting); - + // Also trim native memory used by Db + MallocHelper.Instance.MallocTrim((uint)1.MiB()); // Indicate that GC has finished MarkGCResumed(); diff --git a/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs b/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs index 8eab24494cf..2a8082895ff 100644 --- a/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs +++ b/src/Nethermind/Nethermind.Consensus/Messages/TxErrorMessages.cs @@ -16,6 +16,8 @@ public static string InvalidTxType(string name) => $"InvalidTxType: Transaction type in {name} is not supported."; public const string IntrinsicGasTooLow = "IntrinsicGasTooLow: Gas limit is too low."; + public const string TxMissingTo = + "TxMissingTo: Must be set."; public const string InvalidTxSignature = "InvalidTxSignature: Signature is invalid."; @@ -37,8 +39,8 @@ public static string InvalidTxChainId(ulong expected, ulong? actual) => public const string InvalidTransaction = $"InvalidTransaction: Cannot be {nameof(ShardBlobNetworkWrapper)}."; - public const string TxMissingTo = - "TxMissingTo: Must be set."; + public const string NotAllowedCreateTransaction = + "NotAllowedCreateTransaction: To must be set."; public const string BlobTxMissingMaxFeePerBlobGas = "BlobTxMissingMaxFeePerBlobGas: Must be set."; @@ -70,6 +72,12 @@ public static string InvalidTxChainId(ulong expected, ulong? actual) => public static readonly string InvalidBlobProofSize = $"InvalidBlobProofSize: Cannot be more than {Ckzg.Ckzg.BytesPerProof}."; + public const string NotAllowedAuthorizationList = $"NotAllowedAuthorizationList: Only transactions with type {nameof(TxType.SetCode)} can have authorization_list."; + + public const string MissingAuthorizationList = "MissingAuthorizationList: Must be set."; + + public const string InvalidAuthoritySignature = "InvalidAuthoritySignature: Invalid signature in authorization list."; + public const string InvalidBlobCommitmentHash = "InvalidBlobCommitmentHash: Commitment hash does not match."; diff --git a/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/CensorshipDetectorConfig.cs b/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/CensorshipDetectorConfig.cs index c350c8bf8b4..b4e3bb243fd 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/CensorshipDetectorConfig.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/CensorshipDetectorConfig.cs @@ -5,7 +5,7 @@ namespace Nethermind.Consensus.Processing.CensorshipDetector; public class CensorshipDetectorConfig : ICensorshipDetectorConfig { - public bool Enabled { get; set; } = true; + public bool Enabled { get; set; } = false; public uint BlockCensorshipThreshold { get; set; } = 2; public string[]? AddressesForCensorshipDetection { get; set; } = null; } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/ICensorshipDetectorConfig.cs b/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/ICensorshipDetectorConfig.cs index 29dec2ddf4d..e71ffff02e1 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/ICensorshipDetectorConfig.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/CensorshipDetector/ICensorshipDetectorConfig.cs @@ -7,15 +7,13 @@ namespace Nethermind.Consensus.Processing.CensorshipDetector; public interface ICensorshipDetectorConfig : IConfig { - [ConfigItem(DefaultValue = "true", - Description = "Enabling censorship detection feature")] + [ConfigItem(DefaultValue = "false", Description = "Whether to enable censorship detection.")] bool Enabled { get; set; } [ConfigItem(DefaultValue = "2", - Description = "Number of consecutive blocks with detected potential censorship to report censorship attempt")] + Description = "The number of the consecutive blocks with detected potential censorship to report.")] uint BlockCensorshipThreshold { get; set; } - [ConfigItem(DefaultValue = "null", - Description = "The addresses for which censorship is being detected.")] + [ConfigItem(DefaultValue = "null", Description = "The addresses to detect censorship for.")] string[]? AddressesForCensorshipDetection { get; set; } } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs index fd4e888d9d5..48845fb904d 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/ProcessingStats.cs @@ -239,17 +239,17 @@ void IThreadPoolWorkItem.Execute() _logger.Info($"- Block{(chunkBlocks > 1 ? $"s {chunkBlocks,-9:N0}" : " ")}{(chunkBlocks == 1 ? mgasColor : "")} {chunkMGas,9:F2}{resetColor} MGas | {chunkTx,8:N0} txs | calls {callsColor}{chunkCalls,6:N0}{resetColor} {darkGreyText}({chunkEmptyCalls,3:N0}){resetColor} | sload {chunkSload,7:N0} | sstore {sstoreColor}{chunkSstore,6:N0}{resetColor} | create {createsColor}{chunkCreates,3:N0}{resetColor}{(currentSelfDestructs - _lastSelfDestructs > 0 ? $"{darkGreyText}({-(currentSelfDestructs - _lastSelfDestructs),3:N0}){resetColor}" : "")}"); if (recoveryQueue > 0 || processingQueue > 0) { - _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} t/s | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); + _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | recover {recoveryQueue,5:N0} | process {processingQueue,5:N0}"); } else { - _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} t/s | {bps,7:F2} Blk/s | exec code {resetColor} from cache {cachedContractsUsed,7:N0} |{resetColor} new {contractsAnalysed,6:N0}"); + _logger.Info($"- Block throughput {mgasPerSecondColor}{mgasPerSecond,9:F2}{resetColor} MGas/s{(mgasPerSecond > 1000 ? "🔥" : " ")}| {txps,10:N1} tps | {bps,7:F2} Blk/s | exec code {resetColor} from cache {cachedContractsUsed,7:N0} |{resetColor} new {contractsAnalysed,6:N0}"); } // Only output the total throughput in debug mode if (_logger.IsDebug) { - _logger.Debug($"- Total throughput {totalMgasPerSecond,9:F2} MGas/s | {totalTxPerSecond,9:F2} t/s | {totalBlocksPerSecond,7:F2} Blk/s |⛽ Gas gwei: {Evm.Metrics.MinGasPrice:N2} .. {Math.Max(Evm.Metrics.MinGasPrice, Evm.Metrics.EstMedianGasPrice):N2} ({Evm.Metrics.AveGasPrice:N2}) .. {Evm.Metrics.MaxGasPrice:N2}"); + _logger.Debug($"- Total throughput {totalMgasPerSecond,9:F2} MGas/s | {totalTxPerSecond,9:F2} tps | {totalBlocksPerSecond,7:F2} Blk/s |⛽ Gas gwei: {Evm.Metrics.MinGasPrice:N2} .. {Math.Max(Evm.Metrics.MinGasPrice, Evm.Metrics.EstMedianGasPrice):N2} ({Evm.Metrics.AveGasPrice:N2}) .. {Evm.Metrics.MaxGasPrice:N2}"); } } diff --git a/src/Nethermind/Nethermind.Consensus/Processing/RecoverSignature.cs b/src/Nethermind/Nethermind.Consensus/Processing/RecoverSignature.cs index 64b4dcd0cbb..c748ecae850 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/RecoverSignature.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/RecoverSignature.cs @@ -2,12 +2,14 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Crypto; using Nethermind.Logging; +using Nethermind.Serialization.Rlp; using Nethermind.TxPool; namespace Nethermind.Consensus.Processing @@ -60,7 +62,7 @@ public void RecoverData(Block block) // Don't access txPool in Parallel loop as increases contention foreach (Transaction tx in txs) { - if (!ShouldRecoverSender(tx)) + if (!ShouldRecoverSignatures(tx)) continue; Transaction? poolTx = null; @@ -84,22 +86,38 @@ public void RecoverData(Block block) { recoverFromEcdsa++; } + + if (poolTx is not null && tx.HasAuthorizationList) + { + for (int i = 0; i < tx.AuthorizationList.Length; i++) + { + if (poolTx.AuthorizationList[i].Authority is not null) + { + tx.AuthorizationList[i].Authority = poolTx.AuthorizationList[i].Authority; + } + else if (tx.AuthorizationList[i].Authority is null) + { + recoverFromEcdsa++; + } + } + } } if (recoverFromEcdsa == 0) return; - bool useSignatureChainId = !_specProvider.GetSpec(block.Header).ValidateChainId; + IReleaseSpec releaseSpec = _specProvider.GetSpec(block.Header); + bool useSignatureChainId = !releaseSpec.ValidateChainId; if (recoverFromEcdsa > 3) { // Recover ecdsa in Parallel Parallel.For(0, txs.Length, i => { Transaction tx = txs[i]; - if (!ShouldRecoverSender(tx)) return; - - tx.SenderAddress = _ecdsa.RecoverAddress(tx, useSignatureChainId); + if (!ShouldRecoverSignatures(tx)) return; + tx.SenderAddress ??= _ecdsa.RecoverAddress(tx, useSignatureChainId); + RecoverAuthorities(tx); if (_logger.IsTrace) _logger.Trace($"Recovered {tx.SenderAddress} sender for {tx.Hash}"); }); } @@ -107,17 +125,41 @@ public void RecoverData(Block block) { foreach (Transaction tx in txs) { - if (!ShouldRecoverSender(tx)) continue; - - tx.SenderAddress = _ecdsa.RecoverAddress(tx, useSignatureChainId); + if (!ShouldRecoverSignatures(tx)) continue; + tx.SenderAddress ??= _ecdsa.RecoverAddress(tx, useSignatureChainId); + RecoverAuthorities(tx); if (_logger.IsTrace) _logger.Trace($"Recovered {tx.SenderAddress} sender for {tx.Hash}"); } } + + void RecoverAuthorities(Transaction tx) + { + if (!releaseSpec.IsAuthorizationListEnabled + || !tx.HasAuthorizationList) + { + return; + } + + if (tx.AuthorizationList.Length > 3) + { + Parallel.ForEach(tx.AuthorizationList.Where(t => t.Authority is null), (tuple) => + { + tuple.Authority = _ecdsa.RecoverAddress(tuple); + }); + } + else + { + foreach (AuthorizationTuple tuple in tx.AuthorizationList.AsSpan()) + { + tuple.Authority ??= _ecdsa.RecoverAddress(tuple); + } + } + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool ShouldRecoverSender(Transaction tx) - => tx.IsSigned && tx.SenderAddress is null; + private static bool ShouldRecoverSignatures(Transaction tx) + => tx.IsSigned && (tx.SenderAddress is null || (tx.HasAuthorizationList && tx.AuthorizationList.Any(a => a.Authority is null))); } } diff --git a/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs index d44a33466a1..cbc155d1d9c 100644 --- a/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Requests/ConsensusRequestsProcessor.cs @@ -16,22 +16,24 @@ 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(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) { - if (spec.DepositsEnabled || spec.WithdrawalRequestsEnabled) - { - using ArrayPoolList requestsList = new(receipts.Length * 2); + if (!spec.RequestsEnabled) + return; - requestsList.AddRange(_depositsProcessor.ProcessDeposits(block, receipts, spec)); - requestsList.AddRange(_withdrawalRequestsProcessor.ReadWithdrawalRequests(block, state, spec)); + using ArrayPoolList requestsList = new(receipts.Length * 2); - ConsensusRequest[] requests = requestsList.ToArray(); - Hash256 root = new RequestsTrie(requests).RootHash; - block.Body.Requests = requests; - block.Header.RequestsRoot = root; - } + requestsList.AddRange(_depositsProcessor.ProcessDeposits(block, receipts, spec)); + requestsList.AddRange(_withdrawalRequestsProcessor.ReadRequests(block, state, spec)); + requestsList.AddRange(_consolidationRequestsProcessor.ReadRequests(block, state, spec)); + + 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..9d18024b080 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/ConsolidationRequestProcessor.cs @@ -0,0 +1,44 @@ +// 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.Evm.TransactionProcessing; +using Nethermind.State; + +namespace Nethermind.Consensus.Requests; + +// https://eips.ethereum.org/EIPS/eip-7251#block-processing +public class ConsolidationRequestsProcessor(ITransactionProcessor transactionProcessor) : RequestProcessor(transactionProcessor), IRequestProcessor +{ + private const int SizeOfClass = 20 + 48 + 48; + + public IEnumerable ReadRequests(Block block, IWorldState state, IReleaseSpec spec) + { + return base.ReadRequests(block, state, spec, spec.Eip7251ContractAddress); + } + protected override bool IsEnabledInSpec(IReleaseSpec spec) + { + return spec.ConsolidationRequestsEnabled; + } + protected override IEnumerable ParseResult(Memory result) + { + int count = result.Length / SizeOfClass; + + for (int i = 0; i < count; ++i) + { + int offset = i * SizeOfClass; + ConsolidationRequest request = new() + { + SourceAddress = new Address(result.Slice(offset, 20).ToArray()), + SourcePubkey = result.Slice(offset + 20, 48), + TargetPubkey = result.Slice(offset + 68, 48) + }; + + yield return request; + } + } +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/IWithdrawalRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/IRequestProcessor.cs similarity index 56% rename from src/Nethermind/Nethermind.Consensus/Requests/IWithdrawalRequestsProcessor.cs rename to src/Nethermind/Nethermind.Consensus/Requests/IRequestProcessor.cs index ba396ed1441..6f33453fdfb 100644 --- a/src/Nethermind/Nethermind.Consensus/Requests/IWithdrawalRequestsProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Requests/IRequestProcessor.cs @@ -1,15 +1,14 @@ // 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 Nethermind.Core.Specs; using Nethermind.State; +using System.Collections.Generic; namespace Nethermind.Consensus.Requests; -public interface IWithdrawalRequestsProcessor +public interface IRequestProcessor { - IEnumerable ReadWithdrawalRequests(Block block, IWorldState state, IReleaseSpec spec); + IEnumerable ReadRequests(Block block, IWorldState state, IReleaseSpec spec); } diff --git a/src/Nethermind/Nethermind.Consensus/Requests/RequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/RequestProcessor.cs new file mode 100644 index 00000000000..c9c40c8dcd1 --- /dev/null +++ b/src/Nethermind/Nethermind.Consensus/Requests/RequestProcessor.cs @@ -0,0 +1,53 @@ +// 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.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; + +public abstract class RequestProcessor(ITransactionProcessor transactionProcessor) +{ + private const long GasLimit = 30_000_000L; + + public IEnumerable ReadRequests(Block block, IWorldState state, IReleaseSpec spec, Address contractAddress) + { + if (!IsEnabledInSpec(spec)) + return Array.Empty(); + + if (!state.AccountExists(contractAddress)) + return Array.Empty(); + + CallOutputTracer tracer = new(); + + Transaction? transaction = new() + { + Value = UInt256.Zero, + Data = Array.Empty(), + To = contractAddress, + 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) + return Enumerable.Empty(); + return ParseResult(tracer.ReturnValue); + } + + + protected abstract bool IsEnabledInSpec(IReleaseSpec spec); + protected abstract IEnumerable ParseResult(Memory result); +} diff --git a/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs b/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs index 564f2573f5d..86483add1a0 100644 --- a/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/Requests/WithdrawalRequestsProcessor.cs @@ -4,68 +4,43 @@ using System; using System.Buffers.Binary; 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-7002#block-processing -public class WithdrawalRequestsProcessor(ITransactionProcessor transactionProcessor) : IWithdrawalRequestsProcessor +public class WithdrawalRequestsProcessor(ITransactionProcessor transactionProcessor) : RequestProcessor(transactionProcessor), IRequestProcessor { - private const long GasLimit = 30_000_000L; private const int SizeOfClass = 20 + 48 + 8; - public IEnumerable ReadWithdrawalRequests(Block block, IWorldState state, IReleaseSpec spec) + public IEnumerable ReadRequests(Block block, IWorldState state, IReleaseSpec spec) { - if (!spec.IsEip7002Enabled || !state.AccountExists(spec.Eip7002ContractAddress)) - { - yield break; - } + return ReadRequests(block, state, spec, spec.Eip7002ContractAddress); + } - byte[]? result = ExecuteTransaction(block, spec); - if (result?.Length > 0) - { - int count = result.Length / SizeOfClass; - for (int i = 0; i < count; ++i) - { - Memory memory = result.AsMemory(i * SizeOfClass, SizeOfClass); - WithdrawalRequest request = new() - { - SourceAddress = new Address(memory.Slice(0, 20).AsArray()), - ValidatorPubkey = memory.Slice(20, 48), - Amount = BinaryPrimitives.ReadUInt64BigEndian(memory.Slice(68, 8).Span) - }; - yield return request; - } - } + protected override bool IsEnabledInSpec(IReleaseSpec spec) + { + return spec.WithdrawalRequestsEnabled; } - private byte[]? ExecuteTransaction(Block block, IReleaseSpec spec) + protected override IEnumerable ParseResult(Memory result) { - CallOutputTracer tracer = new(); - Transaction transaction = new() + int count = result.Length / SizeOfClass; + for (int i = 0; i < count; ++i) { - 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); - - return tracer.ReturnValue; + int offset = i * SizeOfClass; + WithdrawalRequest request = new() + { + SourceAddress = new Address(result.Slice(offset, 20).AsArray()), + ValidatorPubkey = result.Slice(offset + 20, 48), + Amount = BinaryPrimitives.ReadUInt64BigEndian(result.Slice(offset + 68, 8).Span) + }; + yield return request; + } } } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs index 88ed3ec3dc7..8c1edb4db43 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs @@ -288,7 +288,7 @@ public bool ValidateRequests(Block block, out string? error) => private bool ValidateRequests(Block block, IReleaseSpec spec, out string? error) { - if (spec.ConsensusRequestsEnabled && block.Requests is null) + if (spec.RequestsEnabled && block.Requests is null) { error = BlockErrorMessages.MissingRequests; @@ -297,7 +297,7 @@ private bool ValidateRequests(Block block, IReleaseSpec spec, out string? error) return false; } - if (!spec.ConsensusRequestsEnabled && block.Requests is not null) + if (!spec.RequestsEnabled && block.Requests is not null) { error = BlockErrorMessages.RequestsNotEnabled; diff --git a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs index 5207135b109..070ab579360 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs @@ -11,6 +11,7 @@ using Nethermind.Crypto; using Nethermind.Evm; using Nethermind.Int256; +using System.Linq; namespace Nethermind.Consensus.Validators; @@ -25,6 +26,7 @@ public TxValidator(ulong chainId) new LegacySignatureTxValidator(chainId), ContractSizeTxValidator.Instance, NonBlobFieldsTxValidator.Instance, + NonSetCodeFieldsTxValidator.Instance ])); RegisterValidator(TxType.AccessList, new CompositeTxValidator([ new ReleaseSpecTxValidator(static spec => spec.IsEip2930Enabled), @@ -33,6 +35,7 @@ public TxValidator(ulong chainId) new ExpectedChainIdTxValidator(chainId), ContractSizeTxValidator.Instance, NonBlobFieldsTxValidator.Instance, + NonSetCodeFieldsTxValidator.Instance ])); RegisterValidator(TxType.EIP1559, new CompositeTxValidator([ new ReleaseSpecTxValidator(static spec => spec.IsEip1559Enabled), @@ -42,6 +45,7 @@ public TxValidator(ulong chainId) GasFieldsTxValidator.Instance, ContractSizeTxValidator.Instance, NonBlobFieldsTxValidator.Instance, + NonSetCodeFieldsTxValidator.Instance ])); RegisterValidator(TxType.Blob, new CompositeTxValidator([ new ReleaseSpecTxValidator(static spec => spec.IsEip4844Enabled), @@ -51,7 +55,19 @@ public TxValidator(ulong chainId) GasFieldsTxValidator.Instance, ContractSizeTxValidator.Instance, BlobFieldsTxValidator.Instance, - MempoolBlobTxValidator.Instance + MempoolBlobTxValidator.Instance, + NonSetCodeFieldsTxValidator.Instance + ])); + RegisterValidator(TxType.SetCode, new CompositeTxValidator([ + new ReleaseSpecTxValidator(static spec => spec.IsEip7702Enabled), + IntrinsicGasTxValidator.Instance, + SignatureTxValidator.Instance, + new ExpectedChainIdTxValidator(chainId), + GasFieldsTxValidator.Instance, + ContractSizeTxValidator.Instance, + NonBlobFieldsTxValidator.Instance, + NoContractCreationTxValidator.Instance, + AuthorizationListTxValidator.Instance, ])); } @@ -150,6 +166,18 @@ private NonBlobFieldsTxValidator() { } }; } +public sealed class NonSetCodeFieldsTxValidator : ITxValidator +{ + public static readonly NonSetCodeFieldsTxValidator Instance = new(); + private NonSetCodeFieldsTxValidator() { } + + public ValidationResult IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec) => transaction switch + { + { AuthorizationList: not null } => TxErrorMessages.NotAllowedAuthorizationList, + _ => ValidationResult.Success + }; +} + public sealed class BlobFieldsTxValidator : ITxValidator { public static readonly BlobFieldsTxValidator Instance = new(); @@ -282,3 +310,33 @@ public sealed class SignatureTxValidator : BaseSignatureTxValidator public static readonly SignatureTxValidator Instance = new(); private SignatureTxValidator() { } } + +public sealed class NoContractCreationTxValidator : ITxValidator +{ + public static readonly NoContractCreationTxValidator Instance = new(); + private NoContractCreationTxValidator() { } + public ValidationResult IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec) => + transaction.IsContractCreation ? TxErrorMessages.NotAllowedCreateTransaction : ValidationResult.Success; +} + +public sealed class AuthorizationListTxValidator : ITxValidator +{ + public static readonly AuthorizationListTxValidator Instance = new(); + private AuthorizationListTxValidator() { } + + public ValidationResult IsWellFormed(Transaction transaction, IReleaseSpec releaseSpec) => + transaction.AuthorizationList switch + { + null or { Length: 0 } => TxErrorMessages.MissingAuthorizationList, + var authorizationList when authorizationList.Any(a => !ValidateAuthoritySignature(a.AuthoritySignature)) => + TxErrorMessages.InvalidAuthoritySignature, + _ => ValidationResult.Success + }; + + private bool ValidateAuthoritySignature(Signature signature) + { + UInt256 sValue = new(signature.SAsSpan, isBigEndian: true); + + return sValue < Secp256K1Curve.HalfNPlusOne && signature.RecoveryId is 0 or 1; + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs index 3558e7b8461..2af6892ee13 100644 --- a/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs +++ b/src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs @@ -38,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; @@ -181,15 +182,16 @@ protected virtual async Task Build(ISpecProvider? specProvider = ReadOnlyState = new ChainHeadReadOnlyStateProvider(BlockTree, StateReader); TransactionComparerProvider = new TransactionComparerProvider(SpecProvider, BlockTree); - TxPool = CreateTxPool(); + CodeInfoRepository codeInfoRepository = new(); + TxPool = CreateTxPool(codeInfoRepository); IChainHeadInfoProvider chainHeadInfoProvider = - new ChainHeadInfoProvider(SpecProvider, BlockTree, StateReader); + new ChainHeadInfoProvider(SpecProvider, BlockTree, StateReader, codeInfoRepository); - NonceManager = new NonceManager(chainHeadInfoProvider.AccountStateProvider); + NonceManager = new NonceManager(chainHeadInfoProvider.ReadOnlyStateProvider); _trieStoreWatcher = new TrieStoreBoundaryWatcher(WorldStateManager, BlockTree, LogManager); - CodeInfoRepository codeInfoRepository = new(); + ReceiptStorage = new InMemoryReceiptStorage(blockTree: BlockTree); VirtualMachine virtualMachine = new(new BlockhashProvider(BlockTree, SpecProvider, State, LogManager), SpecProvider, codeInfoRepository, LogManager); TxProcessor = new TransactionProcessor(SpecProvider, State, virtualMachine, codeInfoRepository, LogManager); @@ -315,12 +317,12 @@ protected virtual IBlockProducerRunner CreateBlockProducerRunner() public virtual ILogManager LogManager { get; set; } = LimboLogs.Instance; - protected virtual TxPool.TxPool CreateTxPool() => + protected virtual TxPool.TxPool CreateTxPool(CodeInfoRepository codeInfoRepository) => new( EthereumEcdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(SpecProvider), BlockTree, ReadOnlyState), - new TxPoolConfig() { BlobsSupport = BlobsSupportMode.InMemory }, + new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(SpecProvider), BlockTree, ReadOnlyState, codeInfoRepository), + new TxPoolConfig { BlobsSupport = BlobsSupportMode.InMemory }, new TxValidator(SpecProvider.ChainId), LogManager, TransactionComparerProvider.GetDefaultComparer()); @@ -362,8 +364,11 @@ protected virtual Block GetGenesisBlock() genesisBlockBuilder.WithParentBeaconBlockRoot(Keccak.Zero); } - if (SpecProvider.GenesisSpec.ConsensusRequestsEnabled) + if (SpecProvider.GenesisSpec.RequestsEnabled) + { genesisBlockBuilder.WithConsensusRequests(0); + } + genesisBlockBuilder.WithStateRoot(State.StateRoot); return genesisBlockBuilder.TestObject; diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs index a43039ef8af..4b5e591949b 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs @@ -281,7 +281,7 @@ public BlockBuilder WithConsensusRequests(int count) var consensusRequests = new ConsensusRequest[count]; for (var i = 0; i < count; i++) - consensusRequests[i] = new(); + consensusRequests[i] = new Deposit(); return WithConsensusRequests(consensusRequests); } 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/TestItem.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs index bae4135790c..ac8cf8e0313 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs @@ -115,6 +115,13 @@ public static Hash256 KeccakFromNumber(int i) 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/TransactionBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs index cb8c9e984f3..cf81f10a348 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs @@ -7,7 +7,6 @@ using Nethermind.Core.Eip2930; using Nethermind.Crypto; using Nethermind.Int256; -using Nethermind.Logging; namespace Nethermind.Core.Test.Builders { @@ -218,6 +217,22 @@ public TransactionBuilder WithShardBlobTxTypeAndFields(int blobCount = 1, boo return this; } + public TransactionBuilder WithAuthorizationCodeIfAuthorizationListTx() + { + return TestObjectInternal.Type == TxType.SetCode ? WithAuthorizationCode(new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[64], 0))) : this; + } + + public TransactionBuilder WithAuthorizationCode(AuthorizationTuple authTuple) + { + TestObjectInternal.AuthorizationList = TestObjectInternal.AuthorizationList is not null ? [.. TestObjectInternal.AuthorizationList, authTuple] : [authTuple]; + return this; + } + public TransactionBuilder WithAuthorizationCode(AuthorizationTuple[] authList) + { + TestObjectInternal.AuthorizationList = authList; + return this; + } + public TransactionBuilder With(Action anyChange) { anyChange(TestObjectInternal); @@ -282,5 +297,11 @@ public TransactionBuilder WithIsServiceTransaction(bool isServiceTransaction) TestObjectInternal.IsServiceTransaction = isServiceTransaction; return this; } + + public TransactionBuilder From(T item) + { + TestObjectInternal = item; + return this; + } } } diff --git a/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs b/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs index 26ad8ee1554..8fd58d46c4c 100644 --- a/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Crypto/BlsSignerTests.cs @@ -8,6 +8,8 @@ namespace Nethermind.Core.Test.Crypto; +using G1 = Bls.P1; + [TestFixture] public class BlsTests { @@ -27,7 +29,9 @@ public void Verify_signature() { Bls.SecretKey sk = new(SkBytes, Bls.ByteOrder.LittleEndian); BlsSigner.Signature s = BlsSigner.Sign(sk, MsgBytes); - Assert.That(BlsSigner.Verify(BlsSigner.GetPublicKey(sk).ToAffine(), s, MsgBytes)); + G1 publicKey = new(); + publicKey.FromSk(sk); + Assert.That(BlsSigner.Verify(publicKey.ToAffine(), s, MsgBytes)); } [Test] @@ -40,13 +44,19 @@ public void Rejects_bad_signature() bytes[34] += 1; BlsSigner.Signature bad = new(bytes); - Assert.That(BlsSigner.Verify(BlsSigner.GetPublicKey(sk).ToAffine(), bad, MsgBytes), Is.False); + G1 publicKey = new(); + publicKey.FromSk(sk); + Assert.That(BlsSigner.Verify(publicKey.ToAffine(), bad, MsgBytes), Is.False); } [Test] public void Public_key_from_private_key() { byte[] expected = [0x95, 0x39, 0x27, 0x35, 0x0c, 0x35, 0x31, 0xb0, 0xbc, 0x58, 0x64, 0xcd, 0x9c, 0x5f, 0xe1, 0x34, 0x74, 0xca, 0x0c, 0x9b, 0x59, 0x99, 0x51, 0xa7, 0x76, 0xc4, 0xb9, 0x8d, 0xf6, 0x6a, 0x0e, 0x62, 0x07, 0xa8, 0x5c, 0x7f, 0x7a, 0x85, 0x1a, 0x0c, 0x02, 0x2a, 0x87, 0xc0, 0x29, 0xc3, 0x65, 0x61]; - Assert.That(BlsSigner.GetPublicKey(new(SkBytes, Bls.ByteOrder.LittleEndian)).Compress(), Is.EqualTo(expected)); + + G1 publicKey = new(); + publicKey.FromSk(new(SkBytes, Bls.ByteOrder.LittleEndian)); + + Assert.That(publicKey.Compress(), Is.EqualTo(expected)); } } diff --git a/src/Nethermind/Nethermind.Core.Test/Crypto/EthereumEcdsaTests.cs b/src/Nethermind/Nethermind.Core.Test/Crypto/EthereumEcdsaTests.cs index a11dc55dd69..64d98d18dea 100644 --- a/src/Nethermind/Nethermind.Core.Test/Crypto/EthereumEcdsaTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Crypto/EthereumEcdsaTests.cs @@ -1,10 +1,15 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; +using System.Net; +using Nethermind.Core.Crypto; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; +using Nethermind.Int256; using Nethermind.Logging; +using Nethermind.Serialization.Rlp; using NUnit.Framework; namespace Nethermind.Core.Test.Crypto @@ -73,5 +78,23 @@ public void Sign_generic_network() Address? address = ecdsa.RecoverAddress(tx); Assert.That(address, Is.EqualTo(key.Address)); } + + [Test] + [Repeat(3)] + public void RecoverAddress_AuthorizationTupleOfDifferentSize_RecoversAddressCorrectly() + { + PrivateKey signer = Build.A.PrivateKey.TestObject; + AuthorizationTuple authorizationTuple = new EthereumEcdsa(BlockchainIds.GenericNonRealNetwork) + .Sign(signer, + TestContext.CurrentContext.Random.NextULong(), + Build.A.Address.TestObjectInternal, + TestContext.CurrentContext.Random.NextULong()); + + EthereumEcdsa ecdsa = new(BlockchainIds.GenericNonRealNetwork); + + Address? authority = ecdsa.RecoverAddress(authorizationTuple); + + Assert.That(authority, Is.EqualTo(signer.Address)); + } } } diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs new file mode 100644 index 00000000000..0d243601940 --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/AuthorizationTupleDecoderTests.cs @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using FluentAssertions; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using Nethermind.Serialization.Rlp; +using NSubstitute.ExceptionExtensions; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Core.Test.Encoding; + +[TestFixture] +public class AuthorizationTupleDecoderTests +{ + public static IEnumerable AuthorizationTupleEncodeCases() + { + yield return new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[64], 0)); + yield return new AuthorizationTuple(0, Address.Zero, 0, new Signature(new byte[64], 0)); + yield return new AuthorizationTuple( + ulong.MaxValue, + new Address(Enumerable.Range(0, 20).Select(i => (byte)0xff).ToArray()), + ulong.MaxValue, + new Signature(Enumerable.Range(0, 64).Select(i => (byte)0xff).ToArray(), 1)); + } + + [TestCaseSource(nameof(AuthorizationTupleEncodeCases))] + public void Encode_TupleHasValues_TupleCanBeDecodedToEquivalentTuple(AuthorizationTuple item) + { + AuthorizationTupleDecoder sut = new(); + + RlpStream result = sut.Encode(item); + result.Position = 0; + + sut.Decode(result).Should().BeEquivalentTo(item); + } + + [Test] + public void DecodeValueDecoderContext_CodeAddressIsNull_ThrowsRlpException() + { + RlpStream stream = TupleRlpStreamWithNull(); + + AuthorizationTupleDecoder sut = new(); + Assert.That(() => + { + Rlp.ValueDecoderContext decoderContext = new Rlp.ValueDecoderContext(stream.Data); + sut.Decode(ref decoderContext, RlpBehaviors.None); + } + , Throws.TypeOf()); + } + + private static RlpStream TupleRlpStreamWithNull() + { + Address? codeAddress = null; + Signature sig = new(new byte[64], 0); + int length = + +Rlp.LengthOf(1) + + Rlp.LengthOf(codeAddress) + + Rlp.LengthOf(0) + + Rlp.LengthOf(sig.RecoveryId) + + Rlp.LengthOf(sig.R) + + Rlp.LengthOf(sig.S); + RlpStream stream = new RlpStream(Rlp.LengthOfSequence(length)); + stream.StartSequence(length); + stream.Encode(1); + stream.Encode(codeAddress); + stream.Encode(0); + stream.Encode(sig.RecoveryId); + stream.Encode(sig.R); + stream.Encode(sig.S); + stream.Position = 0; + return stream; + } +} diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs index 3fc4c35588c..c311fb36a7f 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/ConsensusRequestDecoderTests.cs @@ -50,6 +50,26 @@ public void Roundtrip_withdrawalRequest() 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() { @@ -62,7 +82,7 @@ public void Should_decode_deposit_with_ValueDecoderContext() Amount = int.MaxValue }; RlpStream stream = new(1024); - ConsensusRequestDecoder codec = new(); + ConsensusRequestDecoder codec = ConsensusRequestDecoder.Instance; codec.Encode(stream, deposit); @@ -82,7 +102,7 @@ public void Should_decode_withdrawalRequest_with_ValueDecoderContext() Amount = int.MaxValue }; RlpStream stream = new(1024); - ConsensusRequestDecoder codec = new(); + ConsensusRequestDecoder codec = ConsensusRequestDecoder.Instance; codec.Encode(stream, withdrawalRequest); @@ -93,7 +113,27 @@ public void Should_decode_withdrawalRequest_with_ValueDecoderContext() } [Test] - public void Should_encode_deposit_same_for_Rlp_Encode_and_DepositDecoder_Encode() + 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 = ConsensusRequestDecoder.Instance; + + 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() { @@ -103,14 +143,14 @@ public void Should_encode_deposit_same_for_Rlp_Encode_and_DepositDecoder_Encode( WithdrawalCredentials = KeccakTests.KeccakOfAnEmptyString.ToBytes(), Amount = int.MaxValue }; - byte[] rlp1 = new ConsensusRequestDecoder().Encode(deposit).Bytes; + byte[] rlp1 = ConsensusRequestDecoder.Instance.Encode(deposit).Bytes; byte[] rlp2 = Rlp.Encode(deposit).Bytes; rlp1.Should().BeEquivalentTo(rlp2); } [Test] - public void Should_encode_withdrawalRequest_same_for_Rlp_Encode_and_DepositDecoder_Encode() + public void Should_encode_withdrawalRequest_same_for_Rlp_Encode_and_ConsensusRequestDecoder_Encode() { ConsensusRequest withdrawalRequest = new WithdrawalRequest() { @@ -118,12 +158,27 @@ public void Should_encode_withdrawalRequest_same_for_Rlp_Encode_and_DepositDecod ValidatorPubkey = KeccakTests.KeccakOfAnEmptyString.ToBytes(), Amount = int.MaxValue }; - byte[] rlp1 = new ConsensusRequestDecoder().Encode(withdrawalRequest).Bytes; + byte[] rlp1 = ConsensusRequestDecoder.Instance.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 = ConsensusRequestDecoder.Instance.Encode(consolidationRequest).Bytes; + byte[] rlp2 = Rlp.Encode(consolidationRequest).Bytes; + + rlp1.Should().BeEquivalentTo(rlp2); + } + [Test] public void Should_encode_ConsensusRequests_Array() { @@ -142,12 +197,18 @@ public void Should_encode_ConsensusRequests_Array() 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()); + ConsensusRequest[] decoded = Rlp.DecodeArray(rlpStream, ConsensusRequestDecoder.Instance); decoded.Should().BeEquivalentTo(requests); } } diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs index bf70a7b46f9..ced26306e3a 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs @@ -8,6 +8,7 @@ using FluentAssertions; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Logging; using Nethermind.Serialization.Rlp; @@ -24,7 +25,7 @@ public partial class ShardBlobTxDecoderTests public static Task SetUp() => KzgPolynomialCommitments.InitializeAsync(); public static IEnumerable<(Transaction, string)> TestCaseSource() => - TxDecoderTests.TestObjectsSource().Select(tos => (tos.Item1 + TxDecoderTests.TestCaseSource().Select(tos => (Build.A.Transaction.From(tos.Item1) .WithChainId(TestBlockchainIds.ChainId) .WithShardBlobTxTypeAndFields(2, false) .SignedAndResolved() diff --git a/src/Nethermind/Nethermind.Core/AccountStateProviderExtensions.cs b/src/Nethermind/Nethermind.Core/AccountStateProviderExtensions.cs index 45a8c09be5f..b07d3e9ebdd 100644 --- a/src/Nethermind/Nethermind.Core/AccountStateProviderExtensions.cs +++ b/src/Nethermind/Nethermind.Core/AccountStateProviderExtensions.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core.Specs; +using System; namespace Nethermind.Core { @@ -9,8 +10,5 @@ public static class AccountStateProviderExtensions { public static bool HasCode(this IAccountStateProvider stateProvider, Address address) => stateProvider.TryGetAccount(address, out AccountStruct account) && account.HasCode; - - public static bool IsInvalidContractSender(this IAccountStateProvider stateProvider, IReleaseSpec spec, Address address) => - spec.IsEip3607Enabled && stateProvider.HasCode(address); } } diff --git a/src/Nethermind/Nethermind.Core/Address.cs b/src/Nethermind/Nethermind.Core/Address.cs index f09c9e5b76d..01507842989 100644 --- a/src/Nethermind/Nethermind.Core/Address.cs +++ b/src/Nethermind/Nethermind.Core/Address.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.Diagnostics; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; @@ -19,6 +20,7 @@ namespace Nethermind.Core { [JsonConverter(typeof(AddressConverter))] [TypeConverter(typeof(AddressTypeConverter))] + [DebuggerDisplay("{ToString()}")] public class Address : IEquatable
, IComparable
{ public const int Size = 20; @@ -26,6 +28,8 @@ public class Address : IEquatable
, IComparable
private const int PrefixedHexCharsCount = 2 + HexCharsCount; // 0x5a4eab120fb44eb6684e5e32785702ff45ea344d public static Address Zero { get; } = new(new byte[Size]); + public static Address MaxValue { get; } = new("0xffffffffffffffffffffffffffffffffffffffff"); + public const string SystemUserHex = "0xfffffffffffffffffffffffffffffffffffffffe"; public static Address SystemUser { get; } = new(SystemUserHex); diff --git a/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs b/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs new file mode 100644 index 00000000000..69fd3b53768 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/AuthorizationTuple.cs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using System; + +namespace Nethermind.Core; +public class AuthorizationTuple( + ulong chainId, + Address codeAddress, + ulong nonce, + Signature sig, + Address? authority = null) +{ + public AuthorizationTuple( + ulong chainId, + Address codeAddress, + ulong nonce, + ulong yParity, + byte[] r, + byte[] s, + Address? authority = null) : this(chainId, codeAddress, nonce, new Signature(r, s, yParity + Signature.VOffset), authority) + { } + + public ulong ChainId { get; } = chainId; + public Address CodeAddress { get; protected set; } = codeAddress; + public ulong Nonce { get; } = nonce; + public Signature AuthoritySignature { get; protected set; } = sig; + + /// + /// may be recovered at a later point. + /// + public Address? Authority { get; set; } = authority; + + public override string ToString() => $"Delegation authorization from {Authority} to {CodeAddress} on chain {ChainId} with Nonce {Nonce}"; +} diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index c43e1ee0b0a..827c592d5e6 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -363,5 +363,6 @@ public void Dispose() public Span AsSpan() => _array.AsSpan(0, Count); - public ReadOnlyMemory AsMemory() => new(_array, 0, Count); + public Memory AsMemory() => new(_array, 0, Count); + public ReadOnlyMemory AsReadOnlyMemory() => new(_array, 0, Count); } diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs index b5cbf9f8e34..e9e5f6d38e1 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsensusRequest.cs @@ -10,39 +10,23 @@ namespace Nethermind.Core.ConsensusRequests; public enum ConsensusRequestsType : byte { Deposit = 0, - WithdrawalRequest = 1 + WithdrawalRequest = 1, + ConsolidationRequest = 2 } -public class ConsensusRequest +public abstract 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 Memory? 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) GetTypeCounts(this ConsensusRequest[]? requests) + 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++) { @@ -50,35 +34,45 @@ public static (int depositCount, int withdrawalRequestCount) GetTypeCounts(this { depositCount++; } - else + else if (requests[i].Type == ConsensusRequestsType.WithdrawalRequest) { withdrawalRequestCount++; } + else + { + consolidationRequestCount++; + } } - return (depositCount, withdrawalRequestCount); + return (depositCount, withdrawalRequestCount, consolidationRequestCount); } - public static (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests) SplitRequests(this ConsensusRequest[]? requests) + public static (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) SplitRequests(this ConsensusRequest[]? requests) { - if (requests is null) return (null, null); - (int depositCount, int withdrawalRequestCount) = requests.GetTypeCounts(); + 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 + else if (requests[i].Type == ConsensusRequestsType.WithdrawalRequest) { withdrawalRequests[withdrawalRequestIndex++] = (WithdrawalRequest)requests[i]; } + else + { + consolidationRequests[consolidationRequestIndex++] = (ConsolidationRequest)requests[i]; + } } - return (deposits, withdrawalRequests); + 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..abf79b924a1 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/ConsolidationRequest.cs @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only +using System; +using Nethermind.Core.Extensions; + +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; set; } + public Memory? SourcePubkey { get; set; } + + public Memory? TargetPubkey { get; set; } + + public override string ToString() => ToString(string.Empty); + + public string ToString(string indentation) => @$"{indentation}{nameof(ConsolidationRequest)} + {{ {nameof(SourceAddress)}: {SourceAddress}, + {nameof(SourcePubkey)}: {SourcePubkey?.Span.ToHexString()}, + {nameof(TargetPubkey)}: {TargetPubkey?.Span.ToHexString()}, + }}"; + + +} diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs index 09da1d1f8a8..50b46fc27c2 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/Deposit.cs @@ -3,7 +3,6 @@ using System; using Nethermind.Core.Extensions; -using System.Text; namespace Nethermind.Core.ConsensusRequests; @@ -17,34 +16,13 @@ public Deposit() Type = ConsensusRequestsType.Deposit; Amount = 0; } - public Memory? Pubkey - { - get { return PubKeyField; } - set { PubKeyField = value; } - } + public Memory? Pubkey { get; set; } + public byte[]? WithdrawalCredentials { get; set; } - public byte[]? WithdrawalCredentials - { - get { return WithdrawalCredentialsField; } - set { WithdrawalCredentialsField = value; } - } + public ulong Amount { get; set; } - 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 byte[]? Signature { get; set; } + public ulong? Index { get; set; } public override string ToString() => ToString(string.Empty); public string ToString(string indentation) => @$"{indentation}{nameof(Deposit)} diff --git a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs index 355205d5174..f9d79f127c7 100644 --- a/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs +++ b/src/Nethermind/Nethermind.Core/ConsensusRequests/WithdrawalRequest.cs @@ -17,23 +17,11 @@ public WithdrawalRequest() Type = ConsensusRequestsType.WithdrawalRequest; Amount = 0; } - public Address? SourceAddress - { - get { return SourceAddressField; } - set { SourceAddressField = value; } - } + public Address? SourceAddress { get; set; } - public Memory? ValidatorPubkey - { - get { return PubKeyField; } - set { PubKeyField = value; } - } + public Memory? ValidatorPubkey { get; set; } - public ulong Amount - { - get { return AmountField; } - set { AmountField = value; } - } + public ulong Amount { get; set; } public override string ToString() => ToString(string.Empty); public string ToString(string indentation) => @$"{indentation}{nameof(WithdrawalRequest)} diff --git a/src/Nethermind/Nethermind.Core/Eip2930/AccessList.cs b/src/Nethermind/Nethermind.Core/Eip2930/AccessList.cs index 86bc94d0809..9372639ce52 100644 --- a/src/Nethermind/Nethermind.Core/Eip2930/AccessList.cs +++ b/src/Nethermind/Nethermind.Core/Eip2930/AccessList.cs @@ -26,6 +26,7 @@ private AccessList(List<(Address address, int count)> addresses, List k public static AccessList Empty { get; } = new(new List<(Address, int)>(), new List()); public bool IsEmpty => _addresses.Count == 0; + public (int AddressesCount, int StorageKeysCount) Count => (_addresses.Count, _keys.Count); public class Builder { @@ -72,26 +73,21 @@ public AccessList Build() IEnumerator<(Address Address, StorageKeysEnumerable StorageKeys)> IEnumerable<(Address Address, StorageKeysEnumerable StorageKeys)>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public struct Enumerator : IEnumerator<(Address Address, StorageKeysEnumerable StorageKeys)>, IEnumerator<(Address Address, IEnumerable StorageKeys)> + public struct Enumerator(AccessList accessList) : IEnumerator<(Address Address, StorageKeysEnumerable StorageKeys)>, + IEnumerator<(Address Address, IEnumerable StorageKeys)> { - private readonly AccessList _accessList; private int _index = -1; private int _keysIndex = 0; - public Enumerator(AccessList accessList) - { - _accessList = accessList; - } - public bool MoveNext() { _index++; if (_index > 0) { - _keysIndex += CollectionsMarshal.AsSpan(_accessList._addresses)[_index - 1].count; + _keysIndex += CollectionsMarshal.AsSpan(accessList._addresses)[_index - 1].count; } - return _index < _accessList._addresses.Count; + return _index < accessList._addresses.Count; } public void Reset() @@ -104,8 +100,8 @@ public readonly (Address Address, StorageKeysEnumerable StorageKeys) Current { get { - ref readonly var addressCount = ref CollectionsMarshal.AsSpan(_accessList._addresses)[_index]; - return (addressCount.address, new StorageKeysEnumerable(_accessList, _keysIndex, addressCount.count)); + ref readonly var addressCount = ref CollectionsMarshal.AsSpan(accessList._addresses)[_index]; + return (addressCount.address, new StorageKeysEnumerable(accessList, _keysIndex, addressCount.count)); } } 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/Eip7702Constants.cs b/src/Nethermind/Nethermind.Core/Eip7702Constants.cs new file mode 100644 index 00000000000..d54ff84f4b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Eip7702Constants.cs @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Core; +public static class Eip7702Constants +{ + public const byte Magic = 0x05; + public static ReadOnlySpan DelegationHeader => [0xef, 0x01, 0x00]; + private static readonly int HeaderLength = DelegationHeader.Length; + public static bool IsDelegatedCode(ReadOnlySpan code) => + code.Length == HeaderLength + Address.Size + && DelegationHeader.SequenceEqual(code.Slice(0, DelegationHeader.Length)); +} diff --git a/src/Nethermind/Nethermind.Core/PubSub/CompositePublisher.cs b/src/Nethermind/Nethermind.Core/PubSub/CompositePublisher.cs index 31261d6a34d..cfb3541fa4b 100644 --- a/src/Nethermind/Nethermind.Core/PubSub/CompositePublisher.cs +++ b/src/Nethermind/Nethermind.Core/PubSub/CompositePublisher.cs @@ -16,6 +16,7 @@ public CompositePublisher(params IPublisher[] publishers) public async Task PublishAsync(T data) where T : class { + // TODO: .Net 9 stackalloc Task[] tasks = new Task[_publishers.Length]; for (int i = 0; i < _publishers.Length; i++) { diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index cf33cf5e4ec..6c4f7c550a4 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -283,6 +283,15 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec 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 /// @@ -299,6 +308,11 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// bool IsEip6780Enabled { get; } + /// + /// Transactions that allows code delegation for EOA + /// + bool IsEip7702Enabled { get; } + /// /// Blob base fee collection for Gnosis /// @@ -399,6 +413,8 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec public bool BlobBaseFeeEnabled => IsEip4844Enabled; - public bool ConsensusRequestsEnabled => WithdrawalRequestsEnabled || DepositsEnabled; + bool IsAuthorizationListEnabled => IsEip7702Enabled; + + public bool RequestsEnabled => ConsolidationRequestsEnabled || WithdrawalRequestsEnabled || DepositsEnabled; } } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index 324a5aac919..ac41ca6978b 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -71,10 +71,13 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public Address DepositContractAddress => spec.DepositContractAddress; public bool IsEip7002Enabled => spec.IsEip7002Enabled; public Address Eip7002ContractAddress => spec.Eip7002ContractAddress; + public bool IsEip7251Enabled => spec.IsEip7251Enabled; + public Address Eip7251ContractAddress => spec.Eip7251ContractAddress; public virtual bool IsEip2935Enabled => spec.IsEip2935Enabled; public virtual bool IsEip7709Enabled => spec.IsEip7709Enabled; public virtual Address Eip2935ContractAddress => spec.Eip2935ContractAddress; public virtual bool IsEip6780Enabled => spec.IsEip6780Enabled; + public bool IsEip7702Enabled => spec.IsEip7702Enabled; public virtual bool IsRip7212Enabled => spec.IsRip7212Enabled; public virtual bool IsOpGraniteEnabled => spec.IsOpGraniteEnabled; public virtual bool IsOntakeEnabled => spec.IsOntakeEnabled; diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 84184aecc8d..6783c28d18f 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -4,6 +4,8 @@ using System; using System.Buffers; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json.Serialization; @@ -49,6 +51,7 @@ public class Transaction public bool SupportsAccessList => Type >= TxType.AccessList && Type != TxType.DepositTx; public bool Supports1559 => Type >= TxType.EIP1559 && Type != TxType.DepositTx; public bool SupportsBlobs => Type == TxType.Blob && Type != TxType.DepositTx; + public bool SupportsAuthorizationList => Type == TxType.SetCode && Type != TxType.DepositTx; public long GasLimit { get; set; } public Address? To { get; set; } public UInt256 Value { get; set; } @@ -59,6 +62,12 @@ public class Transaction public bool IsContractCreation => To is null; public bool IsMessageCall => To is not null; + [MemberNotNullWhen(true, nameof(AuthorizationList))] + public bool HasAuthorizationList => + Type == TxType.SetCode && + AuthorizationList is not null && + AuthorizationList.Length > 0; + private Hash256? _hash; [JsonIgnore] @@ -164,6 +173,12 @@ private void ClearPreHashInternal() public object? NetworkWrapper { get; set; } + /// + /// List of EOA code authorizations. + /// https://eips.ethereum.org/EIPS/eip-7702 + /// + public AuthorizationTuple[]? AuthorizationList { get; set; } + /// /// Service transactions are free. The field added to handle baseFee validation after 1559 /// @@ -266,6 +281,7 @@ public bool Return(Transaction obj) obj.IsServiceTransaction = default; obj.PoolIndex = default; obj._size = default; + obj.AuthorizationList = default; return true; } @@ -296,6 +312,7 @@ public void CopyTo(Transaction tx) tx.IsServiceTransaction = IsServiceTransaction; tx.PoolIndex = PoolIndex; tx._size = _size; + tx.AuthorizationList = AuthorizationList; } } @@ -307,7 +324,10 @@ public class GeneratedTransaction : Transaction { } /// /// System transaction that is to be executed by the node without including in the block. /// - public class SystemTransaction : Transaction { } + public class SystemTransaction : Transaction + { + private new const long GasLimit = 30_000_000L; + } /// /// Used inside Transaction::GetSize to calculate encoded transaction size diff --git a/src/Nethermind/Nethermind.Core/TxType.cs b/src/Nethermind/Nethermind.Core/TxType.cs index b642ca78121..8b9d2f65b92 100644 --- a/src/Nethermind/Nethermind.Core/TxType.cs +++ b/src/Nethermind/Nethermind.Core/TxType.cs @@ -9,6 +9,8 @@ public enum TxType : byte AccessList = 1, EIP1559 = 2, Blob = 3, - DepositTx = 0x7E + SetCode = 4, + + DepositTx = 0x7E, } } diff --git a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs index adcaf19f5fd..77e3baebc47 100644 --- a/src/Nethermind/Nethermind.Crypto/BlsSigner.cs +++ b/src/Nethermind/Nethermind.Crypto/BlsSigner.cs @@ -2,22 +2,23 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using System.Text; using Nethermind.Core.Collections; namespace Nethermind.Crypto; -using G1 = Bls.P1; using G1Affine = Bls.P1Affine; using G2 = Bls.P2; using G2Affine = Bls.P2Affine; using GT = Bls.PT; -public class BlsSigner +public static class BlsSigner { private static readonly byte[] Cryptosuite = Encoding.UTF8.GetBytes("BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"); private const int InputLength = 64; + [SkipLocalsInit] public static Signature Sign(Bls.SecretKey sk, ReadOnlySpan message) { G2 p = new(stackalloc long[G2.Sz]); @@ -26,16 +27,7 @@ public static Signature Sign(Bls.SecretKey sk, ReadOnlySpan message) return new(p.Compress()); } - public static void GetPublicKey(G1 res, Bls.SecretKey sk) - => res.FromSk(sk); - - public static G1 GetPublicKey(Bls.SecretKey sk) - { - G1 p = new(); - GetPublicKey(p, sk); - return p; - } - + [SkipLocalsInit] public static bool Verify(G1Affine publicKey, Signature signature, ReadOnlySpan message) { int len = 2 * GT.Sz; diff --git a/src/Nethermind/Nethermind.Crypto/EthereumEcdsa.cs b/src/Nethermind/Nethermind.Crypto/EthereumEcdsa.cs index 7b27a145d43..deecd2c9d11 100644 --- a/src/Nethermind/Nethermind.Crypto/EthereumEcdsa.cs +++ b/src/Nethermind/Nethermind.Crypto/EthereumEcdsa.cs @@ -5,8 +5,10 @@ using System.Globalization; using System.IO; using System.Numerics; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Serialization.Rlp; @@ -26,81 +28,15 @@ public class EthereumEcdsa : Ecdsa, IEthereumEcdsa BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", NumberStyles.HexNumber); + private readonly AuthorizationTupleDecoder _tupleDecoder = AuthorizationTupleDecoder.Instance; private readonly ulong _chainIdValue; + public ulong ChainId => _chainIdValue; public EthereumEcdsa(ulong chainId) { _chainIdValue = chainId; } - public void Sign(PrivateKey privateKey, Transaction tx, bool isEip155Enabled) - { - if (tx.Type != TxType.Legacy) - { - tx.ChainId = _chainIdValue; - } - - Hash256 hash = Keccak.Compute(Rlp.Encode(tx, true, isEip155Enabled, _chainIdValue).Bytes); - tx.Signature = Sign(privateKey, hash); - - if (tx.Type == TxType.Legacy && isEip155Enabled) - { - tx.Signature.V = tx.Signature.V + 8 + 2 * _chainIdValue; - } - } - - /// - /// - /// - /// - /// - /// - public bool Verify(Address sender, Transaction tx) - { - Address? recovered = RecoverAddress(tx); - return recovered?.Equals(sender) ?? false; - } - - /// - /// - /// - /// - /// - /// - public Address? RecoverAddress(Transaction tx, bool useSignatureChainId = false) - { - if (tx.Signature is null) - { - throw new InvalidDataException("Cannot recover sender address from a transaction without a signature."); - } - - useSignatureChainId &= tx.Signature.ChainId.HasValue; - - // feels like it is the same check twice - bool applyEip155 = useSignatureChainId - || tx.Signature.V == CalculateV(_chainIdValue, false) - || tx.Signature.V == CalculateV(_chainIdValue, true); - - ulong chainId; - switch (tx.Type) - { - case TxType.Legacy when useSignatureChainId: - chainId = tx.Signature.ChainId.Value; - break; - case TxType.Legacy: - chainId = _chainIdValue; - break; - default: - chainId = tx.ChainId!.Value; - break; - } - Hash256 hash = Keccak.Compute(Rlp.Encode(tx, true, applyEip155, chainId).Bytes); - - return RecoverAddress(tx.Signature, hash); - } - - public static ulong CalculateV(ulong chainId, bool addParity = true) => chainId * 2 + 35ul + (addParity ? 1u : 0u); - public Address? RecoverAddress(Signature signature, Hash256 message) { return RecoverAddress(signature.BytesWithRecovery, message); diff --git a/src/Nethermind/Nethermind.Crypto/EthereumEcdsaExtensions.cs b/src/Nethermind/Nethermind.Crypto/EthereumEcdsaExtensions.cs new file mode 100644 index 00000000000..3b7d939c936 --- /dev/null +++ b/src/Nethermind/Nethermind.Crypto/EthereumEcdsaExtensions.cs @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.IO; +using System.Runtime.CompilerServices; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Serialization.Rlp; + +namespace Nethermind.Crypto +{ + public static class EthereumEcdsaExtensions + { + public static AuthorizationTuple Sign(this IEthereumEcdsa ecdsa, PrivateKey signer, ulong chainId, Address codeAddress, ulong nonce) + { + using NettyRlpStream rlp = AuthorizationTupleDecoder.Instance.EncodeWithoutSignature(chainId, codeAddress, nonce); + Span preImage = stackalloc byte[rlp.Length + 1]; + preImage[0] = Eip7702Constants.Magic; + rlp.AsSpan().CopyTo(preImage.Slice(1)); + Signature sig = ecdsa.Sign(signer, Keccak.Compute(preImage)); + return new AuthorizationTuple(chainId, codeAddress, nonce, sig); + } + + public static void Sign(this IEthereumEcdsa ecdsa, PrivateKey privateKey, Transaction tx, bool isEip155Enabled = true) + { + if (tx.Type != TxType.Legacy) + { + tx.ChainId = ecdsa.ChainId; + } + + Hash256 hash = Keccak.Compute(Rlp.Encode(tx, true, isEip155Enabled, ecdsa.ChainId).Bytes); + tx.Signature = ecdsa.Sign(privateKey, hash); + + if (tx.Type == TxType.Legacy && isEip155Enabled) + { + tx.Signature.V = tx.Signature.V + 8 + 2 * ecdsa.ChainId; + } + } + + /// + /// + /// + /// + /// + /// + public static bool Verify(this IEthereumEcdsa ecdsa, Address sender, Transaction tx) + { + Address? recovered = ecdsa.RecoverAddress(tx); + return recovered?.Equals(sender) ?? false; + } + + /// + /// + /// + /// + /// + /// + public static Address? RecoverAddress(this IEthereumEcdsa ecdsa, Transaction tx, bool useSignatureChainId = false) + { + if (tx.Signature is null) + { + throw new InvalidDataException("Cannot recover sender address from a transaction without a signature."); + } + + useSignatureChainId &= tx.Signature.ChainId.HasValue; + + // feels like it is the same check twice + bool applyEip155 = useSignatureChainId + || tx.Signature.V == CalculateV(ecdsa.ChainId, false) + || tx.Signature.V == CalculateV(ecdsa.ChainId, true); + + ulong chainId; + switch (tx.Type) + { + case TxType.Legacy when useSignatureChainId: + chainId = tx.Signature.ChainId.Value; + break; + case TxType.Legacy: + chainId = ecdsa.ChainId; + break; + default: + chainId = tx.ChainId!.Value; + break; + } + Hash256 hash = Keccak.Compute(Rlp.Encode(tx, true, applyEip155, chainId).Bytes); + + return ecdsa.RecoverAddress(tx.Signature, hash); + } + + public static ulong CalculateV(ulong chainId, bool addParity = true) => chainId * 2 + 35ul + (addParity ? 1u : 0u); + + [SkipLocalsInit] + public static Address? RecoverAddress(this IEthereumEcdsa ecdsa, AuthorizationTuple tuple) + { + Span buffer = stackalloc byte[128]; + buffer[0] = Eip7702Constants.Magic; + using NettyRlpStream stream = AuthorizationTupleDecoder.Instance.EncodeWithoutSignature(tuple.ChainId, tuple.CodeAddress, tuple.Nonce); + stream.AsSpan().CopyTo(buffer.Slice(1)); + return ecdsa.RecoverAddress(tuple.AuthoritySignature, Keccak.Compute(buffer.Slice(0, stream.Length + 1))); + } + } +} diff --git a/src/Nethermind/Nethermind.Crypto/IEthereumEcdsa.cs b/src/Nethermind/Nethermind.Crypto/IEthereumEcdsa.cs index 2c72540a705..67f17f74d5b 100644 --- a/src/Nethermind/Nethermind.Crypto/IEthereumEcdsa.cs +++ b/src/Nethermind/Nethermind.Crypto/IEthereumEcdsa.cs @@ -4,15 +4,14 @@ using System; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Int256; namespace Nethermind.Crypto { public interface IEthereumEcdsa : IEcdsa { - void Sign(PrivateKey privateKey, Transaction tx, bool isEip155Enabled = true); - Address? RecoverAddress(Transaction tx, bool useSignatureChainId = false); + ulong ChainId { get; } Address? RecoverAddress(Signature signature, Hash256 message); Address? RecoverAddress(Span signatureBytes, Hash256 message); - bool Verify(Address sender, Transaction tx); } } diff --git a/src/Nethermind/Nethermind.Crypto/NullEthereumEcdsa.cs b/src/Nethermind/Nethermind.Crypto/NullEthereumEcdsa.cs index fade56407c5..78ff585916f 100644 --- a/src/Nethermind/Nethermind.Crypto/NullEthereumEcdsa.cs +++ b/src/Nethermind/Nethermind.Crypto/NullEthereumEcdsa.cs @@ -4,6 +4,7 @@ using System; using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Int256; namespace Nethermind.Crypto { @@ -11,6 +12,8 @@ public class NullEthereumEcdsa : IEthereumEcdsa { public static NullEthereumEcdsa Instance { get; } = new(); + public ulong ChainId => 0; + private NullEthereumEcdsa() { } @@ -30,11 +33,6 @@ public CompressedPublicKey RecoverCompressedPublicKey(Signature signature, Hash2 throw new InvalidOperationException($"{nameof(NullEthereumEcdsa)} does not expect any calls"); } - public void Sign(PrivateKey privateKey, Transaction tx, bool _) - { - throw new InvalidOperationException($"{nameof(NullEthereumEcdsa)} does not expect any calls"); - } - public Address RecoverAddress(Transaction tx, bool useSignatureChainId = false) { throw new InvalidOperationException($"{nameof(NullEthereumEcdsa)} does not expect any calls"); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs index fd31df6fa0c..be98ea39aab 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/EvmBenchmarks.cs @@ -55,7 +55,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(ByteCode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, codeInfoRepository), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs index 3a45a79026b..3b3853d5fc5 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/MultipleUnsignedOperations.cs @@ -87,7 +87,7 @@ 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), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, codeInfoRepository), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs index c0f3e229c4f..12c12742ed7 100644 --- a/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs +++ b/src/Nethermind/Nethermind.Evm.Benchmark/StaticCallBenchmarks.cs @@ -98,7 +98,7 @@ public void GlobalSetup() codeInfo: new CodeInfo(Bytecode), value: 0, transferValue: 0, - txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null), + txExecutionContext: new TxExecutionContext(_header, Address.Zero, 0, null, codeInfoRepository), inputData: default ); diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsAddG1PrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsG1AddPrecompileTests.cs similarity index 100% rename from src/Nethermind/Nethermind.Evm.Test/BlsAddG1PrecompileTests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsG1AddPrecompileTests.cs diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG1PrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsG1MSMPrecompileTests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG1PrecompileTests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsG1MSMPrecompileTests.cs index 627ebd8c53c..9626fa7238e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG1PrecompileTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsG1MSMPrecompileTests.cs @@ -12,15 +12,15 @@ namespace Nethermind.Evm.Test; -public class BlsMultiMulG1PrecompileTests +public class BlsG1MSMPrecompileTests { [Test] public void Test() { foreach ((byte[] input, ReadOnlyMemory expectedResult) in Inputs) { - IPrecompile precompile = G1MultiMulPrecompile.Instance; - (ReadOnlyMemory output, bool success) = precompile.Run(input, MuirGlacier.Instance); + IPrecompile precompile = G1MSMPrecompile.Instance; + (ReadOnlyMemory output, bool success) = precompile.Run(input, Prague.Instance); output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray()); success.Should().BeTrue(); } diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsAddG2PrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsG2AddPrecompileTests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsAddG2PrecompileTests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsG2AddPrecompileTests.cs index 4c6dabd96a0..be2609d84ef 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsAddG2PrecompileTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsG2AddPrecompileTests.cs @@ -12,7 +12,7 @@ namespace Nethermind.Evm.Test; -public class BlsAddG2PrecompileTests +public class BlsG2AddPrecompileTests { [Test] public void Test() diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG2PrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsG2MSMPrecompileTests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG2PrecompileTests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsG2MSMPrecompileTests.cs index 3914ae8e860..c9310444b3b 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsMultiMulG2PrecompileTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsG2MSMPrecompileTests.cs @@ -8,19 +8,19 @@ using Nethermind.Evm.Precompiles; using Nethermind.Evm.Precompiles.Bls; using NUnit.Framework; -using static Nethermind.Specs.Forks.MuirGlacier; +using Nethermind.Specs.Forks; namespace Nethermind.Evm.Test; -public class BlsMultiMulG2PrecompileTests +public class BlsG2MSMPrecompileTests { [Test] public void Test() { foreach ((byte[] input, ReadOnlyMemory expectedResult) in Inputs) { - IPrecompile precompile = G2MultiMulPrecompile.Instance; - (ReadOnlyMemory output, bool success) = precompile.Run(input, Instance); + IPrecompile precompile = G2MSMPrecompile.Instance; + (ReadOnlyMemory output, bool success) = precompile.Run(input, Prague.Instance); output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray()); success.Should().BeTrue(); } diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsMapToG2Tests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsMapFp2ToG2Tests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsMapToG2Tests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsMapFp2ToG2Tests.cs index 6122b9ac2a6..61982b51754 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsMapToG2Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsMapFp2ToG2Tests.cs @@ -12,14 +12,14 @@ namespace Nethermind.Evm.Test; -public class BlsMapToG2Tests +public class BlsMapFp2ToG2Tests { [Test] public void Test() { foreach ((byte[] input, ReadOnlyMemory expectedResult) in Inputs) { - IPrecompile precompile = MapToG2Precompile.Instance; + IPrecompile precompile = MapFp2ToG2Precompile.Instance; (ReadOnlyMemory output, bool success) = precompile.Run(input, MuirGlacier.Instance); output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray()); diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsMapToG1Tests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsMapFpToG1Tests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsMapToG1Tests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsMapFpToG1Tests.cs index 35f44f4151e..801f1fdc387 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsMapToG1Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsMapFpToG1Tests.cs @@ -12,14 +12,14 @@ namespace Nethermind.Evm.Test; -public class BlsMapToG1Tests +public class BlsMapFpToG1Tests { [Test] public void Test() { foreach ((byte[] input, ReadOnlyMemory expectedResult) in Inputs) { - IPrecompile precompile = MapToG1Precompile.Instance; + IPrecompile precompile = MapFpToG1Precompile.Instance; (ReadOnlyMemory output, bool success) = precompile.Run(input, MuirGlacier.Instance); output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray()); diff --git a/src/Nethermind/Nethermind.Evm.Test/BlsPairingPrecompileTests.cs b/src/Nethermind/Nethermind.Evm.Test/BlsPairingCheckPrecompileTests.cs similarity index 99% rename from src/Nethermind/Nethermind.Evm.Test/BlsPairingPrecompileTests.cs rename to src/Nethermind/Nethermind.Evm.Test/BlsPairingCheckPrecompileTests.cs index f717785b44f..ee490e4513e 100644 --- a/src/Nethermind/Nethermind.Evm.Test/BlsPairingPrecompileTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/BlsPairingCheckPrecompileTests.cs @@ -12,14 +12,14 @@ namespace Nethermind.Evm.Test; -public class BlsPairingPrecompileTests +public class BlsPairingCheckPrecompileTests { [Test] public void Test() { foreach ((byte[] input, ReadOnlyMemory expectedResult) in Inputs) { - IPrecompile precompile = PairingPrecompile.Instance; + IPrecompile precompile = PairingCheckPrecompile.Instance; (ReadOnlyMemory output, bool success) = precompile.Run(input, MuirGlacier.Instance); output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray()); diff --git a/src/Nethermind/Nethermind.Evm.Test/CodeInfoRepositoryTests.cs b/src/Nethermind/Nethermind.Evm.Test/CodeInfoRepositoryTests.cs new file mode 100644 index 00000000000..f83b9b9846d --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/CodeInfoRepositoryTests.cs @@ -0,0 +1,205 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Crypto; +using Nethermind.Core; +using Nethermind.Crypto; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using NSubstitute; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using Nethermind.Core.Test.Builders; +using FluentAssertions; +using Nethermind.State; +using Nethermind.Core.Specs; +using Nethermind.Evm.CodeAnalysis; +using Nethermind.Core.Extensions; +using Nethermind.Db; +using Nethermind.Trie.Pruning; + +namespace Nethermind.Evm.Test; + +[TestFixture, Parallelizable] +public class CodeInfoRepositoryTests +{ + public static IEnumerable NotDelegationCodeCases() + { + byte[] rndAddress = new byte[20]; + TestContext.CurrentContext.Random.NextBytes(rndAddress); + //Change first byte of the delegation header + byte[] code = [.. Eip7702Constants.DelegationHeader, .. rndAddress]; + code[0] = TestContext.CurrentContext.Random.NextByte(0xee); + yield return new object[] + { + code + }; + //Change second byte of the delegation header + code = [.. Eip7702Constants.DelegationHeader, .. rndAddress]; + code[1] = TestContext.CurrentContext.Random.NextByte(0x2, 0xff); + yield return new object[] + { + code + }; + //Change third byte of the delegation header + code = [.. Eip7702Constants.DelegationHeader, .. rndAddress]; + code[2] = TestContext.CurrentContext.Random.NextByte(0x1, 0xff); + yield return new object[] + { + code + }; + code = [.. Eip7702Constants.DelegationHeader, .. new byte[21]]; + yield return new object[] + { + code + }; + code = [.. Eip7702Constants.DelegationHeader, .. new byte[19]]; + yield return new object[] + { + code + }; + } + [TestCaseSource(nameof(NotDelegationCodeCases))] + public void TryGetDelegation_CodeIsNotDelegation_ReturnsFalse(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + CodeInfoRepository sut = new(); + + sut.TryGetDelegation(stateProvider, TestItem.AddressA, out _).Should().Be(false); + } + + + public static IEnumerable DelegationCodeCases() + { + byte[] address = new byte[20]; + byte[] code = [.. Eip7702Constants.DelegationHeader, .. address]; + yield return new object[] + { + code + }; + TestContext.CurrentContext.Random.NextBytes(address); + code = [.. Eip7702Constants.DelegationHeader, .. address]; + yield return new object[] + { + code + }; + } + [TestCaseSource(nameof(DelegationCodeCases))] + public void TryGetDelegation_CodeTryGetDelegation_ReturnsTrue(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + CodeInfoRepository sut = new(); + + sut.TryGetDelegation(stateProvider, TestItem.AddressA, out _).Should().Be(true); + } + + [TestCaseSource(nameof(DelegationCodeCases))] + public void TryGetDelegation_CodeTryGetDelegation_CorrectDelegationAddressIsSet(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + CodeInfoRepository sut = new(); + + Address result; + sut.TryGetDelegation(stateProvider, TestItem.AddressA, out result); + + result.Should().Be(new Address(code.Slice(3, Address.Size))); + } + + [TestCaseSource(nameof(DelegationCodeCases))] + public void GetExecutableCodeHash_CodeTryGetDelegation_ReturnsHashOfDelegated(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + Address delegationAddress = new Address(code.Slice(3, Address.Size)); + byte[] delegationCode = new byte[32]; + stateProvider.CreateAccount(delegationAddress, 0); + stateProvider.InsertCode(delegationAddress, delegationCode, Substitute.For()); + + CodeInfoRepository sut = new(); + + sut.GetExecutableCodeHash(stateProvider, TestItem.AddressA).Should().Be(Keccak.Compute(delegationCode).ValueHash256); + } + + [TestCaseSource(nameof(NotDelegationCodeCases))] + public void GetExecutableCodeHash_CodeIsNotDelegation_ReturnsCodeHashOfAddress(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + + CodeInfoRepository sut = new(); + + sut.GetExecutableCodeHash(stateProvider, TestItem.AddressA).Should().Be(Keccak.Compute(code).ValueHash256); + } + + [TestCaseSource(nameof(DelegationCodeCases))] + public void GetCachedCodeInfo_CodeTryGetDelegation_ReturnsCodeOfDelegation(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + Address delegationAddress = new Address(code.Slice(3, Address.Size)); + stateProvider.CreateAccount(delegationAddress, 0); + byte[] delegationCode = new byte[32]; + stateProvider.InsertCode(delegationAddress, delegationCode, Substitute.For()); + CodeInfoRepository sut = new(); + + CodeInfo result = sut.GetCachedCodeInfo(stateProvider, TestItem.AddressA, Substitute.For()); + result.MachineCode.ToArray().Should().BeEquivalentTo(delegationCode); + } + + [TestCaseSource(nameof(NotDelegationCodeCases))] + public void GetCachedCodeInfo_CodeIsNotDelegation_ReturnsCodeOfAddress(byte[] code) + { + IDb stateDb = new MemDb(); + IDb codeDb = new MemDb(); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance); + stateProvider.CreateAccount(TestItem.AddressA, 0); + stateProvider.InsertCode(TestItem.AddressA, code, Substitute.For()); + + CodeInfoRepository sut = new(); + + sut.GetCachedCodeInfo(stateProvider, TestItem.AddressA, Substitute.For()).Should().BeEquivalentTo(new CodeInfo(code)); + } + + private static AuthorizationTuple CreateAuthorizationTuple(PrivateKey signer, ulong chainId, Address codeAddress, ulong nonce) + { + AuthorizationTupleDecoder decoder = new(); + using NettyRlpStream rlp = decoder.EncodeWithoutSignature(chainId, codeAddress, nonce); + Span code = stackalloc byte[rlp.Length + 1]; + code[0] = Eip7702Constants.Magic; + rlp.AsSpan().CopyTo(code.Slice(1)); + EthereumEcdsa ecdsa = new(1); + Signature sig = ecdsa.Sign(signer, Keccak.Compute(code)); + + return new AuthorizationTuple(chainId, codeAddress, nonce, sig, signer.Address); + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs b/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs new file mode 100644 index 00000000000..99973b8bc23 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/ConsolidationRequestProcessorTest.cs @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +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 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(); + }); + } + + + [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.ReadRequests(block, _stateProvider, spec).ToList(); + + Assert.That(ConsolidationRequests, Has.Count.EqualTo(10)); + ConsolidationRequest ConsolidationRequestResult = ConsolidationRequests[0]; + + ConsolidationRequestResult.Should().BeEquivalentTo(ConsolidationRequest, options => options + .Using>(ctx => ctx.Subject.Span.SequenceEqual(ctx.Expectation.Span).Should().BeTrue()) + .WhenTypeIs>()); + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs index 97d509a3b57..0f2e4bed9ff 100644 --- a/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/Eip1153Tests.cs @@ -80,7 +80,6 @@ public void tload_uninitialized_returns_zero() [Test] public void transient_storage_performance_test() { - Stopwatch stopwatch = new Stopwatch(); long blockGasLimit = 30000000; long numOfOps = (long)(blockGasLimit * .95) / (GasCostOf.TLoad + GasCostOf.TStore + GasCostOf.VeryLow * 4); Prepare prepare = Prepare.EvmCode; @@ -93,11 +92,10 @@ public void transient_storage_performance_test() byte[] code = prepare.Done; - stopwatch.Start(); + long startTime = Stopwatch.GetTimestamp(); TestAllTracerWithOutput result = Execute((MainnetSpecProvider.GrayGlacierBlockNumber, Timestamp), blockGasLimit, code, blockGasLimit); Assert.That(result.StatusCode, Is.EqualTo(StatusCode.Success)); - stopwatch.Stop(); - Assert.That(stopwatch.ElapsedMilliseconds < 5000, Is.True); + Assert.That(Stopwatch.GetElapsedTime(startTime).TotalMilliseconds < 5000, Is.True); } /// diff --git a/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs b/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs index 126efa7438b..d1c068694da 100644 --- a/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using FluentAssertions; +using MathNet.Numerics.Random; using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Extensions; @@ -115,5 +116,89 @@ void Test(IReleaseSpec spec, bool isAfterRepricing) Test(Shanghai.Instance, true); Test(Cancun.Instance, true); } + public static IEnumerable<(AuthorizationTuple[] contractCode, long expectedCost)> AuthorizationListTestCaseSource() + { + yield return ( + [], 0); + yield return ( + [new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)) + ], + GasCostOf.NewAccount); + yield return ( + [new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)), + new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)) + ], + GasCostOf.NewAccount * 2); + yield return ( + [new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)), + new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)), + new AuthorizationTuple( + TestContext.CurrentContext.Random.NextULong(), + new Address(TestContext.CurrentContext.Random.NextBytes(20)), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)) + ], + GasCostOf.NewAccount * 3); + } + [TestCaseSource(nameof(AuthorizationListTestCaseSource))] + public void Calculate_TxHasAuthorizationList_ReturnsExpectedCostOfTx((AuthorizationTuple[] AuthorizationList, long ExpectedCost) testCase) + { + Transaction tx = Build.A.Transaction.SignedAndResolved() + .WithAuthorizationCode(testCase.AuthorizationList) + .TestObject; + + IntrinsicGasCalculator.Calculate(tx, Prague.Instance) + .Should().Be(GasCostOf.Transaction + (testCase.ExpectedCost)); + } + + [Test] + public void Calculate_TxHasAuthorizationListBeforePrague_ThrowsInvalidDataException() + { + Transaction tx = Build.A.Transaction.SignedAndResolved() + .WithAuthorizationCode( + new AuthorizationTuple( + 0, + TestItem.AddressF, + 0, + TestContext.CurrentContext.Random.NextULong(), + TestContext.CurrentContext.Random.NextBytes(10), + TestContext.CurrentContext.Random.NextBytes(10)) + ) + .TestObject; + + Assert.That(() => IntrinsicGasCalculator.Calculate(tx, Cancun.Instance), Throws.InstanceOf()); + } } } diff --git a/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs new file mode 100644 index 00000000000..4fe6ab74f20 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/TransactionProcessorEip7702Tests.cs @@ -0,0 +1,613 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Db; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Logging; +using Nethermind.Specs.Forks; +using Nethermind.State; +using Nethermind.Trie.Pruning; +using NUnit.Framework; +using System.Collections.Generic; +using Nethermind.Core.Crypto; +using System; +using System.Linq; +using Nethermind.Int256; + +namespace Nethermind.Evm.Test; + +[TestFixture] +internal class TransactionProcessorEip7702Tests +{ + private ISpecProvider _specProvider; + private IEthereumEcdsa _ethereumEcdsa; + private TransactionProcessor _transactionProcessor; + private IWorldState _stateProvider; + + [SetUp] + public void Setup() + { + MemDb stateDb = new(); + _specProvider = new TestSpecProvider(Prague.Instance); + TrieStore trieStore = new(stateDb, LimboLogs.Instance); + _stateProvider = new WorldState(trieStore, new MemDb(), LimboLogs.Instance); + CodeInfoRepository codeInfoRepository = new(); + VirtualMachine virtualMachine = new(new TestBlockhashProvider(_specProvider), _specProvider, codeInfoRepository, LimboLogs.Instance); + _transactionProcessor = new TransactionProcessor(_specProvider, _stateProvider, virtualMachine, codeInfoRepository, LimboLogs.Instance); + _ethereumEcdsa = new EthereumEcdsa(_specProvider.ChainId); + } + + [Test] + public void Execute_TxHasAuthorizationWithCodeThatSavesCallerAddress_ExpectedAddressIsSaved() + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + //Save caller in storage slot 0 + byte[] code = Prepare.EvmCode + .Op(Instruction.CALLER) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(codeSource, code); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(signer, _specProvider.ChainId, codeSource, 0)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + ReadOnlySpan cell = _stateProvider.Get(new StorageCell(signer.Address, 0)); + + Assert.That(new Address(cell.ToArray()), Is.EqualTo(sender.Address)); + } + + public static IEnumerable DelegatedAndNotDelegatedCodeCases() + { + byte[] delegatedCode = new byte[23]; + Eip7702Constants.DelegationHeader.CopyTo(delegatedCode); + yield return new object[] { delegatedCode, true }; + yield return new object[] { Prepare.EvmCode.Op(Instruction.GAS).Done, false }; + } + [TestCaseSource(nameof(DelegatedAndNotDelegatedCodeCases))] + public void Execute_TxHasAuthorizationCodeButAuthorityHasCode_OnlyInsertIfExistingCodeIsDelegated(byte[] authorityCode, bool shouldInsert) + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + //Save caller in storage slot 0 + byte[] code = Prepare.EvmCode + .Op(Instruction.CALLER) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(codeSource, code); + DeployCode(signer.Address, authorityCode); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(60_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(signer, _specProvider.ChainId, codeSource, 0)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + ReadOnlySpan signerCode = _stateProvider.GetCode(signer.Address); + + byte[] expectedCode = shouldInsert ? [.. Eip7702Constants.DelegationHeader, .. codeSource.Bytes] : authorityCode; + + Assert.That(signerCode.ToArray(), Is.EquivalentTo(expectedCode)); + } + + public static IEnumerable SenderSignerCases() + { + yield return new object[] { TestItem.PrivateKeyA, TestItem.PrivateKeyB, 0ul }; + yield return new object[] { TestItem.PrivateKeyA, TestItem.PrivateKeyA, 1ul }; + } + [TestCaseSource(nameof(SenderSignerCases))] + public void Execute_SenderAndSignerIsTheSameOrNotWithCodeThatSavesCallerAddress_SenderAddressIsSaved(PrivateKey sender, PrivateKey signer, ulong nonce) + { + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + //Save caller in storage slot 0 + byte[] code = Prepare.EvmCode + .Op(Instruction.CALLER) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(codeSource, code); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(600_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(signer, _specProvider.ChainId, codeSource, nonce)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + ReadOnlySpan cellValue = _stateProvider.Get(new StorageCell(signer.Address, 0)); + + Assert.That(cellValue.ToArray(), Is.EqualTo(sender.Address.Bytes)); + } + public static IEnumerable DifferentCommitValues() + { + //Base case + yield return new object[] { 1ul, 0ul, TestItem.AddressA.Bytes }; + //Wrong nonce + yield return new object[] { 1ul, 1ul, new[] { (byte)0x0 } }; + //Wrong chain id + yield return new object[] { 2ul, 0ul, new[] { (byte)0x0 } }; + } + + [TestCaseSource(nameof(DifferentCommitValues))] + public void Execute_CommitMessageHasDifferentData_ExpectedAddressIsSavedInStorageSlot(ulong chainId, ulong nonce, byte[] expectedStorageValue) + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + //Save caller in storage slot 0 + byte[] code = Prepare.EvmCode + .Op(Instruction.CALLER) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(codeSource, code); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign(signer, chainId, codeSource, nonce)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + var actual = _stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(); + Assert.That(actual, Is.EqualTo(expectedStorageValue)); + } + + [TestCase(0)] + [TestCase(1)] + [TestCase(10)] + [TestCase(99)] + public void Execute_TxHasDifferentAmountOfAuthorizedCode_UsedGasIsExpected(int count) + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount * count) + .WithAuthorizationCode(Enumerable.Range(0, count) + .Select(i => _ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + TestItem.AddressC, + 0)).ToArray()) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(100000000).TestObject; + + CallOutputTracer tracer = new(); + + _transactionProcessor.Execute(tx, block.Header, tracer); + + Assert.That(tracer.GasSpent, Is.EqualTo(GasCostOf.Transaction + GasCostOf.NewAccount * count)); + } + + private static IEnumerable EvmExecutionErrorCases() + { + byte[] runOutOfGasCode = Prepare.EvmCode + .Op(Instruction.CALLER) + .Op(Instruction.BALANCE) + .Op(Instruction.PUSH0) + .Op(Instruction.JUMP) + .Done; + yield return new object[] { runOutOfGasCode }; + byte[] revertExecution = Prepare.EvmCode + .Op(Instruction.REVERT) + .Done; + yield return new object[] { revertExecution }; + } + [TestCaseSource(nameof(EvmExecutionErrorCases))] + public void Execute_TxWithDelegationRunsOutOfGas_DelegationRefundIsStillApplied(byte[] executionErrorCode) + { + PrivateKey sender = TestItem.PrivateKeyA; + Address codeSource = TestItem.AddressB; + + _stateProvider.CreateAccount(codeSource, 0); + _stateProvider.InsertCode(codeSource, executionErrorCode, Prague.Instance); + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + const long gasLimit = 10_000_000; + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(codeSource) + .WithGasLimit(gasLimit) + .WithAuthorizationCode( + _ethereumEcdsa.Sign( + sender, + _specProvider.ChainId, + Address.Zero, + 1) + ) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(long.MaxValue).TestObject; + + CallOutputTracer tracer = new(); + + _transactionProcessor.Execute(tx, block.Header, tracer); + + Assert.That(tracer.GasSpent, Is.EqualTo(gasLimit - GasCostOf.NewAccount + GasCostOf.PerAuthBaseCost)); + } + + [Test] + public void Execute_TxAuthorizationListWithBALANCE_WarmAccountReadGasIsCharged() + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + byte[] code = Prepare.EvmCode + .PushData(signer.Address) + .Op(Instruction.BALANCE) + .Done; + DeployCode(codeSource, code); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(60_000) + .WithAuthorizationCode( + _ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + codeSource, + 0)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(100000000).TestObject; + + CallOutputTracer tracer = new(); + + _transactionProcessor.Execute(tx, block.Header, tracer); + //Tx should only be charged for warm state read + Assert.That(tracer.GasSpent, Is.EqualTo(GasCostOf.Transaction + + GasCostOf.NewAccount + + Prague.Instance.GetBalanceCost() + + GasCostOf.WarmStateRead + + GasCostOf.VeryLow)); + } + + [TestCase(2)] + [TestCase(1)] + public void Execute_AuthorizationListHasSameAuthorityButDifferentCode_OnlyLastInstanceIsUsed(int expectedStoredValue) + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address firstCodeSource = TestItem.AddressC; + Address secondCodeSource = TestItem.AddressD; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + byte[] firstCode = Prepare.EvmCode + .PushData(0) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(firstCodeSource, firstCode); + + byte[] secondCode = Prepare.EvmCode + .PushData(expectedStoredValue) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(secondCodeSource, secondCode); + + AuthorizationTuple[] authList = [ + _ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + firstCodeSource, + 0), + _ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + secondCodeSource, + 1), + ]; + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(authList) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + Assert.That(_stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(), Is.EquivalentTo(new[] { expectedStoredValue })); + } + + [TestCase] + public void Execute_FirstTxHasAuthorizedCodeThatIncrementsAndSecondDoesNot_StorageSlotIsOnlyIncrementedOnce() + { + PrivateKey sender = TestItem.PrivateKeyA; + PrivateKey signer = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + //Increment 1 everytime it's called + byte[] code = Prepare.EvmCode + .Op(Instruction.PUSH0) + .Op(Instruction.SLOAD) + .PushData(1) + .Op(Instruction.ADD) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done; + DeployCode(codeSource, code); + + Transaction tx1 = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(60_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + codeSource, + 0)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Transaction tx2 = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(1) + .WithTo(signer.Address) + .WithGasLimit(60_000) + .WithAuthorizationCode([]) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx1, tx2) + .WithGasLimit(10000000).TestObject; + + _transactionProcessor.Execute(tx1, block.Header, NullTxTracer.Instance); + _transactionProcessor.Execute(tx2, block.Header, NullTxTracer.Instance); + + Assert.That(_stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(), Is.EquivalentTo(new[] { 1 })); + } + + public static IEnumerable OpcodesWithEXT() + { + //EXTCODESIZE should return the size of the delegated code + yield return new object[] { + Prepare.EvmCode + .PushData(TestItem.AddressA) + .Op(Instruction.EXTCODESIZE) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done, + new byte[]{ 2 + 22 } }; + //EXTCODEHASH should return the HASH of the delegated code + yield return new object[] { + Prepare.EvmCode + .PushData(TestItem.AddressA) + .Op(Instruction.EXTCODEHASH) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done, + Keccak.Compute( + Prepare.EvmCode + .PushData(TestItem.AddressA) + .Op(Instruction.EXTCODEHASH) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Done).Bytes.ToArray() + }; + //EXTCOPYCODE should copy the delegated code + byte[] code = Prepare.EvmCode + .PushData(TestItem.AddressA) + .Op(Instruction.DUP1) + .Op(Instruction.EXTCODESIZE) + .Op(Instruction.PUSH0) + .Op(Instruction.PUSH0) + .Op(Instruction.DUP4) + .Op(Instruction.EXTCODECOPY) + .Op(Instruction.PUSH0) + .Op(Instruction.MLOAD) + .Op(Instruction.PUSH0) + .Op(Instruction.SSTORE) + .Op(Instruction.STOP) + .Done; + yield return new object[] + { + code, + code + }; + } + [TestCaseSource(nameof(OpcodesWithEXT))] + public void Execute_DelegatedCodeUsesEXTOPCODES_StoresExpectedValue(byte[] code, byte[] expectedValue) + { + PrivateKey signer = TestItem.PrivateKeyA; + PrivateKey sender = TestItem.PrivateKeyB; + Address codeSource = TestItem.AddressC; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + DeployCode(codeSource, code); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(signer.Address) + .WithGasLimit(100_000) + .WithAuthorizationCode(_ethereumEcdsa.Sign( + signer, + _specProvider.ChainId, + codeSource, + 0)) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + TransactionResult result = _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + Assert.That(_stateProvider.Get(new StorageCell(signer.Address, 0)).ToArray(), Is.EquivalentTo(expectedValue)); + } + + public static IEnumerable CountsAsAccessedCases() + { + EthereumEcdsa ethereumEcdsa = new(BlockchainIds.GenericNonRealNetwork); + + yield return new object[] + { + new AuthorizationTuple[] + { + ethereumEcdsa.Sign(TestItem.PrivateKeyA, 1, TestItem.AddressF, 0), + ethereumEcdsa.Sign(TestItem.PrivateKeyB, 1, TestItem.AddressF, 0), + }, + new Address[] + { + TestItem.AddressA, + TestItem.AddressB + } + }; + yield return new object[] + { + new AuthorizationTuple[] + { + ethereumEcdsa.Sign(TestItem.PrivateKeyA, 1, TestItem.AddressF, 0), + ethereumEcdsa.Sign(TestItem.PrivateKeyB, 2, TestItem.AddressF, 0), + }, + new Address[] + { + TestItem.AddressA, + } + }; + yield return new object[] + { + new AuthorizationTuple[] + { + ethereumEcdsa.Sign(TestItem.PrivateKeyA, 1, TestItem.AddressF, 0), + //Bad signature + new AuthorizationTuple(1, TestItem.AddressF, 0, new Signature(new byte[65]), TestItem.AddressA) + }, + new Address[] + { + TestItem.AddressA, + } + }; + } + + [TestCaseSource(nameof(CountsAsAccessedCases))] + public void Execute_CombinationOfValidAndInvalidTuples_AddsTheCorrectAddressesToAccessedAddresses(AuthorizationTuple[] tuples, Address[] shouldCountAsAccessed) + { + PrivateKey sender = TestItem.PrivateKeyA; + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressB) + .WithGasLimit(100_000) + .WithAuthorizationCode(tuples) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + + AccessTxTracer txTracer = new AccessTxTracer(); + TransactionResult result = _transactionProcessor.Execute(tx, block.Header, txTracer); + Assert.That(txTracer.AccessList.Select(a => a.Address), Is.SupersetOf(shouldCountAsAccessed)); + } + + [TestCase(true)] + [TestCase(false)] + public void Execute_AuthorityAccountExistsOrNot_NonceIsIncrementedByOne(bool accountExists) + { + PrivateKey authority = TestItem.PrivateKeyA; + PrivateKey sender = TestItem.PrivateKeyB; + + if (accountExists) + _stateProvider.CreateAccount(authority.Address, 0); + _stateProvider.CreateAccount(sender.Address, 1.Ether()); + + AuthorizationTuple[] tuples = + { + _ethereumEcdsa.Sign(authority, 1, sender.Address, 0), + }; + + Transaction tx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithTo(TestItem.AddressB) + .WithGasLimit(100_000) + .WithAuthorizationCode(tuples) + .SignedAndResolved(_ethereumEcdsa, sender, true) + .TestObject; + Block block = Build.A.Block.WithNumber(long.MaxValue) + .WithTimestamp(MainnetSpecProvider.PragueBlockTimestamp) + .WithTransactions(tx) + .WithGasLimit(10000000).TestObject; + TransactionResult result = _transactionProcessor.Execute(tx, block.Header, NullTxTracer.Instance); + + Assert.That(_stateProvider.GetNonce(authority.Address), Is.EqualTo((UInt256)1)); + } + + private void DeployCode(Address codeSource, byte[] code) + { + _stateProvider.CreateAccountIfNotExists(codeSource, 0); + _stateProvider.InsertCode(codeSource, ValueKeccak.Compute(code), code, _specProvider.GetSpec(MainnetSpecProvider.PragueActivation)); + } +} diff --git a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs index 6df49e3f481..6960cc8b18c 100644 --- a/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs +++ b/src/Nethermind/Nethermind.Evm.Test/VirtualMachineTestsBase.cs @@ -124,10 +124,22 @@ protected TestAllTracerWithOutput Execute(ForkActivation activation, params byte return tracer; } + protected TestAllTracerWithOutput Execute(ForkActivation activation, Transaction tx) + { + (Block block, _) = PrepareTx(activation, 100000, null); + TestAllTracerWithOutput tracer = CreateTracer(); + _processor.Execute(tx, block.Header, tracer); + return tracer; + } + protected TestAllTracerWithOutput Execute(params byte[] code) { return Execute(Activation, code); } + protected TestAllTracerWithOutput Execute(Transaction tx) + { + return Execute(Activation, tx); + } protected virtual TestAllTracerWithOutput CreateTracer() => new(); @@ -197,7 +209,8 @@ protected TestAllTracerWithOutput Execute(ForkActivation activation, long gasLim int value = 1, long blockGasLimit = DefaultBlockGasLimit, byte[][]? blobVersionedHashes = null, - ulong excessBlobGas = 0) + ulong excessBlobGas = 0, + Transaction transaction = null) { senderRecipientAndMiner ??= SenderRecipientAndMiner.Default; @@ -227,7 +240,7 @@ protected TestAllTracerWithOutput Execute(ForkActivation activation, long gasLim TestState.CommitTree(0); GetLogManager().GetClassLogger().Debug("Committed initial tree"); - Transaction transaction = Build.A.Transaction + transaction ??= Build.A.Transaction .WithGasLimit(gasLimit) .WithGasPrice(1) .WithValue(value) diff --git a/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs b/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs index 820809bf95f..279b668e424 100644 --- a/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs +++ b/src/Nethermind/Nethermind.Evm.Test/WithdrawalRequestsProcessorTests.cs @@ -32,11 +32,8 @@ public class WithdrawalRequestProcessorTests 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] @@ -72,7 +69,7 @@ public void Setup() public void ShouldProcessWithdrawalRequest() { IReleaseSpec spec = Substitute.For(); - spec.IsEip7002Enabled.Returns(true); + spec.WithdrawalRequestsEnabled.Returns(true); spec.Eip7002ContractAddress.Returns(eip7002Account); Block block = Build.A.Block.TestObject; @@ -86,7 +83,7 @@ public void ShouldProcessWithdrawalRequest() Amount = 0 }; - var withdrawalRequests = withdrawalRequestsProcessor.ReadWithdrawalRequests(block, _stateProvider, spec).ToList(); + var withdrawalRequests = withdrawalRequestsProcessor.ReadRequests(block, _stateProvider, spec).ToList(); Assert.That(withdrawalRequests, Has.Count.EqualTo(16)); diff --git a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs index 1dd64dabe4d..fe631b3fb10 100644 --- a/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs @@ -16,49 +16,12 @@ using Nethermind.Evm.Precompiles.Bls; using Nethermind.Evm.Precompiles.Snarks; using Nethermind.State; +using Nethermind.Crypto; namespace Nethermind.Evm; public class CodeInfoRepository : ICodeInfoRepository { - internal sealed class CodeLruCache - { - private const int CacheCount = 16; - private const int CacheMax = CacheCount - 1; - private readonly ClockCache[] _caches; - - public CodeLruCache() - { - _caches = new ClockCache[CacheCount]; - for (int i = 0; i < _caches.Length; i++) - { - // Cache per nibble to reduce contention as TxPool is very parallel - _caches[i] = new ClockCache(MemoryAllowance.CodeCacheSize / CacheCount); - } - } - - public CodeInfo? Get(in ValueHash256 codeHash) - { - ClockCache cache = _caches[GetCacheIndex(codeHash)]; - return cache.Get(codeHash); - } - - public bool Set(in ValueHash256 codeHash, CodeInfo codeInfo) - { - ClockCache cache = _caches[GetCacheIndex(codeHash)]; - return cache.Set(codeHash, codeInfo); - } - - private static int GetCacheIndex(in ValueHash256 codeHash) => codeHash.Bytes[^1] & CacheMax; - - public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out CodeInfo? codeInfo) - { - codeInfo = Get(codeHash); - return codeInfo is not null; - } - } - - private static readonly FrozenDictionary _precompiles = InitializePrecompiledContracts(); private static readonly CodeLruCache _codeCache = new(); private readonly FrozenDictionary _localPrecompiles; @@ -81,13 +44,13 @@ private static FrozenDictionary InitializePrecompiledCon [G1AddPrecompile.Address] = new(G1AddPrecompile.Instance), [G1MulPrecompile.Address] = new(G1MulPrecompile.Instance), - [G1MultiMulPrecompile.Address] = new(G1MultiMulPrecompile.Instance), + [G1MSMPrecompile.Address] = new(G1MSMPrecompile.Instance), [G2AddPrecompile.Address] = new(G2AddPrecompile.Instance), [G2MulPrecompile.Address] = new(G2MulPrecompile.Instance), - [G2MultiMulPrecompile.Address] = new(G2MultiMulPrecompile.Instance), - [PairingPrecompile.Address] = new(PairingPrecompile.Instance), - [MapToG1Precompile.Address] = new(MapToG1Precompile.Instance), - [MapToG2Precompile.Address] = new(MapToG2Precompile.Instance), + [G2MSMPrecompile.Address] = new(G2MSMPrecompile.Instance), + [PairingCheckPrecompile.Address] = new(PairingCheckPrecompile.Instance), + [MapFpToG1Precompile.Address] = new(MapFpToG1Precompile.Instance), + [MapFp2ToG2Precompile.Address] = new(MapFp2ToG2Precompile.Instance), [PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance), @@ -102,13 +65,26 @@ public CodeInfoRepository(ConcurrentDictionary kvp.Key, kvp => CreateCachedPrecompile(kvp, precompileCache)); } - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) + public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress) { + delegationAddress = null; if (codeSource.IsPrecompile(vmSpec)) { return _localPrecompiles[codeSource]; } + CodeInfo cachedCodeInfo = InternalGetCachedCode(worldState, codeSource); + + if (TryGetDelegatedAddress(cachedCodeInfo.MachineCode.Span, out delegationAddress)) + { + cachedCodeInfo = InternalGetCachedCode(worldState, delegationAddress); + } + + return cachedCodeInfo; + } + + private static CodeInfo InternalGetCachedCode(IReadOnlyStateProvider worldState, Address codeSource) + { CodeInfo? cachedCodeInfo = null; ValueHash256 codeHash = worldState.GetCodeHash(codeSource); if (codeHash == Keccak.OfAnEmptyString.ValueHash256) @@ -145,28 +121,61 @@ static void MissingCode(Address codeSource, in ValueHash256 codeHash) } } - public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode) + public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) { - if (!_codeCache.TryGet(codeHash, out CodeInfo? codeInfo)) - { - codeInfo = new(initCode.ToArray()); + CodeInfo codeInfo = new(code); + codeInfo.AnalyseInBackgroundIfRequired(); - // Prime the code cache as likely to be used by more txs - _codeCache.Set(codeHash, codeInfo); - } + ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); + state.InsertCode(codeOwner, codeHash, code, spec); + _codeCache.Set(codeHash, codeInfo); + } - return codeInfo; + public void SetDelegation(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec) + { + byte[] authorizedBuffer = new byte[Eip7702Constants.DelegationHeader.Length + Address.Size]; + Eip7702Constants.DelegationHeader.CopyTo(authorizedBuffer); + codeSource.Bytes.CopyTo(authorizedBuffer, Eip7702Constants.DelegationHeader.Length); + ValueHash256 codeHash = ValueKeccak.Compute(authorizedBuffer); + state.InsertCode(authority, codeHash, authorizedBuffer.AsMemory(), spec); + _codeCache.Set(codeHash, new CodeInfo(authorizedBuffer)); } + /// + /// Retrieves code hash of delegation if delegated. Otherwise code hash of . + /// + /// + /// + public ValueHash256 GetExecutableCodeHash(IWorldState worldState, Address address) + { + ValueHash256 codeHash = worldState.GetCodeHash(address); + if (codeHash == Keccak.OfAnEmptyString.ValueHash256) + { + return Keccak.OfAnEmptyString.ValueHash256; + } - public void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec) + CodeInfo codeInfo = InternalGetCachedCode(worldState, address); + return codeInfo.IsEmpty + ? Keccak.OfAnEmptyString.ValueHash256 + : TryGetDelegatedAddress(codeInfo.MachineCode.Span, out Address? delegationAddress) + ? worldState.GetCodeHash(delegationAddress) + : codeHash; + } + + /// + /// Parses delegation code to extract the contained address. + /// Assumes is delegation code! + /// + private static bool TryGetDelegatedAddress(ReadOnlySpan code, [NotNullWhen(true)] out Address? address) { - CodeInfo codeInfo = new(code); - codeInfo.AnalyseInBackgroundIfRequired(); + if (Eip7702Constants.IsDelegatedCode(code)) + { + address = new Address(code.Slice(Eip7702Constants.DelegationHeader.Length).ToArray()); + return true; + } - Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); - state.InsertCode(codeOwner, codeHash, code, spec); - _codeCache.Set(codeHash, codeInfo); + address = null; + return false; } private CodeInfo CreateCachedPrecompile( @@ -174,6 +183,9 @@ private CodeInfo CreateCachedPrecompile( ConcurrentDictionary, bool)> cache) => new(new CachedPrecompile(originalPrecompile.Key.Value, originalPrecompile.Value.Precompile!, cache)); + public bool TryGetDelegation(IReadOnlyStateProvider worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress) => + TryGetDelegatedAddress(InternalGetCachedCode(worldState, address).MachineCode.Span, out delegatedAddress); + private class CachedPrecompile( Address address, IPrecompile precompile, @@ -199,4 +211,42 @@ private class CachedPrecompile( return result; } } + + private sealed class CodeLruCache + { + private const int CacheCount = 16; + private const int CacheMax = CacheCount - 1; + private readonly ClockCache[] _caches; + + public CodeLruCache() + { + _caches = new ClockCache[CacheCount]; + for (int i = 0; i < _caches.Length; i++) + { + // Cache per nibble to reduce contention as TxPool is very parallel + _caches[i] = new ClockCache(MemoryAllowance.CodeCacheSize / CacheCount); + } + } + + public CodeInfo? Get(in ValueHash256 codeHash) + { + ClockCache cache = _caches[GetCacheIndex(codeHash)]; + return cache.Get(codeHash); + } + + public bool Set(in ValueHash256 codeHash, CodeInfo codeInfo) + { + ClockCache cache = _caches[GetCacheIndex(codeHash)]; + return cache.Set(codeHash, codeInfo); + } + + private static int GetCacheIndex(in ValueHash256 codeHash) => codeHash.Bytes[^1] & CacheMax; + + public bool TryGet(in ValueHash256 codeHash, [NotNullWhen(true)] out CodeInfo? codeInfo) + { + codeInfo = Get(codeHash); + return codeInfo is not null; + } + } } + diff --git a/src/Nethermind/Nethermind.Evm/GasCostOf.cs b/src/Nethermind/Nethermind.Evm/GasCostOf.cs index 68dc8cae654..e67770b53e8 100644 --- a/src/Nethermind/Nethermind.Evm/GasCostOf.cs +++ b/src/Nethermind/Nethermind.Evm/GasCostOf.cs @@ -62,5 +62,6 @@ public static class GasCostOf public const long AccessStorageListEntry = 1900; // eip-2930 public const long TLoad = WarmStateRead; // eip-1153 public const long TStore = WarmStateRead; // eip-1153 + public const long PerAuthBaseCost = 2500; // eip-7702 } } diff --git a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs index 6fde3cbfe7f..b2f0a4a176d 100644 --- a/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; @@ -12,7 +14,15 @@ namespace Nethermind.Evm; public interface ICodeInfoRepository { - CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec); - CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan initCode); + CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress); + ValueHash256 GetExecutableCodeHash(IWorldState worldState, Address address); void InsertCode(IWorldState state, ReadOnlyMemory code, Address codeOwner, IReleaseSpec spec); + void SetDelegation(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec); + bool TryGetDelegation(IReadOnlyStateProvider worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress); +} + +public static class CodeInfoRepositoryExtensions +{ + public static CodeInfo GetCachedCodeInfo(this ICodeInfoRepository codeInfoRepository, IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) + => codeInfoRepository.GetCachedCodeInfo(worldState, codeSource, vmSpec, out _); } diff --git a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs index 8099d6d7b6c..a5ca480a980 100644 --- a/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/IVirtualMachine.cs @@ -1,11 +1,6 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; -using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Specs; -using Nethermind.Evm.CodeAnalysis; using Nethermind.Evm.Tracing; using Nethermind.State; diff --git a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs index 7742bcfc5ba..a8e5dc5c86f 100644 --- a/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs +++ b/src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs @@ -2,52 +2,39 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Numerics; using Nethermind.Core; using Nethermind.Core.Eip2930; using Nethermind.Core.Specs; using Nethermind.Int256; -using System.Runtime.Intrinsics; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; using Nethermind.Core.Extensions; namespace Nethermind.Evm; public static class IntrinsicGasCalculator { - public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec) - { - long result = GasCostOf.Transaction; - result += DataCost(transaction, releaseSpec); - result += CreateCost(transaction, releaseSpec); - result += AccessListCost(transaction, releaseSpec); - return result; - } - - private static long CreateCost(Transaction transaction, IReleaseSpec releaseSpec) - { - long createCost = 0; - if (transaction.IsContractCreation && releaseSpec.IsEip2Enabled) - { - createCost += GasCostOf.TxCreate; - } + public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec) => + GasCostOf.Transaction + + DataCost(transaction, releaseSpec) + + CreateCost(transaction, releaseSpec) + + AccessListCost(transaction, releaseSpec) + + AuthorizationListCost(transaction, releaseSpec); - return createCost; - } + private static long CreateCost(Transaction transaction, IReleaseSpec releaseSpec) => + transaction.IsContractCreation && releaseSpec.IsEip2Enabled ? GasCostOf.TxCreate : 0; private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) { - long txDataNonZeroGasCost = - releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; + long txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; Span data = transaction.Data.GetValueOrDefault().Span; int totalZeros = data.CountZeros(); - var baseDataCost = (transaction.IsContractCreation && releaseSpec.IsEip3860Enabled + long baseDataCost = transaction.IsContractCreation && releaseSpec.IsEip3860Enabled ? EvmPooledMemory.Div32Ceiling((UInt256)data.Length) * GasCostOf.InitCodeWord - : 0); + : 0; return baseDataCost + totalZeros * GasCostOf.TxDataZero + @@ -57,27 +44,48 @@ private static long DataCost(Transaction transaction, IReleaseSpec releaseSpec) private static long AccessListCost(Transaction transaction, IReleaseSpec releaseSpec) { AccessList? accessList = transaction.AccessList; - long accessListCost = 0; if (accessList is not null) { if (!releaseSpec.UseTxAccessLists) { - throw new InvalidDataException( - $"Transaction with an access list received within the context of {releaseSpec.Name}. Eip-2930 is not enabled."); + ThrowInvalidDataException(releaseSpec); } - if (accessList.IsEmpty) return accessListCost; + (int addressesCount, int storageKeysCount) = accessList.Count; + return addressesCount * GasCostOf.AccessAccountListEntry + storageKeysCount * GasCostOf.AccessStorageListEntry; + } + + return 0; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowInvalidDataException(IReleaseSpec releaseSpec) + { + throw new InvalidDataException($"Transaction with an authorization list received within the context of {releaseSpec.Name}. Eip-7702 is not enabled."); + } + } + + private static long AuthorizationListCost(Transaction transaction, IReleaseSpec releaseSpec) + { + AuthorizationTuple[]? transactionAuthorizationList = transaction.AuthorizationList; - foreach ((Address address, AccessList.StorageKeysEnumerable storageKeys) entry in accessList) + if (transactionAuthorizationList is not null) + { + if (!releaseSpec.IsAuthorizationListEnabled) { - accessListCost += GasCostOf.AccessAccountListEntry; - foreach (UInt256 _ in entry.storageKeys) - { - accessListCost += GasCostOf.AccessStorageListEntry; - } + ThrowInvalidDataException(releaseSpec); } + + return transactionAuthorizationList.Length * GasCostOf.NewAccount; } - return accessListCost; + return 0; + + [DoesNotReturn] + [StackTraceHidden] + static void ThrowInvalidDataException(IReleaseSpec releaseSpec) + { + throw new InvalidDataException($"Transaction with an authorization list received within the context of {releaseSpec.Name}. Eip-7702 is not enabled."); + } } } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsConst.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsConst.cs index 6587e354ee4..cd378705202 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsConst.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsConst.cs @@ -8,6 +8,8 @@ namespace Nethermind.Evm.Precompiles.Bls; public static class BlsConst { + public const bool DisableConcurrency = false; + public const bool DisableSubgroupChecks = false; public const int LenFr = 32; public const int LenFp = 64; public const int LenFpTrimmed = 48; @@ -16,6 +18,7 @@ public static class BlsConst public const int LenG1Trimmed = 2 * LenFpTrimmed; public const int LenG2 = 4 * LenFp; public const int LenG2Trimmed = 4 * LenFpTrimmed; + public static readonly byte[] BaseFieldOrder = [0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab]; public static readonly ReadOnlyMemory G1Inf = Enumerable.Repeat(0, 128).ToArray(); public static readonly ReadOnlyMemory G2Inf = Enumerable.Repeat(0, 256).ToArray(); diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsExtensions.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsExtensions.cs index 4a3d539b334..ab8d862d60d 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsExtensions.cs @@ -41,20 +41,6 @@ public static bool TryDecodeRaw(this G1 p, ReadOnlySpan raw) return true; } - public static ReadOnlyMemory EncodeRaw(this G1 p) - { - if (p.IsInf()) - { - return BlsConst.G1Inf; - } - - byte[] raw = new byte[BlsConst.LenG1]; - ReadOnlySpan trimmed = p.Serialize(); - trimmed[..BlsConst.LenFpTrimmed].CopyTo(raw.AsSpan()[BlsConst.LenFpPad..BlsConst.LenFp]); - trimmed[BlsConst.LenFpTrimmed..].CopyTo(raw.AsSpan()[(BlsConst.LenFp + BlsConst.LenFpPad)..]); - return raw; - } - public static bool TryDecodeRaw(this G2 p, ReadOnlySpan raw) { if (raw.Length != BlsConst.LenG2) @@ -96,6 +82,20 @@ public static bool TryDecodeRaw(this G2 p, ReadOnlySpan raw) return true; } + public static ReadOnlyMemory EncodeRaw(this G1 p) + { + if (p.IsInf()) + { + return BlsConst.G1Inf; + } + + byte[] raw = new byte[BlsConst.LenG1]; + ReadOnlySpan trimmed = p.Serialize(); + trimmed[..BlsConst.LenFpTrimmed].CopyTo(raw.AsSpan()[BlsConst.LenFpPad..BlsConst.LenFp]); + trimmed[BlsConst.LenFpTrimmed..].CopyTo(raw.AsSpan()[(BlsConst.LenFp + BlsConst.LenFpPad)..]); + return raw; + } + public static ReadOnlyMemory EncodeRaw(this G2 p) { if (p.IsInf()) @@ -128,4 +128,52 @@ public static bool ValidRawFp(ReadOnlySpan fp) // check that fp < base field order return fp[BlsConst.LenFpPad..].SequenceCompareTo(BlsConst.BaseFieldOrder.AsSpan()) < 0; } + + public static bool TryDecodeG1ToBuffer(ReadOnlyMemory inputData, Memory pointBuffer, Memory scalarBuffer, int dest, int index) + => TryDecodePointToBuffer(inputData, pointBuffer, scalarBuffer, dest, index, BlsConst.LenG1, G1MSMPrecompile.ItemSize, DecodeAndCheckG1); + + public static bool TryDecodeG2ToBuffer(ReadOnlyMemory inputData, Memory pointBuffer, Memory scalarBuffer, int dest, int index) + => TryDecodePointToBuffer(inputData, pointBuffer, scalarBuffer, dest, index, BlsConst.LenG2, G2MSMPrecompile.ItemSize, DecodeAndCheckG2); + + private static bool DecodeAndCheckG1(ReadOnlyMemory rawPoint, Memory pointBuffer, int dest) + { + G1 p = new(pointBuffer.Span[(dest * G1.Sz)..]); + return p.TryDecodeRaw(rawPoint.Span) && (BlsConst.DisableSubgroupChecks || p.InGroup()); + } + + private static bool DecodeAndCheckG2(ReadOnlyMemory rawPoint, Memory pointBuffer, int dest) + { + G2 p = new(pointBuffer.Span[(dest * G2.Sz)..]); + return p.TryDecodeRaw(rawPoint.Span) && (BlsConst.DisableSubgroupChecks || p.InGroup()); + } + + private static bool TryDecodePointToBuffer( + ReadOnlyMemory inputData, + Memory pointBuffer, + Memory scalarBuffer, + int dest, + int index, + int pointLen, + int itemSize, + Func, Memory, int, bool> decodeAndCheckPoint) + { + if (dest == -1) + { + return true; + } + + int offset = index * itemSize; + ReadOnlyMemory rawPoint = inputData[offset..(offset + pointLen)]; + ReadOnlyMemory reversedScalar = inputData[(offset + pointLen)..(offset + itemSize)]; + + if (!decodeAndCheckPoint(rawPoint, pointBuffer, dest)) + { + return false; + } + + int destOffset = dest * 32; + reversedScalar.CopyTo(scalarBuffer[destOffset..]); + scalarBuffer[destOffset..(destOffset + 32)].Span.Reverse(); + return true; + } } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1AddPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1AddPrecompile.cs index 2f23c913a8d..53df7f426df 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1AddPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1AddPrecompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; @@ -26,6 +27,7 @@ private G1AddPrecompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG1AddPrecompile++; diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MultiMulPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MSMPrecompile.cs similarity index 62% rename from src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MultiMulPrecompile.cs rename to src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MSMPrecompile.cs index 3e1e9a1765d..8f3f734f37f 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MultiMulPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MSMPrecompile.cs @@ -8,17 +8,18 @@ using Nethermind.Core.Collections; using G1 = Nethermind.Crypto.Bls.P1; +using System.Runtime.CompilerServices; namespace Nethermind.Evm.Precompiles.Bls; /// /// https://eips.ethereum.org/EIPS/eip-2537 /// -public class G1MultiMulPrecompile : IPrecompile +public class G1MSMPrecompile : IPrecompile { - public static readonly G1MultiMulPrecompile Instance = new(); + public static readonly G1MSMPrecompile Instance = new(); - private G1MultiMulPrecompile() + private G1MSMPrecompile() { } @@ -32,8 +33,9 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec return 12000L * k * Discount.For(k) / 1000; } - private const int ItemSize = 160; + public const int ItemSize = 160; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG1MSMPrecompile++; @@ -45,7 +47,7 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec int nItems = inputData.Length / ItemSize; - using ArrayPoolList rawPoints = new(nItems * 18, nItems * 18); + using ArrayPoolList rawPoints = new(nItems * G1.Sz, nItems * G1.Sz); using ArrayPoolList rawScalars = new(nItems * 32, nItems * 32); using ArrayPoolList pointDestinations = new(nItems); @@ -66,34 +68,39 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec } bool fail = false; - Parallel.ForEach(pointDestinations, (dest, state, i) => + +#pragma warning disable CS0162 // Unreachable code detected + if (BlsConst.DisableConcurrency) { - if (dest != -1) + for (int i = 0; i < pointDestinations.Count; i++) { - int offset = (int)i * ItemSize; - ReadOnlySpan rawPoint = inputData[offset..(offset + BlsConst.LenG1)].Span; - ReadOnlySpan rawScalar = inputData[(offset + BlsConst.LenG1)..(offset + ItemSize)].Span; - - G1 p = new(rawPoints.AsSpan()[(dest * 18)..]); - - if (!p.TryDecodeRaw(rawPoint) || !p.InGroup()) + if (!BlsExtensions.TryDecodeG1ToBuffer(inputData, rawPoints.AsMemory(), rawScalars.AsMemory(), pointDestinations[i], i)) { fail = true; - state.Break(); + break; } - - int destOffset = dest * 32; - rawScalar.CopyTo(rawScalars.AsSpan()[destOffset..]); - rawScalars.AsSpan()[destOffset..(destOffset + 32)].Reverse(); } - }); + } + else + { + Parallel.ForEach(pointDestinations, (dest, state, i) => + { + int index = (int)i; + if (!BlsExtensions.TryDecodeG1ToBuffer(inputData, rawPoints.AsMemory(), rawScalars.AsMemory(), dest, index)) + { + fail = true; + state.Break(); + } + }); + } +#pragma warning restore CS0162 // Unreachable code detected if (fail) { return IPrecompile.Failure; } - G1 res = new G1().MultiMult(rawPoints.AsSpan(), rawScalars.AsSpan(), npoints); + G1 res = new G1(stackalloc long[G1.Sz]).MultiMult(rawPoints.AsSpan(), rawScalars.AsSpan(), npoints); return (res.EncodeRaw(), true); } } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MulPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MulPrecompile.cs index ef78ac65181..18eb5889913 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MulPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G1MulPrecompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; using G1 = Nethermind.Crypto.Bls.P1; @@ -25,6 +26,7 @@ private G1MulPrecompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG1MulPrecompile++; @@ -36,7 +38,7 @@ private G1MulPrecompile() } G1 x = new(stackalloc long[G1.Sz]); - if (!x.TryDecodeRaw(inputData[..BlsConst.LenG1].Span) || !x.InGroup()) + if (!x.TryDecodeRaw(inputData[..BlsConst.LenG1].Span) || !(BlsConst.DisableSubgroupChecks || x.InGroup())) { return IPrecompile.Failure; } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2AddPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2AddPrecompile.cs index ad0ed53a8d3..710954f3a3b 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2AddPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2AddPrecompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; @@ -26,6 +27,7 @@ private G2AddPrecompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG2AddPrecompile++; diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MultiMulPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MSMPrecompile.cs similarity index 59% rename from src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MultiMulPrecompile.cs rename to src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MSMPrecompile.cs index 714d0d6ad72..495e5c91a59 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MultiMulPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MSMPrecompile.cs @@ -8,17 +8,18 @@ using System.Threading.Tasks; using G2 = Nethermind.Crypto.Bls.P2; +using System.Runtime.CompilerServices; namespace Nethermind.Evm.Precompiles.Bls; /// /// https://eips.ethereum.org/EIPS/eip-2537 /// -public class G2MultiMulPrecompile : IPrecompile +public class G2MSMPrecompile : IPrecompile { - public static readonly G2MultiMulPrecompile Instance = new(); + public static readonly G2MSMPrecompile Instance = new(); - private G2MultiMulPrecompile() + private G2MSMPrecompile() { } @@ -32,8 +33,9 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec return 45000L * k * Discount.For(k) / 1000; } - private const int ItemSize = 288; + public const int ItemSize = 288; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG2MSMPrecompile++; @@ -45,8 +47,8 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec int nItems = inputData.Length / ItemSize; - using ArrayPoolList rawPoints = new(nItems * 36, nItems * 36); - using ArrayPoolList rawScalars = new(nItems * 32, nItems * 32); + using ArrayPoolList pointBuffer = new(nItems * G2.Sz, nItems * G2.Sz); + using ArrayPoolList scalarBuffer = new(nItems * 32, nItems * 32); using ArrayPoolList pointDestinations = new(nItems); int npoints = 0; @@ -66,34 +68,39 @@ public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec } bool fail = false; - Parallel.ForEach(pointDestinations, (dest, state, i) => + +#pragma warning disable CS0162 // Unreachable code detected + if (BlsConst.DisableConcurrency) { - if (dest != -1) + for (int i = 0; i < pointDestinations.Count; i++) { - int offset = (int)i * ItemSize; - ReadOnlySpan rawPoint = inputData[offset..(offset + BlsConst.LenG2)].Span; - ReadOnlySpan rawScalar = inputData[(offset + BlsConst.LenG2)..(offset + ItemSize)].Span; - - G2 p = new(rawPoints.AsSpan()[(dest * 36)..]); - - if (!p.TryDecodeRaw(rawPoint) || !p.InGroup()) + if (!BlsExtensions.TryDecodeG2ToBuffer(inputData, pointBuffer.AsMemory(), scalarBuffer.AsMemory(), pointDestinations[i], i)) { fail = true; - state.Break(); + break; } - - int destOffset = dest * 32; - rawScalar.CopyTo(rawScalars.AsSpan()[destOffset..]); - rawScalars.AsSpan()[destOffset..(destOffset + 32)].Reverse(); } - }); + } + else + { + Parallel.ForEach(pointDestinations, (dest, state, i) => + { + int index = (int)i; + if (!BlsExtensions.TryDecodeG2ToBuffer(inputData, pointBuffer.AsMemory(), scalarBuffer.AsMemory(), dest, index)) + { + fail = true; + state.Break(); + } + }); + } +#pragma warning restore CS0162 // Unreachable code detected if (fail) { return IPrecompile.Failure; } - G2 res = new G2().MultiMult(rawPoints.AsSpan(), rawScalars.AsSpan(), npoints); + G2 res = new G2(stackalloc long[G2.Sz]).MultiMult(pointBuffer.AsSpan(), scalarBuffer.AsSpan(), npoints); return (res.EncodeRaw(), true); } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MulPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MulPrecompile.cs index 4756c49620a..fbe99e32867 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MulPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/G2MulPrecompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; @@ -26,6 +27,7 @@ private G2MulPrecompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsG2MulPrecompile++; @@ -38,7 +40,7 @@ private G2MulPrecompile() } G2 x = new(stackalloc long[G2.Sz]); - if (!x.TryDecodeRaw(inputData[..BlsConst.LenG2].Span) || !x.InGroup()) + if (!x.TryDecodeRaw(inputData[..BlsConst.LenG2].Span) || !(BlsConst.DisableSubgroupChecks || x.InGroup())) { return IPrecompile.Failure; } diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG2Precompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFp2ToG2Precompile.cs similarity index 85% rename from src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG2Precompile.cs rename to src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFp2ToG2Precompile.cs index 9443a8c78da..5190c58be57 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG2Precompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFp2ToG2Precompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; @@ -12,11 +13,11 @@ namespace Nethermind.Evm.Precompiles.Bls; /// /// https://eips.ethereum.org/EIPS/eip-2537 /// -public class MapToG2Precompile : IPrecompile +public class MapFp2ToG2Precompile : IPrecompile { - public static readonly MapToG2Precompile Instance = new(); + public static readonly MapFp2ToG2Precompile Instance = new(); - private MapToG2Precompile() + private MapFp2ToG2Precompile() { } @@ -26,6 +27,7 @@ private MapToG2Precompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsMapFp2ToG2Precompile++; diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG1Precompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFpToG1Precompile.cs similarity index 82% rename from src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG1Precompile.cs rename to src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFpToG1Precompile.cs index 441ad1d3c8b..eb1032ea1f5 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapToG1Precompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/MapFpToG1Precompile.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Specs; using G1 = Nethermind.Crypto.Bls.P1; @@ -11,11 +12,11 @@ namespace Nethermind.Evm.Precompiles.Bls; /// /// https://eips.ethereum.org/EIPS/eip-2537 /// -public class MapToG1Precompile : IPrecompile +public class MapFpToG1Precompile : IPrecompile { - public static readonly MapToG1Precompile Instance = new MapToG1Precompile(); + public static readonly MapFpToG1Precompile Instance = new MapFpToG1Precompile(); - private MapToG1Precompile() + private MapFpToG1Precompile() { } @@ -25,6 +26,7 @@ private MapToG1Precompile() public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 0L; + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsMapFpToG1Precompile++; diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingPrecompile.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingCheckPrecompile.cs similarity index 74% rename from src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingPrecompile.cs rename to src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingCheckPrecompile.cs index cf3412e4dab..6be90e9054a 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingPrecompile.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingCheckPrecompile.cs @@ -2,7 +2,9 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Runtime.CompilerServices; using Nethermind.Core; +using Nethermind.Core.Collections; using Nethermind.Core.Specs; using G1 = Nethermind.Crypto.Bls.P1; @@ -14,12 +16,12 @@ namespace Nethermind.Evm.Precompiles.Bls; /// /// https://eips.ethereum.org/EIPS/eip-2537 /// -public class PairingPrecompile : IPrecompile +public class PairingCheckPrecompile : IPrecompile { private const int PairSize = 384; - public static readonly PairingPrecompile Instance = new(); + public static readonly PairingCheckPrecompile Instance = new(); - private PairingPrecompile() { } + private PairingCheckPrecompile() { } public static Address Address { get; } = Address.FromNumber(0x11); @@ -27,6 +29,7 @@ private PairingPrecompile() { } public long DataGasCost(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) => 43000L * (inputData.Length / PairSize); + [SkipLocalsInit] public (ReadOnlyMemory, bool) Run(ReadOnlyMemory inputData, IReleaseSpec releaseSpec) { Metrics.BlsPairingCheckPrecompile++; @@ -39,15 +42,18 @@ private PairingPrecompile() { } G1 x = new(stackalloc long[G1.Sz]); G2 y = new(stackalloc long[G2.Sz]); - var acc = GT.One(); + using ArrayPoolList buf = new(GT.Sz * 2, GT.Sz * 2); + var acc = GT.One(buf.AsSpan()); + GT p = new(buf.AsSpan()[GT.Sz..]); + for (int i = 0; i < inputData.Length / PairSize; i++) { int offset = i * PairSize; if (!x.TryDecodeRaw(inputData[offset..(offset + BlsConst.LenG1)].Span) || - !x.InGroup() || + !(BlsConst.DisableSubgroupChecks || x.InGroup()) || !y.TryDecodeRaw(inputData[(offset + BlsConst.LenG1)..(offset + PairSize)].Span) || - !y.InGroup()) + !(BlsConst.DisableSubgroupChecks || y.InGroup())) { return IPrecompile.Failure; } @@ -58,7 +64,8 @@ private PairingPrecompile() { } continue; } - acc.Mul(new GT(y, x)); + p.MillerLoop(y, x); + acc.Mul(p); } bool verified = acc.FinalExp().IsOne(); diff --git a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs index 37f181cee8e..bc89734d8c5 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/GethStyle/Custom/Native/Call/NativeCallTracerCallFrameConverter.cs @@ -49,13 +49,13 @@ public override void Write(Utf8JsonWriter writer, NativeCallTracerCallFrame valu } else { - JsonSerializer.Serialize(writer, value.Input.AsMemory(), options); + JsonSerializer.Serialize(writer, value.Input.AsReadOnlyMemory(), options); } if (value.Output?.Count > 0) { writer.WritePropertyName("output"u8); - JsonSerializer.Serialize(writer, value.Output.AsMemory(), options); + JsonSerializer.Serialize(writer, value.Output.AsReadOnlyMemory(), options); } if (value.Error is not null) diff --git a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs index 439427fb1c2..dee699912cc 100644 --- a/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs +++ b/src/Nethermind/Nethermind.Evm/Tracing/TxTracer.cs @@ -10,7 +10,7 @@ namespace Nethermind.Evm.Tracing; -public class TxTracer : ITxTracer +public abstract class TxTracer : ITxTracer { [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] protected TxTracer() diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 72fb5465645..b080819523c 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; @@ -17,7 +18,6 @@ using Nethermind.Logging; using Nethermind.State; using Nethermind.State.Tracing; -using static Nethermind.Core.Extensions.MemoryExtensions; using static Nethermind.Evm.VirtualMachine; @@ -41,6 +41,7 @@ public abstract class TransactionProcessorBase : ITransactionProcessor private readonly ICodeInfoRepository _codeInfoRepository; private SystemTransactionProcessor? _systemTransactionProcessor; private readonly ILogManager _logManager; + private readonly HashSet
_accessedAddresses = []; [Flags] protected enum ExecutionOptions @@ -149,10 +150,13 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitStorageRoots: false); - ExecutionEnvironment env = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice); + _accessedAddresses.Clear(); + int delegationRefunds = ProcessDelegations(tx, spec, _accessedAddresses); + + ExecutionEnvironment env = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, _codeInfoRepository, _accessedAddresses); long gasAvailable = tx.GasLimit - intrinsicGas; - ExecuteEvmCall(tx, header, spec, tracer, opts, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode); + ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, _accessedAddresses, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode); PayFees(tx, header, spec, tracer, substate, spentGas, premiumPerGas, blobBaseFee, statusCode); // Finalize @@ -168,6 +172,7 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon if (!opts.HasFlag(ExecutionOptions.NoValidation)) WorldState.AddToBalance(tx.SenderAddress!, senderReservedGasPayment, spec); DecrementNonce(tx); + WorldState.Commit(spec); } } @@ -200,6 +205,74 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon return TransactionResult.Ok; } + private int ProcessDelegations(Transaction tx, IReleaseSpec spec, HashSet
accessedAddresses) + { + int refunds = 0; + if (spec.IsEip7702Enabled && tx.HasAuthorizationList) + { + foreach (AuthorizationTuple authTuple in tx.AuthorizationList) + { + authTuple.Authority ??= Ecdsa.RecoverAddress(authTuple); + + if (!IsValidForExecution(authTuple, accessedAddresses, out _)) + { + if (Logger.IsDebug) Logger.Debug($"Delegation {authTuple} is invalid"); + } + else + { + if (!WorldState.AccountExists(authTuple.Authority!)) + { + WorldState.CreateAccount(authTuple.Authority, 0, 1); + } + else + { + refunds++; + WorldState.IncrementNonce(authTuple.Authority); + } + + _codeInfoRepository.SetDelegation(WorldState, authTuple.CodeAddress, authTuple.Authority, spec); + } + } + + } + + return refunds; + + bool IsValidForExecution( + AuthorizationTuple authorizationTuple, + ISet
accessedAddresses, + [NotNullWhen(false)] out string? error) + { + if (authorizationTuple.Authority is null) + { + error = "Bad signature."; + return false; + } + if (authorizationTuple.ChainId != 0 && SpecProvider.ChainId != authorizationTuple.ChainId) + { + error = $"Chain id ({authorizationTuple.ChainId}) does not match."; + return false; + } + + accessedAddresses.Add(authorizationTuple.Authority); + + if (WorldState.HasCode(authorizationTuple.Authority) && !_codeInfoRepository.TryGetDelegation(WorldState, authorizationTuple.Authority, out _)) + { + error = $"Authority ({authorizationTuple.Authority}) has code deployed."; + return false; + } + UInt256 authNonce = WorldState.GetNonce(authorizationTuple.Authority); + if (authNonce != authorizationTuple.Nonce) + { + error = $"Skipping tuple in authorization_list because nonce is set to {authorizationTuple.Nonce}, but authority ({authorizationTuple.Authority}) has {authNonce}."; + return false; + } + + error = null; + return true; + } + } + protected virtual IReleaseSpec GetSpec(Transaction tx, BlockHeader header) => SpecProvider.GetSpec(header); private static void UpdateMetrics(ExecutionOptions opts, UInt256 effectiveGasPrice) @@ -377,6 +450,7 @@ 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); @@ -431,16 +505,24 @@ private ExecutionEnvironment BuildExecutionEnvironment( Transaction tx, in BlockExecutionContext blCtx, IReleaseSpec spec, - in UInt256 effectiveGasPrice) + in UInt256 effectiveGasPrice, + ICodeInfoRepository codeInfoRepository, + HashSet
accessedAddresses) { 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!); + accessedAddresses.Add(recipient); + accessedAddresses.Add(tx.SenderAddress!); + TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository); + Address? delegationAddress = null; CodeInfo codeInfo = tx.IsContractCreation ? new(tx.Data ?? Memory.Empty) - : _codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec); + : codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec, out delegationAddress); + + if (delegationAddress is not null) + accessedAddresses.Add(delegationAddress); codeInfo.AnalyseInBackgroundIfRequired(); @@ -467,6 +549,8 @@ protected virtual void ExecuteEvmCall( IReleaseSpec spec, ITxTracer tracer, ExecutionOptions opts, + int delegationRefunds, + IEnumerable
accessedAddresses, in long gasAvailable, in ExecutionEnvironment env, out TransactionSubstate? substate, @@ -490,7 +574,7 @@ protected virtual void ExecuteEvmCall( if (tx.IsContractCreation) { // if transaction is a contract creation then recipient address is the contract deployment address - if (!PrepareAccountForContractDeployment(env.ExecutingAccount, spec)) + if (!PrepareAccountForContractDeployment(env.ExecutingAccount, _codeInfoRepository, spec)) { goto Fail; } @@ -500,21 +584,7 @@ protected virtual void ExecuteEvmCall( 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!); - } + WarmUp(tx, header, spec, state, accessedAddresses); substate = !tracer.IsTracingActions ? VirtualMachine.Run(state, WorldState, tracer) @@ -574,7 +644,7 @@ protected virtual void ExecuteEvmCall( statusCode = StatusCode.Success; } - spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice); + spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice, delegationRefunds); goto Complete; } catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not @@ -595,6 +665,27 @@ protected virtual void PayValue(Transaction tx, IReleaseSpec spec, ExecutionOpti WorldState.SubtractFromBalance(tx.SenderAddress!, tx.Value, spec); } + private void WarmUp(Transaction tx, BlockHeader header, IReleaseSpec spec, EvmState state, IEnumerable
accessedAddresses) + { + if (spec.UseTxAccessLists) + { + state.WarmUp(tx.AccessList); // eip-2930 + } + + if (spec.UseHotAndColdStorage) // eip-2929 + { + foreach (Address accessed in accessedAddresses) + { + state.WarmUp(accessed); + } + } + + if (spec.AddCoinbaseToTxAccessList) + { + state.WarmUp(header.GasBeneficiary!); + } + } + protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, in TransactionSubstate substate, in long spentGas, in UInt256 premiumPerGas, in UInt256 blobBaseFee, in byte statusCode) { bool gasBeneficiaryNotDestroyed = substate?.DestroyList.Contains(header.GasBeneficiary) != true; @@ -619,9 +710,9 @@ protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec } } - protected bool PrepareAccountForContractDeployment(Address contractAddress, IReleaseSpec spec) + protected bool PrepareAccountForContractDeployment(Address contractAddress, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec) { - if (WorldState.AccountExists(contractAddress) && contractAddress.IsNonZeroAccount(spec, _codeInfoRepository, WorldState)) + if (WorldState.AccountExists(contractAddress) && contractAddress.IsNonZeroAccount(spec, codeInfoRepository, WorldState)) { if (Logger.IsTrace) Logger.Trace($"Contract collision at {contractAddress}"); @@ -638,22 +729,36 @@ protected void TraceLogInvalidTx(Transaction transaction, string reason) } protected virtual long Refund(Transaction tx, BlockHeader header, IReleaseSpec spec, ExecutionOptions opts, - in TransactionSubstate substate, in long unspentGas, in UInt256 gasPrice) + in TransactionSubstate substate, in long unspentGas, in UInt256 gasPrice, int codeInsertRefunds) { long spentGas = tx.GasLimit; + var codeInsertRefund = (GasCostOf.NewAccount - GasCostOf.PerAuthBaseCost) * codeInsertRefunds; + if (!substate.IsError) { spentGas -= unspentGas; - long refund = substate.ShouldRevert - ? 0 - : RefundHelper.CalculateClaimableRefund(spentGas, - substate.Refund + substate.DestroyList.Count * RefundOf.Destroy(spec.IsEip3529Enabled), spec); + + long totalToRefund = codeInsertRefund; + if (!substate.ShouldRevert) + totalToRefund += substate.Refund + substate.DestroyList.Count * RefundOf.Destroy(spec.IsEip3529Enabled); + long actualRefund = RefundHelper.CalculateClaimableRefund(spentGas, totalToRefund, spec); + + if (Logger.IsTrace) + Logger.Trace("Refunding unused gas of " + unspentGas + " and refund of " + actualRefund); + // If noValidation we didn't charge for gas, so do not refund + if (!opts.HasFlag(ExecutionOptions.NoValidation)) + WorldState.AddToBalance(tx.SenderAddress!, (ulong)(unspentGas + actualRefund) * gasPrice, spec); + spentGas -= actualRefund; + } + else if (codeInsertRefund > 0) + { + long refund = RefundHelper.CalculateClaimableRefund(spentGas, codeInsertRefund, spec); if (Logger.IsTrace) - Logger.Trace("Refunding unused gas of " + unspentGas + " and refund of " + refund); + Logger.Trace("Refunding delegations only: " + 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); + WorldState.AddToBalance(tx.SenderAddress!, (ulong)refund * gasPrice, spec); spentGas -= refund; } diff --git a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs index 637c6fc0258..e1aa68c2b4e 100644 --- a/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs +++ b/src/Nethermind/Nethermind.Evm/TxExecutionContext.cs @@ -6,19 +6,17 @@ namespace Nethermind.Evm { - public readonly struct TxExecutionContext + public readonly struct TxExecutionContext( + in BlockExecutionContext blockExecutionContext, + Address origin, + in UInt256 gasPrice, + byte[][] blobVersionedHashes, + ICodeInfoRepository codeInfoRepository) { - public readonly BlockExecutionContext BlockExecutionContext; - public Address Origin { get; } - public UInt256 GasPrice { get; } - public byte[][]? BlobVersionedHashes { get; } - - public TxExecutionContext(in BlockExecutionContext blockExecutionContext, Address origin, in UInt256 gasPrice, byte[][] blobVersionedHashes) - { - BlockExecutionContext = blockExecutionContext; - Origin = origin; - GasPrice = gasPrice; - BlobVersionedHashes = blobVersionedHashes; - } + public readonly BlockExecutionContext BlockExecutionContext = blockExecutionContext; + public Address Origin { get; } = origin; + public UInt256 GasPrice { get; } = gasPrice; + public byte[][]? BlobVersionedHashes { get; } = blobVersionedHashes; + public ICodeInfoRepository CodeInfoRepository { get; } = codeInfoRepository; } } diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index c1b9ab1af67..b5ca0aa4a7d 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using Nethermind.Core; using Nethermind.Core.Crypto; @@ -27,9 +26,9 @@ [assembly: InternalsVisibleTo("Nethermind.Evm.Test")] namespace Nethermind.Evm; - using Int256; + public class VirtualMachine : IVirtualMachine { public const int MaxCallDepth = 1024; @@ -69,8 +68,8 @@ public VirtualMachine( { ILogger logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _evm = logger.IsTrace - ? new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, logger) - : new VirtualMachine(blockhashProvider, specProvider, codeInfoRepository, logger); + ? new VirtualMachine(blockhashProvider, specProvider, logger) + : new VirtualMachine(blockhashProvider, specProvider, logger); } public TransactionSubstate Run(EvmState state, IWorldState worldState, ITxTracer txTracer) @@ -140,23 +139,20 @@ internal sealed class VirtualMachine : IVirtualMachine where TLogger : private readonly IBlockhashProvider _blockhashProvider; private readonly ISpecProvider _specProvider; private readonly ILogger _logger; - private IWorldState _state; + private IWorldState _state = null!; private readonly Stack _stateStack = new(); private (Address Address, bool ShouldDelete) _parityTouchBugAccount = (Address.FromNumber(3), false); private ReadOnlyMemory _returnDataBuffer = Array.Empty(); private ITxTracer _txTracer = NullTxTracer.Instance; - private readonly ICodeInfoRepository _codeInfoRepository; public VirtualMachine( IBlockhashProvider? blockhashProvider, ISpecProvider? specProvider, - ICodeInfoRepository codeInfoRepository, ILogger? logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _blockhashProvider = blockhashProvider ?? throw new ArgumentNullException(nameof(blockhashProvider)); _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider)); - _codeInfoRepository = codeInfoRepository ?? throw new ArgumentNullException(nameof(codeInfoRepository)); _chainId = ((UInt256)specProvider.ChainId).ToBigEndian(); } @@ -166,7 +162,9 @@ public TransactionSubstate Run(EvmState state, IWorldState worl _txTracer = txTracer; _state = worldState; - IReleaseSpec spec = _specProvider.GetSpec(state.Env.TxExecutionContext.BlockExecutionContext.Header.Number, state.Env.TxExecutionContext.BlockExecutionContext.Header.Timestamp); + ref readonly TxExecutionContext txExecutionContext = ref state.Env.TxExecutionContext; + ICodeInfoRepository codeInfoRepository = txExecutionContext.CodeInfoRepository; + IReleaseSpec spec = _specProvider.GetSpec(txExecutionContext.BlockExecutionContext.Header.Number, txExecutionContext.BlockExecutionContext.Header.Timestamp); EvmState currentState = state; ReadOnlyMemory? previousCallResult = null; ZeroPaddedSpan previousCallOutput = ZeroPaddedSpan.Empty; @@ -347,7 +345,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); currentState.GasAvailable -= codeDepositGasCost; @@ -494,13 +492,22 @@ private static void UpdateGasUp(long refund, ref long gasAvailable) gasAvailable += refund; } - private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec, bool chargeForWarm = true) + private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Address address, bool chargeForDelegation, IReleaseSpec spec, bool chargeForWarm = true) { - // Console.WriteLine($"Accessing {address}"); + if (!spec.UseHotAndColdStorage) + { + return true; + } + bool notOutOfGas = ChargeAccountGas(ref gasAvailable, vmState, address, spec); + return notOutOfGas + && chargeForDelegation + && vmState.Env.TxExecutionContext.CodeInfoRepository.TryGetDelegation(_state, address, out Address delegated) + ? ChargeAccountGas(ref gasAvailable, vmState, delegated, spec) + : notOutOfGas; - bool result = true; - if (spec.UseHotAndColdStorage) + bool ChargeAccountGas(ref long gasAvailable, EvmState vmState, Address address, IReleaseSpec spec) { + bool result = true; if (_txTracer.IsTracingAccess) // when tracing access we want cost as if it was warmed up from access list { vmState.WarmUp(address); @@ -515,9 +522,8 @@ private bool ChargeAccountAccessGas(ref long gasAvailable, EvmState vmState, Add { result = UpdateGas(GasCostOf.WarmStateRead, ref gasAvailable); } + return result; } - - return result; } private enum StorageAccessType @@ -1170,7 +1176,7 @@ private CallResult ExecuteCode externalCode = _codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; + ReadOnlyMemory externalCode = txCtx.CodeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; slice = externalCode.SliceWithZeroPadding(b, (int)result); vmState.Memory.Save(in a, in slice); if (typeof(TTracingInstructions) == typeof(IsTracing)) @@ -1914,15 +1920,17 @@ private CallResult ExecuteCode [SkipLocalsInit] [MethodImpl(MethodImplOptions.NoInlining)] - private void InstructionExtCodeSize(Address address, ref EvmStack stack, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing + private void InstructionExtCodeSize(Address address, ref EvmStack stack, ICodeInfoRepository codeInfoRepository, IReleaseSpec spec) where TTracingInstructions : struct, IIsTracing { - ReadOnlyMemory accountCode = _codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; + ReadOnlyMemory accountCode = codeInfoRepository.GetCachedCodeInfo(_state, address, spec).MachineCode; UInt256 result = (UInt256)accountCode.Span.Length; stack.PushUInt256(in result); } @@ -2092,7 +2100,7 @@ private EvmExceptionType InstructionCall( Address codeSource = stack.PopAddress(); if (codeSource is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, spec)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, codeSource, true, spec)) return EvmExceptionType.OutOfGas; UInt256 callValue; switch (instruction) @@ -2117,6 +2125,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; @@ -2147,7 +2156,7 @@ private EvmExceptionType InstructionCall( !UpdateMemoryCost(vmState, ref gasAvailable, in outputOffset, outputLength) || !UpdateGas(gasExtra, ref gasAvailable)) return EvmExceptionType.OutOfGas; - CodeInfo codeInfo = _codeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); + CodeInfo codeInfo = vmState.Env.TxExecutionContext.CodeInfoRepository.GetCachedCodeInfo(_state, codeSource, spec); codeInfo.AnalyseInBackgroundIfRequired(); if (spec.Use63Over64Rule) @@ -2166,8 +2175,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(); @@ -2311,7 +2319,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref Address inheritor = stack.PopAddress(); if (inheritor is null) return EvmExceptionType.StackUnderflow; - if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, spec, false)) return EvmExceptionType.OutOfGas; + if (!ChargeAccountAccessGas(ref gasAvailable, vmState, inheritor, false, spec, false)) return EvmExceptionType.OutOfGas; Address executingAccount = vmState.Env.ExecutingAccount; bool createInSameTx = vmState.CreateList.Contains(executingAccount); @@ -2436,7 +2444,7 @@ private EvmExceptionType InstructionSelfDestruct(EvmState vmState, ref bool accountExists = _state.AccountExists(contractAddress); - if (accountExists && contractAddress.IsNonZeroAccount(spec, _codeInfoRepository, _state)) + if (accountExists && contractAddress.IsNonZeroAccount(spec, env.TxExecutionContext.CodeInfoRepository, _state)) { /* we get the snapshot before this as there is a possibility with that we will touch an empty account and remove it even if the REVERT operation follows */ if (typeof(TLogger) == typeof(IsTracing)) _logger.Trace($"Contract collision at {contractAddress}"); @@ -2777,30 +2785,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/AuthorizationTupleForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs new file mode 100644 index 00000000000..c35c97b1d56 --- /dev/null +++ b/src/Nethermind/Nethermind.Facade/Eth/AuthorizationTupleForRpc.cs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Int256; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Nethermind.Facade.Eth +{ + public struct AuthorizationTupleForRpc + { + [JsonConstructor] + public AuthorizationTupleForRpc() + { + } + public AuthorizationTupleForRpc(UInt256 chainId, ulong nonce, Address address, UInt256? yParity, UInt256? s, UInt256? r) + { + ChainId = chainId; + Nonce = nonce; + Address = address; + YParity = yParity; + S = s; + R = r; + } + + public UInt256 ChainId { get; set; } + public ulong Nonce { get; set; } + public Address Address { get; set; } + public UInt256? YParity { get; set; } + public UInt256? S { get; set; } + public UInt256? R { get; set; } + + public static IEnumerable FromAuthorizationList(AuthorizationTuple[] authorizationList) => + authorizationList.Select(tuple => new AuthorizationTupleForRpc(tuple.ChainId, + tuple.Nonce, + tuple.CodeAddress, + tuple.AuthoritySignature.RecoveryId, + new UInt256(tuple.AuthoritySignature.S), + new UInt256(tuple.AuthoritySignature.R))); + } +} diff --git a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs index d51ed363156..5b054f48106 100644 --- a/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs +++ b/src/Nethermind/Nethermind.Facade/Eth/TransactionForRpc.cs @@ -61,6 +61,11 @@ public TransactionForRpc(Hash256? blockHash, long? blockNumber, int? txIndex, Tr { AccessList = null; } + AuthorizationList = transaction.SupportsAuthorizationList + ? transaction.AuthorizationList is null + ? Array.Empty() + : AuthorizationTupleForRpc.FromAuthorizationList(transaction.AuthorizationList) + : null; MaxFeePerBlobGas = transaction.MaxFeePerBlobGas; BlobVersionedHashes = transaction.BlobVersionedHashes; @@ -126,6 +131,8 @@ public TransactionForRpc() { } public TxType Type { get; set; } public IEnumerable? AccessList { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IEnumerable? AuthorizationList { get; set; } [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public UInt256? MaxFeePerBlobGas { get; set; } // eip4844 diff --git a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs index dbe43ddddc7..082d5ad57fe 100644 --- a/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs +++ b/src/Nethermind/Nethermind.Facade/OverridableCodeInfoRepository.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; @@ -16,17 +17,17 @@ public class OverridableCodeInfoRepository(ICodeInfoRepository codeInfoRepositor { private readonly Dictionary _codeOverwrites = new(); - public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec) => - _codeOverwrites.TryGetValue(codeSource, out CodeInfo result) + public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress) + { + delegationAddress = null; + return _codeOverwrites.TryGetValue(codeSource, out CodeInfo result) ? result : codeInfoRepository.GetCachedCodeInfo(worldState, codeSource, vmSpec); - - 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 SetCodeOverwrite( IWorldState worldState, IReleaseSpec vmSpec, @@ -36,9 +37,18 @@ public void SetCodeOverwrite( { if (redirectAddress is not null) { - _codeOverwrites[redirectAddress] = GetCachedCodeInfo(worldState, key, vmSpec); + _codeOverwrites[redirectAddress] = this.GetCachedCodeInfo(worldState, key, vmSpec); } _codeOverwrites[key] = value; } + + public void SetDelegation(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec) => + codeInfoRepository.SetDelegation(state, codeSource, authority, spec); + + public bool TryGetDelegation(IReadOnlyStateProvider worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress) => + codeInfoRepository.TryGetDelegation(worldState, address, out delegatedAddress); + + public ValueHash256 GetExecutableCodeHash(IWorldState worldState, Address address) => + codeInfoRepository.GetExecutableCodeHash(worldState, address); } diff --git a/src/Nethermind/Nethermind.Facade/Proxy/DefaultHttpClient.cs b/src/Nethermind/Nethermind.Facade/Proxy/DefaultHttpClient.cs index aac11f47041..53765e36759 100644 --- a/src/Nethermind/Nethermind.Facade/Proxy/DefaultHttpClient.cs +++ b/src/Nethermind/Nethermind.Facade/Proxy/DefaultHttpClient.cs @@ -79,7 +79,7 @@ private async Task ProcessRequestAsync(Method method, string endpoint, str string methodType = method.ToString(); string json = payload is null ? "{}" : _jsonSerializer.Serialize(payload); if (_logger.IsTrace) _logger.Trace($"Sending HTTP {methodType} request to: {endpoint} [id: {requestId}]{(method == Method.Get ? "." : $": {json}")}"); - Stopwatch stopWatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); HttpResponseMessage response; switch (method) { @@ -94,9 +94,7 @@ private async Task ProcessRequestAsync(Method method, string endpoint, str if (_logger.IsError) _logger.Error($"Unsupported HTTP method: {methodType}."); return default; } - - stopWatch.Stop(); - if (_logger.IsTrace) _logger.Trace($"Received HTTP {methodType} response from: {endpoint} [id: {requestId}, elapsed: {stopWatch.ElapsedMilliseconds} ms]: {response}"); + if (_logger.IsTrace) _logger.Trace($"Received HTTP {methodType} response from: {endpoint} [id: {requestId}, elapsed: {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0} ms]: {response}"); if (!response.IsSuccessStatusCode) { return default; diff --git a/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs b/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs index 4912b4ba817..337729eb705 100644 --- a/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs +++ b/src/Nethermind/Nethermind.Init/Steps/EthereumStepsManager.cs @@ -147,14 +147,14 @@ private void RunOneRoundOfInitialization(CancellationToken cancellationToken) private async Task ExecuteStep(IStep step, StepInfo stepInfo, CancellationToken cancellationToken) { - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); try { await step.Execute(cancellationToken); if (_logger.IsDebug) _logger.Debug( - $"Step {step.GetType().Name.PadRight(24)} executed in {stopwatch.ElapsedMilliseconds}ms"); + $"Step {step.GetType().Name.PadRight(24)} executed in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); stepInfo.Stage = StepInitializationStage.Complete; } @@ -164,7 +164,7 @@ private async Task ExecuteStep(IStep step, StepInfo stepInfo, CancellationToken { if (_logger.IsError) _logger.Error( - $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms", + $"Step {step.GetType().Name.PadRight(24)} failed after {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms", exception); stepInfo.Stage = StepInitializationStage.Failed; @@ -174,13 +174,12 @@ private async Task ExecuteStep(IStep step, StepInfo stepInfo, CancellationToken if (_logger.IsWarn) { _logger.Warn( - $"Step {step.GetType().Name.PadRight(24)} failed after {stopwatch.ElapsedMilliseconds}ms {exception}"); + $"Step {step.GetType().Name.PadRight(24)} failed after {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms {exception}"); } stepInfo.Stage = StepInitializationStage.Complete; } finally { - stopwatch.Stop(); _autoResetEvent.Set(); if (_logger.IsDebug) _logger.Debug($"{step.GetType().Name.PadRight(24)} complete"); diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs index 46e940ab03e..46d2bfd6818 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs @@ -58,7 +58,9 @@ protected virtual Task InitBlockchain() IReceiptConfig receiptConfig = getApi.Config(); IStateReader stateReader = setApi.StateReader!; - ITxPool txPool = _api.TxPool = CreateTxPool(); + PreBlockCaches? preBlockCaches = (_api.WorldState as IPreBlockCaches)?.Caches; + CodeInfoRepository codeInfoRepository = new(preBlockCaches?.PrecompileCache); + ITxPool txPool = _api.TxPool = CreateTxPool(codeInfoRepository); ReceiptCanonicalityMonitor receiptCanonicalityMonitor = new(getApi.ReceiptStorage, _api.LogManager); getApi.DisposeStack.Push(receiptCanonicalityMonitor); @@ -67,8 +69,7 @@ protected virtual Task InitBlockchain() _api.BlockPreprocessor.AddFirst( new RecoverSignatures(getApi.EthereumEcdsa, txPool, getApi.SpecProvider, getApi.LogManager)); - PreBlockCaches? preBlockCaches = (_api.WorldState as IPreBlockCaches)?.Caches; - CodeInfoRepository codeInfoRepository = new(preBlockCaches?.PrecompileCache); + VirtualMachine virtualMachine = CreateVirtualMachine(codeInfoRepository); _api.TransactionProcessor = CreateTransactionProcessor(codeInfoRepository, virtualMachine); @@ -80,17 +81,17 @@ protected virtual Task InitBlockchain() setApi.BlockValidator = CreateBlockValidator(); IChainHeadInfoProvider chainHeadInfoProvider = - new ChainHeadInfoProvider(getApi.SpecProvider!, getApi.BlockTree!, stateReader); + new ChainHeadInfoProvider(getApi.SpecProvider!, getApi.BlockTree!, stateReader, codeInfoRepository); // TODO: can take the tx sender from plugin here maybe ITxSigner txSigner = new WalletTxSigner(getApi.Wallet, getApi.SpecProvider!.ChainId); TxSealer nonceReservingTxSealer = new(txSigner, getApi.Timestamper); - INonceManager nonceManager = new NonceManager(chainHeadInfoProvider.AccountStateProvider); + INonceManager nonceManager = new NonceManager(chainHeadInfoProvider.ReadOnlyStateProvider); setApi.NonceManager = nonceManager; setApi.TxSender = new TxPoolSender(txPool, nonceReservingTxSealer, nonceManager, getApi.EthereumEcdsa!); - setApi.TxPoolInfoProvider = new TxPoolInfoProvider(chainHeadInfoProvider.AccountStateProvider, txPool); + setApi.TxPoolInfoProvider = new TxPoolInfoProvider(chainHeadInfoProvider.ReadOnlyStateProvider, txPool); setApi.GasPriceOracle = new GasPriceOracle(getApi.BlockTree!, getApi.SpecProvider, _api.LogManager, blocksConfig.MinGasPrice); BlockCachePreWarmer? preWarmer = blocksConfig.PreWarmStateOnBlockProcessing ? new(new(_api.WorldStateManager!, _api.BlockTree!, _api.SpecProvider, _api.LogManager, _api.WorldState), _api.SpecProvider!, _api.LogManager, preBlockCaches) @@ -205,10 +206,10 @@ protected virtual IHealthHintService CreateHealthHintService() => protected virtual IBlockProductionPolicy CreateBlockProductionPolicy() => new BlockProductionPolicy(_api.Config()); - protected virtual TxPool.TxPool CreateTxPool() => + protected virtual TxPool.TxPool CreateTxPool(CodeInfoRepository codeInfoRepository) => new(_api.EthereumEcdsa!, _api.BlobTxStorage ?? NullBlobTxStorage.Instance, - new ChainHeadInfoProvider(_api.SpecProvider!, _api.BlockTree!, _api.StateReader!), + new ChainHeadInfoProvider(_api.SpecProvider!, _api.BlockTree!, _api.StateReader!, codeInfoRepository), _api.Config(), _api.TxValidator!, _api.LogManager, diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs b/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs index 31143a8f43e..122ba122348 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializePlugins.cs @@ -30,11 +30,10 @@ public async Task Execute(CancellationToken cancellationToken) try { if (logger.IsInfo) logger.Info($" {plugin.Name} by {plugin.Author}"); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); await plugin.Init(_api); - stopwatch.Stop(); if (logger.IsInfo) - logger.Info($" {plugin.Name} by {plugin.Author} initialized in {stopwatch.ElapsedMilliseconds}ms"); + logger.Info($" {plugin.Name} by {plugin.Author} initialized in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); } catch (Exception e) { diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs index 914c3ebf8d8..843406aad94 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/Eth/EthRpcModuleTests.cs @@ -1201,6 +1201,101 @@ public async Task eth_getBlockByNumber_should_return_withdrawals_correctly() })), Is.EqualTo(result)); } + + [Test] + public async Task eth_sendRawTransaction_sender_with_non_delegated_code_is_rejected() + { + var specProvider = new TestSpecProvider(Prague.Instance); + specProvider.AllowTestChainOverride = false; + + TestRpcBlockchain Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(specProvider); + + Transaction testTx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(Test.State.GetNonce(TestItem.AddressA)) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount) + .WithAuthorizationCodeIfAuthorizationListTx() + .WithTo(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyA).TestObject; + + string result = await Test.TestEthRpc("eth_sendRawTransaction", Bytes.ToHexString(Rlp.Encode(testTx).Bytes)); + + JsonRpcErrorResponse actual = new EthereumJsonSerializer().Deserialize(result); + Assert.That(actual.Error!.Message, Is.EqualTo(nameof(AcceptTxResult.SenderIsContract))); + } + + + [Test] + public async Task eth_sendRawTransaction_sender_with_delegated_code_is_accepted() + { + var specProvider = new TestSpecProvider(Prague.Instance); + specProvider.AllowTestChainOverride = false; + + TestRpcBlockchain test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(specProvider); + Transaction setCodeTx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(test.State.GetNonce(TestItem.AddressB)) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount) + .WithAuthorizationCode(test.EthereumEcdsa.Sign(TestItem.PrivateKeyB, 0, Address.Zero, (ulong)test.State.GetNonce(TestItem.AddressB) + 1)) + .WithTo(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyB).TestObject; + + await test.AddBlock(setCodeTx); + + var code = test.State.GetCode(TestItem.AddressB); + + Assert.That(code!.Slice(0, 3), Is.EquivalentTo(Eip7702Constants.DelegationHeader.ToArray())); + + Transaction normalTx = Build.A.Transaction + .WithNonce(test.State.GetNonce(TestItem.AddressB)) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(GasCostOf.Transaction) + .WithTo(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyB).TestObject; + + string result = await test.TestEthRpc("eth_sendRawTransaction", Bytes.ToHexString(Rlp.Encode(normalTx).Bytes)); + + JsonRpcSuccessResponse actual = new EthereumJsonSerializer().Deserialize(result); + Assert.That(actual.Result, Is.Not.Null); + } + + [Test] + public async Task eth_sendRawTransaction_returns_correct_error_if_AuthorityTuple_has_null_value() + { + var specProvider = new TestSpecProvider(Prague.Instance); + specProvider.AllowTestChainOverride = false; + + TestRpcBlockchain test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).Build(specProvider); + Transaction invalidSetCodeTx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(test.State.GetNonce(TestItem.AddressB)) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(GasCostOf.Transaction + GasCostOf.NewAccount) + .WithAuthorizationCode(new AllowNullAuthorizationTuple(0, null, 0, new Signature(new byte[65]))) + .WithTo(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyB).TestObject; + + string result = await test.TestEthRpc("eth_sendRawTransaction", Bytes.ToHexString(Rlp.Encode(invalidSetCodeTx).Bytes)); + + JsonRpcErrorResponse actual = new EthereumJsonSerializer().Deserialize(result); + Assert.That(actual.Error!.Code, Is.EqualTo(ErrorCodes.TransactionRejected)); + } + public class AllowNullAuthorizationTuple : AuthorizationTuple + { + public AllowNullAuthorizationTuple(ulong chainId, Address? codeAddress, ulong nonce, Signature? sig) + : base(chainId, Address.Zero, nonce, new Signature(new byte[65])) + { + CodeAddress = codeAddress!; + AuthoritySignature = sig!; + } + } + private static (byte[] ByteCode, AccessListItemForRpc[] AccessList) GetTestAccessList(long loads = 2, bool allowSystemUser = true) { AccessListItemForRpc[] accessList = allowSystemUser diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs index 3fd4d6dbac0..708dd5575c1 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/ParityRpcModuleTests.cs @@ -35,6 +35,7 @@ using NSubstitute; using NUnit.Framework; using System; +using Nethermind.Evm; namespace Nethermind.JsonRpc.Test.Modules { @@ -75,7 +76,7 @@ public void Initialize() _txPool = new TxPool.TxPool(_ethereumEcdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(specProvider), _blockTree, stateProvider), + new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(specProvider), _blockTree, stateProvider, new CodeInfoRepository()), new TxPoolConfig(), new TxValidator(specProvider.ChainId), LimboLogs.Instance, diff --git a/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs b/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs index bcc2a257cab..b684ee14b23 100644 --- a/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs +++ b/src/Nethermind/Nethermind.JsonRpc/JsonRpcProcessor.cs @@ -119,7 +119,7 @@ private ArrayPoolList DeserializeArray(JsonElement element) => public async IAsyncEnumerable ProcessAsync(PipeReader reader, JsonRpcContext context) { reader = await RecordRequest(reader); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); CancellationTokenSource timeoutSource = new(_jsonRpcConfig.Timeout); // Handles general exceptions during parsing and validation. @@ -130,8 +130,7 @@ JsonRpcResult GetParsingError(string error, Exception? exception = null) if (_logger.IsError) _logger.Error(error, exception); JsonRpcErrorResponse response = _jsonRpcService.GetErrorResponse(ErrorCodes.ParseError, "Incorrect message"); TraceResult(response); - stopwatch.Stop(); - return JsonRpcResult.Single(RecordResponse(response, new RpcReport("# parsing error #", stopwatch.ElapsedMicroseconds(), false))); + return JsonRpcResult.Single(RecordResponse(response, new RpcReport("# parsing error #", (long)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds, false))); } // Initializes a buffer to store the data read from the reader. @@ -219,9 +218,6 @@ JsonRpcResult GetParsingError(string error, Exception? exception = null) yield return deserializationFailureResult.Value; break; } - - // Stops the stopwatch and yields the batch processing result. - stopwatch.Stop(); JsonRpcBatchResult jsonRpcBatchResult = new((e, c) => IterateRequest(collection, context, e).GetAsyncEnumerator(c)); jsonRpcBatchResult.AddDisposable(() => collection.Dispose()); yield return JsonRpcResult.Collection(jsonRpcBatchResult); @@ -235,9 +231,8 @@ JsonRpcResult GetParsingError(string error, Exception? exception = null) errorResponse.AddDisposable(() => jsonDocument.Dispose()); TraceResult(errorResponse); - stopwatch.Stop(); - if (_logger.IsDebug) _logger.Debug($" Failed request handled in {stopwatch.Elapsed.TotalMilliseconds}ms"); - deserializationFailureResult = JsonRpcResult.Single(RecordResponse(errorResponse, new RpcReport("# parsing error #", stopwatch.ElapsedMicroseconds(), false))); + if (_logger.IsDebug) _logger.Debug($" Failed request handled in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); + deserializationFailureResult = JsonRpcResult.Single(RecordResponse(errorResponse, new RpcReport("# parsing error #", (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds, false))); yield return deserializationFailureResult.Value; break; } @@ -304,7 +299,7 @@ static bool HasNonWhitespace(ReadOnlySpan span) { try { - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); int requestIndex = 0; for (int index = 0; index < requests.Count; index++) { @@ -325,7 +320,7 @@ static bool HasNonWhitespace(ReadOnlySpan span) yield return RecordResponse(response); } - if (_logger.IsDebug) _logger.Debug($" {requests.Count} requests handled in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (_logger.IsDebug) _logger.Debug($" {requests.Count} requests handled in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); } finally { @@ -336,7 +331,7 @@ static bool HasNonWhitespace(ReadOnlySpan span) private async Task HandleSingleRequest(JsonRpcRequest request, JsonRpcContext context) { Metrics.JsonRpcRequests++; - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); JsonRpcResponse response = await _jsonRpcService.SendRequestAsync(request, context); JsonRpcErrorResponse localErrorResponse = response as JsonRpcErrorResponse; @@ -355,11 +350,10 @@ static bool HasNonWhitespace(ReadOnlySpan span) Metrics.JsonRpcSuccesses++; } - stopwatch.Stop(); - if (_logger.IsDebug) _logger.Debug($" {request} handled in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (_logger.IsDebug) _logger.Debug($" {request} handled in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); - JsonRpcResult.Entry result = new(response, new RpcReport(request.Method, stopwatch.ElapsedMicroseconds(), isSuccess)); + JsonRpcResult.Entry result = new(response, new RpcReport(request.Method, (long)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds, isSuccess)); TraceResult(result); return result; } diff --git a/src/Nethermind/Nethermind.JsonRpc/WebSockets/JsonRpcSocketsClient.cs b/src/Nethermind/Nethermind.JsonRpc/WebSockets/JsonRpcSocketsClient.cs index 3cf54185a5e..ddf29f87e36 100644 --- a/src/Nethermind/Nethermind.JsonRpc/WebSockets/JsonRpcSocketsClient.cs +++ b/src/Nethermind/Nethermind.JsonRpc/WebSockets/JsonRpcSocketsClient.cs @@ -58,7 +58,6 @@ public override void Dispose() public override async Task ProcessAsync(ArraySegment data) { - Stopwatch stopwatch = Stopwatch.StartNew(); IncrementBytesReceivedMetric(data.Count); PipeReader request = PipeReader.Create(new ReadOnlySequence(data.Array!, data.Offset, data.Count)); int allResponsesSize = 0; @@ -67,23 +66,21 @@ public override async Task ProcessAsync(ArraySegment data) { using (result) { - stopwatch.Restart(); - int singleResponseSize = await SendJsonRpcResult(result); allResponsesSize += singleResponseSize; + long startTime = Stopwatch.GetTimestamp(); + if (result.IsCollection) { - long handlingTimeMicroseconds = stopwatch.ElapsedMicroseconds(); + long handlingTimeMicroseconds = (long)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds; _ = _jsonRpcLocalStats.ReportCall(new RpcReport("# collection serialization #", handlingTimeMicroseconds, true), handlingTimeMicroseconds, singleResponseSize); } else { - long handlingTimeMicroseconds = stopwatch.ElapsedMicroseconds(); + long handlingTimeMicroseconds = (long)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds; _ = _jsonRpcLocalStats.ReportCall(result.Report!.Value, handlingTimeMicroseconds, singleResponseSize); } - - stopwatch.Restart(); } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs index 386989099f8..14b1b8881c1 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ConsensusRequestsProcessorMock.cs @@ -20,7 +20,9 @@ public class ConsensusRequestsProcessorMock : IConsensusRequestsProcessor TestItem.DepositA_1Eth, TestItem.DepositB_2Eth, TestItem.WithdrawalRequestA, - TestItem.WithdrawalRequestB + TestItem.WithdrawalRequestB, + TestItem.ConsolidationRequestA, + TestItem.ConsolidationRequestB ]; public void ProcessRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs index af91c25f367..2de5457185a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs @@ -119,8 +119,15 @@ private static ExecutionPayload CreateBlockRequest(MergeTestBlockchain chain, Ex return blockRequest; } - private static ExecutionPayloadV3 CreateBlockRequestV3(MergeTestBlockchain chain, ExecutionPayload parent, Address miner, Withdrawal[]? withdrawals = null, - ulong? blobGasUsed = null, ulong? excessBlobGas = null, Transaction[]? transactions = null, Hash256? parentBeaconBlockRoot = null) + private static ExecutionPayloadV3 CreateBlockRequestV3( + MergeTestBlockchain chain, + ExecutionPayload parent, + Address miner, + Withdrawal[]? withdrawals = null, + ulong? blobGasUsed = null, + ulong? excessBlobGas = null, + Transaction[]? transactions = null, + Hash256? parentBeaconBlockRoot = null) { ExecutionPayloadV3 blockRequestV3 = CreateBlockRequestInternal(parent, miner, withdrawals, blobGasUsed, excessBlobGas, transactions: transactions, parentBeaconBlockRoot: parentBeaconBlockRoot); blockRequestV3.TryGetBlock(out Block? block); @@ -170,9 +177,11 @@ private static T CreateBlockRequestInternal(ExecutionPayload parent, Address { Deposit[]? deposits = null; WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + if (requests is not null) { - (deposits, withdrawalRequests) = requests.SplitRequests(); + (deposits, withdrawalRequests, consolidationRequests) = requests.SplitRequests(); } T blockRequest = new() @@ -191,7 +200,8 @@ private static T CreateBlockRequestInternal(ExecutionPayload parent, Address ExcessBlobGas = excessBlobGas, ParentBeaconBlockRoot = parentBeaconBlockRoot, DepositRequests = deposits, - WithdrawalRequests = withdrawalRequests + WithdrawalRequests = withdrawalRequests, + ConsolidationRequests = consolidationRequests, }; blockRequest.SetTransactions(transactions ?? Array.Empty()); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs index 3c3874b2f8f..7918db73e3c 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs @@ -167,6 +167,42 @@ public virtual async Task Should_process_block_as_expected_V4(string latestValid })); } + + [Test] + public async Task NewPayloadV4_reject_payload_with_bad_authorization_list_rlp() + { + ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); + using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance, null, null, null, consensusRequestsProcessorMock); + IEngineRpcModule rpc = CreateEngineModule(chain); + Hash256 lastHash = (await ProduceBranchV4(rpc, chain, 10, CreateParentBlockRequestOnHead(chain.BlockTree), true)) + .LastOrDefault()?.BlockHash ?? Keccak.Zero; + + Transaction invalidSetCodeTx = Build.A.Transaction + .WithType(TxType.SetCode) + .WithNonce(0) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(100_000) + .WithAuthorizationCode(new JsonRpc.Test.Modules.Eth.EthRpcModuleTests.AllowNullAuthorizationTuple(0, null, 0, new Signature(new byte[65]))) + .WithTo(TestItem.AddressA) + .SignedAndResolved(TestItem.PrivateKeyB).TestObject; + + Block invalidBlock = Build.A.Block + .WithNumber(chain.BlockTree.Head!.Number + 1) + .WithTimestamp(chain.BlockTree.Head!.Timestamp + 12) + .WithTransactions([invalidSetCodeTx]) + .WithParentBeaconBlockRoot(chain.BlockTree.Head!.ParentBeaconBlockRoot) + .WithBlobGasUsed(0) + .WithExcessBlobGas(0) + .TestObject; + + ExecutionPayloadV4 executionPayload = ExecutionPayloadV4.Create(invalidBlock); + + var response = await rpc.engine_newPayloadV4(executionPayload, [], invalidBlock.ParentBeaconBlockRoot); + + Assert.That(response.Data.Status, Is.EqualTo("INVALID")); + } + [TestCase(30)] public async Task can_progress_chain_one_by_one_v4(int count) { @@ -319,9 +355,11 @@ public virtual async Task { Deposit[]? deposits = null; WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + if (requests is not null) { - (deposits, withdrawalRequests) = requests.SplitRequests(); + (deposits, withdrawalRequests, consolidationRequests) = requests.SplitRequests(); } ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); using MergeTestBlockchain chain = await CreateBlockchain(Prague.Instance, null, null, null, consensusRequestsProcessorMock); @@ -332,9 +370,9 @@ public virtual async Task IEnumerable payloadBodies = rpc.engine_getPayloadBodiesByHashV2(blockHashes).Data; ExecutionPayloadBodyV2Result?[] expected = { - new (Array.Empty(), Array.Empty() , deposits, withdrawalRequests), + new (Array.Empty(), Array.Empty() , deposits, withdrawalRequests, consolidationRequests), null, - new (Array.Empty(), Array.Empty(), deposits, withdrawalRequests), + new (Array.Empty(), Array.Empty(), deposits, withdrawalRequests,consolidationRequests), }; payloadBodies.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering()); } @@ -346,9 +384,11 @@ public virtual async Task { Deposit[]? deposits = null; WithdrawalRequest[]? withdrawalRequests = null; + ConsolidationRequest[]? consolidationRequests = null; + if (requests is not null) { - (deposits, withdrawalRequests) = requests.SplitRequests(); + (deposits, withdrawalRequests, consolidationRequests) = requests.SplitRequests(); } ConsensusRequestsProcessorMock consensusRequestsProcessorMock = new(); @@ -363,7 +403,7 @@ await rpc.engine_forkchoiceUpdatedV3(new ForkchoiceStateV1(executionPayload2.Blo rpc.engine_getPayloadBodiesByRangeV2(1, 3).Result.Data; ExecutionPayloadBodyV2Result?[] expected = { - new (Array.Empty(), Array.Empty() , deposits, withdrawalRequests), + new (Array.Empty(), Array.Empty() , deposits, withdrawalRequests, consolidationRequests), }; payloadBodies.Should().BeEquivalentTo(expected, o => o.WithStrictOrdering()); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 771759edb6c..e95369b7d91 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -89,6 +89,12 @@ public byte[][] Transactions /// 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 @@ -226,7 +232,7 @@ public ValidationResult ValidateParams(IReleaseSpec spec, int version, out strin return ValidationResult.Fail; } - if (spec.ConsensusRequestsEnabled) + if (spec.RequestsEnabled) { error = "ExecutionPayloadV4 expected"; return ValidationResult.Fail; @@ -246,7 +252,7 @@ public ValidationResult ValidateParams(IReleaseSpec spec, int version, out strin protected virtual int GetExecutionPayloadVersion() => this switch { - { DepositRequests: not null, WithdrawalRequests: not null } => 4, + { DepositRequests: not null, WithdrawalRequests: not null, ConsolidationRequests: not null } => 4, { BlobGasUsed: not null } or { ExcessBlobGas: not null } or { ParentBeaconBlockRoot: not null } => 3, { Withdrawals: not null } => 2, _ => 1 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 index 508f900fc55..d929fcf6fd8 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadBodyV2Result.cs @@ -11,26 +11,34 @@ namespace Nethermind.Merge.Plugin.Data; public class ExecutionPayloadBodyV2Result : ExecutionPayloadBodyV1Result { public ExecutionPayloadBodyV2Result( - IList transactions, - IList? withdrawals, - IList? deposits, - IList? withdrawalsRequests + 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 IList? DepositRequests { get; set; } + public IReadOnlyList? DepositRequests { get; set; } /// /// Withdrawal requests . /// [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public IList? WithdrawalRequests { get; set; } + 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/ExecutionPayloadV4.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs index cef9b04c3fb..e0d65da1215 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV4.cs @@ -25,7 +25,7 @@ public class ExecutionPayloadV4 : ExecutionPayloadV3, IExecutionPayloadFactory(block); ConsensusRequest[]? blockRequests = block.Requests; - (executionPayload.DepositRequests, executionPayload.WithdrawalRequests) = blockRequests?.SplitRequests() ?? ([], []); + (executionPayload.DepositRequests, executionPayload.WithdrawalRequests, executionPayload.ConsolidationRequests) = blockRequests?.SplitRequests() ?? ([], [], []); return executionPayload; } @@ -40,7 +40,8 @@ public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? var depositsLength = DepositRequests?.Length ?? 0; var withdrawalRequestsLength = WithdrawalRequests?.Length ?? 0; - var requestsCount = depositsLength + withdrawalRequestsLength; + var consolidationRequestsLength = ConsolidationRequests?.Length ?? 0; + var requestsCount = depositsLength + withdrawalRequestsLength + consolidationRequestsLength; if (requestsCount > 0) { var requests = new ConsensusRequest[requestsCount]; @@ -50,11 +51,16 @@ public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? requests[i] = DepositRequests![i]; } - for (; i < requestsCount; ++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; } @@ -69,7 +75,8 @@ public override bool TryGetBlock([NotNullWhen(true)] out Block? block, UInt256? public override bool ValidateFork(ISpecProvider specProvider) => specProvider.GetSpec(BlockNumber, Timestamp).DepositsEnabled - && specProvider.GetSpec(BlockNumber, Timestamp).WithdrawalRequestsEnabled; + && specProvider.GetSpec(BlockNumber, Timestamp).WithdrawalRequestsEnabled + && specProvider.GetSpec(BlockNumber, Timestamp).ConsolidationRequestsEnabled; /// /// Gets or sets as defined in @@ -84,4 +91,11 @@ public override bool ValidateFork(ISpecProvider specProvider) => /// [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/EngineRpcModule.Paris.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs index f5c0616b826..4cf84584a76 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs @@ -41,15 +41,14 @@ protected async Task> ForkchoiceUpdated { if (await _locker.WaitAsync(_timeout)) { - Stopwatch watch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); try { return await _forkchoiceUpdatedV1Handler.Handle(forkchoiceState, payloadAttributes, version); } finally { - watch.Stop(); - Metrics.ForkchoiceUpdedExecutionTime = watch.ElapsedMilliseconds; + Metrics.ForkchoiceUpdedExecutionTime = (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds; _locker.Release(); } } @@ -82,7 +81,7 @@ protected async Task> NewPayload(IExecutionPayloa if (await _locker.WaitAsync(_timeout)) { - Stopwatch watch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); try { using IDisposable region = _gcKeeper.TryStartNoGCRegion(); @@ -95,8 +94,7 @@ protected async Task> NewPayload(IExecutionPayloa } finally { - watch.Stop(); - Metrics.NewPayloadExecutionTime = watch.ElapsedMilliseconds; + Metrics.NewPayloadExecutionTime = (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds; _locker.Release(); } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/GC/GCKeeper.cs b/src/Nethermind/Nethermind.Merge.Plugin/GC/GCKeeper.cs index e2d3a48957d..e1bbd1eb58c 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/GC/GCKeeper.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/GC/GCKeeper.cs @@ -8,7 +8,6 @@ using FastEnumUtility; using Nethermind.Consensus; using Nethermind.Core.Extensions; -using Nethermind.Core.Memory; using Nethermind.Logging; namespace Nethermind.Merge.Plugin.GC; @@ -150,8 +149,8 @@ private async Task ScheduleGCInternal() { // This should give time to finalize response in Engine API // Normally we should get block every 12s (5s on some chains) - // Lets say we process block in 2s, then delay 500ms, then invoke GC - await Task.Delay(500); + // Lets say we process block in 2s, then delay 125ms, then invoke GC + await Task.Delay(125); if (GCSettings.LatencyMode != GCLatencyMode.NoGCRegion) { diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs index e69773391e4..57d1ab5f5bd 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/EngineRpcCapabilitiesProvider.cs @@ -49,10 +49,17 @@ public EngineRpcCapabilitiesProvider(ISpecProvider specProvider) #endregion #region Prague - _capabilities[nameof(IEngineRpcModule.engine_getPayloadV4)] = (spec.ConsensusRequestsEnabled, spec.ConsensusRequestsEnabled); - _capabilities[nameof(IEngineRpcModule.engine_newPayloadV4)] = (spec.ConsensusRequestsEnabled, spec.ConsensusRequestsEnabled); - _capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByHashV2)] = (spec.ConsensusRequestsEnabled, false); - _capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByRangeV2)] = (spec.ConsensusRequestsEnabled, false); + _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 + + #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 } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs index ff64739b948..0c95bbd88b0 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByHashV2Handler.cs @@ -31,8 +31,8 @@ public class GetPayloadBodiesByHashV2Handler(IBlockTree blockTree, ILogManager l for (int i = 0; i < blockHashes.Count; i++) { Block? block = _blockTree.FindBlock(blockHashes[i]); - (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests) = block?.Requests?.SplitRequests() ?? (null, null); - yield return block is null ? null : new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests); + (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) = block?.Requests?.SplitRequests() ?? (null, null, null); + yield return block is null ? null : new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests, consolidationRequests); } } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs index 4f97a0b800a..f1a3107457d 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadBodiesByRangeV2Handler.cs @@ -42,8 +42,8 @@ public class GetPayloadBodiesByRangeV2Handler(IBlockTree blockTree, ILogManager continue; } - (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests) = block!.Requests?.SplitRequests() ?? (null, null); - yield return new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests); + (Deposit[]? deposits, WithdrawalRequest[]? withdrawalRequests, ConsolidationRequest[]? consolidationRequests) = block!.Requests?.SplitRequests() ?? (null, null, null); + yield return new ExecutionPayloadBodyV2Result(block.Transactions, block.Withdrawals, deposits, withdrawalRequests, consolidationRequests); } yield break; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs index 1091473ca26..f0768e39168 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Synchronization/MergeBlockDownloader.cs @@ -179,7 +179,7 @@ bool HasMoreToSync(out BlockHeader[]? headers, out int headersToRequest) if (cancellation.IsCancellationRequested) return blocksSynced; // check before every heavy operation - Stopwatch sw = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); await RequestBodies(bestPeer, cancellation, context); if (downloadReceipts) @@ -189,7 +189,7 @@ bool HasMoreToSync(out BlockHeader[]? headers, out int headersToRequest) await RequestReceipts(bestPeer, cancellation, context); } - AdjustSyncBatchSize(sw.Elapsed); + AdjustSyncBatchSize(Stopwatch.GetElapsedTime(startTime)); blocks = context.Blocks; receipts = context.ReceiptsForBlocks; diff --git a/src/Nethermind/Nethermind.Mev.Test/MevMegabundleTests.cs b/src/Nethermind/Nethermind.Mev.Test/MevMegabundleTests.cs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Nethermind/Nethermind.Mev/MevPlugin.cs b/src/Nethermind/Nethermind.Mev/MevPlugin.cs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs b/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/Nethermind/Nethermind.Network.Benchmark/Eth62ProtocolHandlerBenchmarks.cs b/src/Nethermind/Nethermind.Network.Benchmark/Eth62ProtocolHandlerBenchmarks.cs index 5700d30de35..538ae8f1ff6 100644 --- a/src/Nethermind/Nethermind.Network.Benchmark/Eth62ProtocolHandlerBenchmarks.cs +++ b/src/Nethermind/Nethermind.Network.Benchmark/Eth62ProtocolHandlerBenchmarks.cs @@ -16,6 +16,7 @@ using Nethermind.Core.Timers; using Nethermind.Crypto; using Nethermind.Db; +using Nethermind.Evm; using Nethermind.Logging; using Nethermind.Network.P2P; using Nethermind.Network.P2P.Analyzers; @@ -58,7 +59,7 @@ public void SetUp() TxPool.TxPool txPool = new TxPool.TxPool( ecdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(MainnetSpecProvider.Instance), tree, stateProvider), + new ChainHeadInfoProvider(new FixedForkActivationChainHeadSpecProvider(MainnetSpecProvider.Instance), tree, stateProvider, new CodeInfoRepository()), new TxPoolConfig(), new TxValidator(TestBlockchainIds.ChainId), LimboLogs.Instance, diff --git a/src/Nethermind/Nethermind.Network.Discovery.Test/DiscoveryManagerTests.cs b/src/Nethermind/Nethermind.Network.Discovery.Test/DiscoveryManagerTests.cs index f6d36407232..7921d946c1c 100644 --- a/src/Nethermind/Nethermind.Network.Discovery.Test/DiscoveryManagerTests.cs +++ b/src/Nethermind/Nethermind.Network.Discovery.Test/DiscoveryManagerTests.cs @@ -200,7 +200,7 @@ public async Task RateLimitOutgoingMessage() MaxOutgoingMessagePerSecond = 5 }); - Stopwatch sw = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); FindNodeMsg msg = new(_publicKey, 0, Array.Empty()); await _discoveryManager.SendMessageAsync(msg); await _discoveryManager.SendMessageAsync(msg); @@ -208,7 +208,7 @@ public async Task RateLimitOutgoingMessage() await _discoveryManager.SendMessageAsync(msg); await _discoveryManager.SendMessageAsync(msg); await _discoveryManager.SendMessageAsync(msg); - sw.Elapsed.Should().BeGreaterOrEqualTo(TimeSpan.FromSeconds(0.9)); + Stopwatch.GetElapsedTime(startTime).Should().BeGreaterOrEqualTo(TimeSpan.FromSeconds(0.9)); } } } diff --git a/src/Nethermind/Nethermind.Network.Dns.Test/EnrDiscoveryTests.cs b/src/Nethermind/Nethermind.Network.Dns.Test/EnrDiscoveryTests.cs index fb20bb43b14..0c5dc4143ed 100644 --- a/src/Nethermind/Nethermind.Network.Dns.Test/EnrDiscoveryTests.cs +++ b/src/Nethermind/Nethermind.Network.Dns.Test/EnrDiscoveryTests.cs @@ -33,10 +33,10 @@ public async Task Test_enr_discovery(string url) INetworkConfig config = new NetworkConfig(); config.DiscoveryDns = url; EnrDiscovery enrDiscovery = new(new EnrRecordParser(singer), config, testErrorLogManager); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); List addedRecords = enrDiscovery.DiscoverNodes(default).ToBlockingEnumerable().ToList(); - await TestContext.Out.WriteLineAsync($"Actually added {addedRecords.Count} in {stopwatch.Elapsed:g}"); + await TestContext.Out.WriteLineAsync($"Actually added {addedRecords.Count} in {Stopwatch.GetElapsedTime(startTime):g}"); foreach (TestErrorLogManager.Error error in testErrorLogManager.Errors) { await TestContext.Out.WriteLineAsync(error.Text); diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/PacketSenderTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/PacketSenderTests.cs index 785f9ef49bc..7d7aa9c3165 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/PacketSenderTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/PacketSenderTests.cs @@ -38,7 +38,6 @@ public void Does_send_on_active_channel() PacketSender packetSender = new(serializer, LimboLogs.Instance, TimeSpan.Zero); packetSender.HandlerAdded(context); packetSender.Enqueue(testMessage); - testMessage.WasDisposed.Should().BeTrue(); context.Received(1).WriteAndFlushAsync(Arg.Any()); } @@ -62,7 +61,6 @@ public void Does_not_try_to_send_on_inactive_channel() PacketSender packetSender = new(serializer, LimboLogs.Instance, TimeSpan.Zero); packetSender.HandlerAdded(context); packetSender.Enqueue(testMessage); - testMessage.WasDisposed.Should().BeTrue(); context.Received(0).WriteAndFlushAsync(Arg.Any()); } @@ -88,7 +86,6 @@ public async Task Send_after_delay_if_specified() PacketSender packetSender = new(serializer, LimboLogs.Instance, delay); packetSender.HandlerAdded(context); packetSender.Enqueue(testMessage); - testMessage.WasDisposed.Should().BeTrue(); await context.Received(0).WriteAndFlushAsync(Arg.Any()); @@ -101,13 +98,6 @@ private class TestMessage : P2PMessage { public override int PacketType { get; } = 0; public override string Protocol { get; } = ""; - - public bool WasDisposed { get; set; } - public override void Dispose() - { - base.Dispose(); - WasDisposed = true; - } } } } diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/SessionTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/SessionTests.cs index 95108ed1533..a9078b29656 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/SessionTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/SessionTests.cs @@ -15,7 +15,6 @@ using Nethermind.Network.Rlpx; using Nethermind.Stats.Model; using NSubstitute; -using NSubstitute.ReceivedExtensions; using NUnit.Framework; namespace Nethermind.Network.Test.P2P; @@ -452,9 +451,11 @@ public void Can_deliver_messages() session.AddProtocolHandler(bbb); session.AddProtocolHandler(ccc); - _packetSender.Enqueue(PingMessage.Instance).Returns(10); - session.DeliverMessage(PingMessage.Instance); - _packetSender.Received().Enqueue(PingMessage.Instance); + var message = new TestMessage(); + _packetSender.Enqueue(message).Returns(10); + session.DeliverMessage(message); + _packetSender.Received().Enqueue(message); + message.WasDisposed.Should().BeTrue(); Metrics.P2PBytesSent.Should().Be(10); } @@ -462,10 +463,12 @@ public void Can_deliver_messages() [Test] public void Cannot_deliver_before_initialized() { + var message = new TestMessage(); Session session = new(30312, new Node(TestItem.PublicKeyA, "127.0.0.1", 8545), _channel, NullDisconnectsAnalyzer.Instance, LimboLogs.Instance); - Assert.Throws(() => session.DeliverMessage(PingMessage.Instance)); + Assert.Throws(() => session.DeliverMessage(message)); session.Handshake(TestItem.PublicKeyA); - Assert.Throws(() => session.DeliverMessage(PingMessage.Instance)); + Assert.Throws(() => session.DeliverMessage(message)); + message.WasDisposed.Should().BeTrue(); session.Init(5, _channelHandlerContext, _packetSender); IProtocolHandler p2p = BuildHandler("p2p", 10); session.AddProtocolHandler(p2p); @@ -494,8 +497,10 @@ public void Stops_delivering_messages_after_disconnect() session.InitiateDisconnect(DisconnectReason.Other); - session.DeliverMessage(PingMessage.Instance); - _packetSender.DidNotReceive().Enqueue(Arg.Any()); + var message = new TestMessage(); + session.DeliverMessage(message); + _packetSender.DidNotReceive().Enqueue(Arg.Any()); + message.WasDisposed.Should().BeTrue(); } [Test] @@ -538,10 +543,11 @@ public void Protocol_handler_can_send_message_on_disconnect() IProtocolHandler p2p = BuildHandler("p2p", 10); session.AddProtocolHandler(p2p); + var message = new TestMessage(); p2p.When(it => it.DisconnectProtocol(Arg.Any(), Arg.Any())) .Do((_) => { - session.DeliverMessage(PingMessage.Instance); + session.DeliverMessage(message); }); session.Init(5, _channelHandlerContext, _packetSender); @@ -549,7 +555,9 @@ public void Protocol_handler_can_send_message_on_disconnect() _packetSender .Received() - .Enqueue(PingMessage.Instance); + .Enqueue(message); + + message.WasDisposed.Should().BeTrue(); } [Test, Retry(3)] @@ -619,4 +627,17 @@ public void Updates_local_and_remote_metrics_on_disconnects() Assert.That(afterLocal, Is.EqualTo(beforeLocal)); Assert.That(afterRemote, Is.EqualTo(beforeRemote + 1)); } + + private class TestMessage : P2PMessage + { + public override int PacketType => P2PMessageCode.Ping; + public override string Protocol => "p2p"; + + public bool WasDisposed { get; set; } + public override void Dispose() + { + base.Dispose(); + WasDisposed = true; + } + } } diff --git a/src/Nethermind/Nethermind.Network/CompositeNodeSource.cs b/src/Nethermind/Nethermind.Network/CompositeNodeSource.cs index a56329f4731..1402ebcf725 100644 --- a/src/Nethermind/Nethermind.Network/CompositeNodeSource.cs +++ b/src/Nethermind/Nethermind.Network/CompositeNodeSource.cs @@ -20,7 +20,8 @@ public async IAsyncEnumerable DiscoverNodes([EnumeratorCancellation] Cance { Channel ch = Channel.CreateBounded(1); - Task[] feedTasks = _nodeSources.Select(async (innerSource) => + // TODO: .Net 9 stackalloc + Task[] feedTasks = _nodeSources.Select(async innerSource => { await foreach (Node node in innerSource.DiscoverNodes(cancellationToken)) { diff --git a/src/Nethermind/Nethermind.Network/P2P/PacketSender.cs b/src/Nethermind/Nethermind.Network/P2P/PacketSender.cs index 2201fb73fd2..3248eb98115 100644 --- a/src/Nethermind/Nethermind.Network/P2P/PacketSender.cs +++ b/src/Nethermind/Nethermind.Network/P2P/PacketSender.cs @@ -27,20 +27,12 @@ public PacketSender(IMessageSerializationService messageSerializationService, IL public int Enqueue(T message) where T : P2PMessage { - IByteBuffer buffer; - try + if (!_context.Channel.IsWritable || !_context.Channel.Active) { - if (!_context.Channel.IsWritable || !_context.Channel.Active) - { - return 0; - } - - buffer = _messageSerializationService.ZeroSerialize(message); - } - finally - { - message.Dispose(); + return 0; } + + IByteBuffer buffer = _messageSerializationService.ZeroSerialize(message); int length = buffer.ReadableBytes; // Running in background diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/P2PProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/P2PProtocolHandler.cs index e5a3db6b9d6..b0c3c5d1615 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/P2PProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/P2PProtocolHandler.cs @@ -271,7 +271,7 @@ public async Task SendPing() if (Logger.IsTrace) Logger.Trace($"{Session} P2P sending ping on {Session.RemotePort} ({RemoteClientId})"); Send(PingMessage.Instance); _nodeStatsManager.ReportEvent(Session.Node, NodeStatsEventType.P2PPingOut); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); CancellationTokenSource delayCancellation = new(); try @@ -286,7 +286,7 @@ public async Task SendPing() return false; } - long latency = stopwatch.ElapsedMilliseconds; + long latency = (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds; _nodeStatsManager.ReportTransferSpeedEvent(Session.Node, TransferSpeedType.Latency, latency); return true; } diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs index b47db24a5ac..e930beb17dc 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs @@ -283,7 +283,7 @@ private void SendMessage(IOwnedReadOnlyList txsToSend) protected async Task Handle(GetBlockHeadersMessage getBlockHeadersMessage, CancellationToken cancellationToken) { using GetBlockHeadersMessage message = getBlockHeadersMessage; - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); if (Logger.IsTrace) { Logger.Trace($"Received headers request from {Session.Node:c}:"); @@ -309,8 +309,7 @@ protected async Task Handle(GetBlockHeadersMessage getBlock // } BlockHeadersMessage resp = await FulfillBlockHeadersRequest(message, cancellationToken); - stopwatch.Stop(); - if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} BlockHeaders to {Node:c} in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} BlockHeaders to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); return resp; } @@ -343,12 +342,11 @@ protected async Task Handle(GetBlockBodiesMessage request, C Logger.Trace($"Received bodies request of length {message.BlockHashes.Count} from {Session.Node:c}:"); } - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); Interlocked.Increment(ref Counter); BlockBodiesMessage resp = await FulfillBlockBodiesRequest(message, cancellationToken); - stopwatch.Stop(); - if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} BlockBodies to {Node:c} in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} BlockBodies to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); return resp; } @@ -391,10 +389,9 @@ protected async Task Handle(GetReceiptsMessage msg, Cancellatio throw new EthSyncException("Incoming receipts request for more than 512 blocks"); } - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); ReceiptsMessage resp = await FulfillReceiptsRequest(message, cancellationToken); - stopwatch.Stop(); - if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} Receipts to {Node:c} in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} Receipts to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); return resp; } diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ZeroProtocolHandlerBase.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ZeroProtocolHandlerBase.cs index 8bf24cc1ac3..9a4e07a900e 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ZeroProtocolHandlerBase.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/ZeroProtocolHandlerBase.cs @@ -52,29 +52,39 @@ CancellationToken token ) { Task task = request.CompletionSource.Task; - using CancellationTokenSource delayCancellation = new(); - using CancellationTokenSource compositeCancellation = - CancellationTokenSource.CreateLinkedTokenSource(token, delayCancellation.Token); - Task firstTask = await Task.WhenAny(task, Task.Delay(Timeouts.Eth, compositeCancellation.Token)); - if (firstTask.IsCanceled) + bool success = false; + try { - token.ThrowIfCancellationRequested(); - } + using CancellationTokenSource delayCancellation = new(); + using CancellationTokenSource compositeCancellation = CancellationTokenSource.CreateLinkedTokenSource(token, delayCancellation.Token); + Task firstTask = await Task.WhenAny(task, Task.Delay(Timeouts.Eth, compositeCancellation.Token)); + if (firstTask.IsCanceled) + { + token.ThrowIfCancellationRequested(); + } - if (firstTask == task) - { - delayCancellation.Cancel(); - long elapsed = request.FinishMeasuringTime(); - long bytesPerMillisecond = (long)((decimal)request.ResponseSize / Math.Max(1, elapsed)); - if (Logger.IsTrace) Logger.Trace($"{this} speed is {request.ResponseSize}/{elapsed} = {bytesPerMillisecond}"); - StatsManager.ReportTransferSpeedEvent(Session.Node, speedType, bytesPerMillisecond); + if (firstTask == task) + { + await delayCancellation.CancelAsync(); + long elapsed = request.FinishMeasuringTime(); + long bytesPerMillisecond = (long)((decimal)request.ResponseSize / Math.Max(1, elapsed)); + if (Logger.IsTrace) Logger.Trace($"{this} speed is {request.ResponseSize}/{elapsed} = {bytesPerMillisecond}"); + StatsManager.ReportTransferSpeedEvent(Session.Node, speedType, bytesPerMillisecond); - return await task; - } + success = true; + return await task; + } - CleanupTimeoutTask(task); - StatsManager.ReportTransferSpeedEvent(Session.Node, speedType, 0L); - throw new TimeoutException($"{Session} Request timeout in {describeRequestFunc(request.Message)}"); + StatsManager.ReportTransferSpeedEvent(Session.Node, speedType, 0L); + throw new TimeoutException($"{Session} Request timeout in {describeRequestFunc(request.Message)}"); + } + finally + { + if (!success) + { + CleanupTimeoutTask(task); + } + } } private static void CleanupTimeoutTask(Task task) diff --git a/src/Nethermind/Nethermind.Network/P2P/Session.cs b/src/Nethermind/Nethermind.Network/P2P/Session.cs index f092fd41ce2..2efaa613de7 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Session.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Session.cs @@ -207,31 +207,38 @@ public void ReceiveMessage(ZeroPacket zeroPacket) public int DeliverMessage(T message) where T : P2PMessage { - lock (_sessionStateLock) + try { - if (State < SessionState.Initialized) + lock (_sessionStateLock) { - throw new InvalidOperationException($"{nameof(DeliverMessage)} called {this}"); - } + if (State < SessionState.Initialized) + { + throw new InvalidOperationException($"{nameof(DeliverMessage)} called {this}"); + } - // Must allow sending out packet when `DisconnectingProtocols` so that we can send out disconnect reason - // and hello (part of protocol) - if (IsClosed) - { - return 1; + // Must allow sending out packet when `DisconnectingProtocols` so that we can send out disconnect reason + // and hello (part of protocol) + if (IsClosed) + { + return 1; + } } - } - if (_logger.IsTrace) _logger.Trace($"P2P to deliver {message.Protocol}.{message.PacketType} on {this}"); + if (_logger.IsTrace) _logger.Trace($"P2P to deliver {message.Protocol}.{message.PacketType} on {this}"); - message.AdaptivePacketType = _resolver.ResolveAdaptiveId(message.Protocol, message.PacketType); - int size = _packetSender.Enqueue(message); + message.AdaptivePacketType = _resolver.ResolveAdaptiveId(message.Protocol, message.PacketType); + int size = _packetSender.Enqueue(message); - RecordOutgoingMessageMetric(message, size); + RecordOutgoingMessageMetric(message, size); - Interlocked.Add(ref Metrics.P2PBytesSent, size); + Interlocked.Add(ref Metrics.P2PBytesSent, size); - return size; + return size; + } + finally + { + message.Dispose(); + } } public void ReceiveMessage(Packet packet) 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 65fd4af43d8..c037fe6b16e 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 @@ -59,7 +59,7 @@ private class BlockBodyDecoder : IRlpValueDecoder private readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly HeaderDecoder _headerDecoder = new(); private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); - private readonly ConsensusRequestDecoder _requestsDecoder = new(); + private readonly ConsensusRequestDecoder _requestsDecoder = ConsensusRequestDecoder.Instance; public int GetLength(BlockBody item, RlpBehaviors rlpBehaviors) { diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V63/Eth63ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V63/Eth63ProtocolHandler.cs index be1d225effb..a3f07a11fa4 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V63/Eth63ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V63/Eth63ProtocolHandler.cs @@ -106,10 +106,9 @@ private async Task Handle(GetNodeDataMessage msg, CancellationT { using var message = msg; - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); NodeDataMessage response = await FulfillNodeDataRequest(message, cancellationToken); - stopwatch.Stop(); - if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} NodeData to {Node:c} in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} NodeData to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); return response; } diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs index a15a71193f5..b322f6905ed 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V65/Eth65ProtocolHandler.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; @@ -97,15 +98,14 @@ protected virtual void Handle(NewPooledTransactionHashesMessage msg) using var _ = msg; AddNotifiedTransactions(msg.Hashes); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); TxPool.Metrics.PendingTransactionsHashesReceived += msg.Hashes.Count; _pooledTxsRequestor.RequestTransactions(Send, msg.Hashes); - stopwatch.Stop(); if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage)} to {Node:c} " + - $"in {stopwatch.Elapsed.TotalMilliseconds}ms"); + $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); } protected void AddNotifiedTransactions(IReadOnlyList hashes) @@ -119,12 +119,11 @@ protected void AddNotifiedTransactions(IReadOnlyList hashes) private async ValueTask Handle(GetPooledTransactionsMessage msg, CancellationToken cancellationToken) { using var message = msg; - var stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); Send(await FulfillPooledTransactionsRequest(message, cancellationToken)); - stopwatch.Stop(); if (Logger.IsTrace) Logger.Trace($"OUT {Counter:D5} {nameof(GetPooledTransactionsMessage)} to {Node:c} " + - $"in {stopwatch.Elapsed.TotalMilliseconds}ms"); + $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); } internal Task FulfillPooledTransactionsRequest(GetPooledTransactionsMessage msg, CancellationToken cancellationToken) diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs index 95595b3b5c0..265feceb2c0 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V66/Eth66ProtocolHandler.cs @@ -194,15 +194,14 @@ protected override void Handle(NewPooledTransactionHashesMessage msg) { using var message = msg; bool isTrace = Logger.IsTrace; - Stopwatch? stopwatch = isTrace ? Stopwatch.StartNew() : null; + long startTime = Stopwatch.GetTimestamp(); TxPool.Metrics.PendingTransactionsHashesReceived += message.Hashes.Count; _pooledTxsRequestor.RequestTransactionsEth66(_sendAction, message.Hashes); - stopwatch?.Stop(); if (isTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage)} to {Node:c} " + - $"in {stopwatch.Elapsed.TotalMilliseconds}ms"); + $"in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"); } protected override async Task> SendRequest(V62.Messages.GetBlockHeadersMessage message, CancellationToken token) diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs index 15c0ace7a19..4dae6ba58b9 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandler.cs @@ -94,13 +94,11 @@ private void Handle(NewPooledTransactionHashesMessage68 msg) AddNotifiedTransactions(message.Hashes); - Stopwatch? stopwatch = isTrace ? Stopwatch.StartNew() : null; + long startTime = isTrace ? Stopwatch.GetTimestamp() : 0; _pooledTxsRequestor.RequestTransactionsEth68(_sendAction, message.Hashes, message.Sizes, message.Types); - stopwatch?.Stop(); - - if (isTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage68)} to {Node:c} in {stopwatch.Elapsed.TotalMilliseconds}ms"); + if (isTrace) Logger.Trace($"OUT {Counter:D5} {nameof(NewPooledTransactionHashesMessage68)} to {Node:c} in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds}ms"); } protected override void SendNewTransactionCore(Transaction tx) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEthereumEcdsa.cs b/src/Nethermind/Nethermind.Optimism/OptimismEthereumEcdsa.cs index f70db353bf7..16c2e6b8cf8 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismEthereumEcdsa.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEthereumEcdsa.cs @@ -12,25 +12,13 @@ public class OptimismEthereumEcdsa : Ecdsa, IEthereumEcdsa { private readonly IEthereumEcdsa _ethereumEcdsa; + public ulong ChainId => _ethereumEcdsa.ChainId; + public OptimismEthereumEcdsa(IEthereumEcdsa ethereumEcdsa) { _ethereumEcdsa = ethereumEcdsa; } - - public void Sign(PrivateKey privateKey, Transaction tx, bool isEip155Enabled = true) => _ethereumEcdsa.Sign(privateKey, tx, isEip155Enabled); - - public Address? RecoverAddress(Transaction tx, bool useSignatureChainId = false) - { - if (tx.Signature is null && tx.IsOPSystemTransaction) - { - return Address.Zero; - } - return _ethereumEcdsa.RecoverAddress(tx, useSignatureChainId); - } - public Address? RecoverAddress(Signature signature, Hash256 message) => _ethereumEcdsa.RecoverAddress(signature, message); public Address? RecoverAddress(Span signatureBytes, Hash256 message) => _ethereumEcdsa.RecoverAddress(signatureBytes, message); - - public bool Verify(Address sender, Transaction tx) => _ethereumEcdsa.Verify(sender, tx); } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs b/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs index 481c655d757..58dab4bab85 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismTransactionProcessor.cs @@ -153,7 +153,7 @@ protected override void PayFees(Transaction tx, BlockHeader header, IReleaseSpec } protected override long Refund(Transaction tx, BlockHeader header, IReleaseSpec spec, ExecutionOptions opts, - in TransactionSubstate substate, in long unspentGas, in UInt256 gasPrice) + in TransactionSubstate substate, in long unspentGas, in UInt256 gasPrice, int refunds) { // if deposit: skip refunds, skip tipping coinbase // Regolith changes this behaviour to report the actual gasUsed instead of always reporting all gas used. @@ -164,6 +164,6 @@ protected override long Refund(Transaction tx, BlockHeader header, IReleaseSpec return tx.IsOPSystemTransaction ? 0 : tx.GasLimit; } - return base.Refund(tx, header, spec, opts, substate, unspentGas, gasPrice); + return base.Refund(tx, header, spec, opts, substate, unspentGas, gasPrice, refunds); } } diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1AddBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1AddBenchmark.cs new file mode 100644 index 00000000000..26733025c3d --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1AddBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG1AddBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G1AddPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg1add"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MSMBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MSMBenchmark.cs new file mode 100644 index 00000000000..ebd52dddb05 --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MSMBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG1MSMBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G1MSMPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg1msm"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MulBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MulBenchmark.cs new file mode 100644 index 00000000000..7d1adc95338 --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG1MulBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG1MulBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G1MulPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg1mul"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2AddBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2AddBenchmark.cs new file mode 100644 index 00000000000..d89edf3e771 --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2AddBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG2AddBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G2AddPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg2add"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MSMBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MSMBenchmark.cs new file mode 100644 index 00000000000..59efd92b36f --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MSMBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG2MSMBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G2MSMPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg2msm"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MulBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MulBenchmark.cs new file mode 100644 index 00000000000..6df853a9778 --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsG2MulBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsG2MulBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + G2MulPrecompile.Instance + }; + + protected override string InputsDirectory => "blsg2mul"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFp2ToG2Benchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFp2ToG2Benchmark.cs new file mode 100644 index 00000000000..ff903b6e1e7 --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFp2ToG2Benchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsMapFp2ToG2Benchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + MapFp2ToG2Precompile.Instance + }; + + protected override string InputsDirectory => "blsmapfp2tog2"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFpToG1Benchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFpToG1Benchmark.cs new file mode 100644 index 00000000000..0f2fc9bad0b --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsMapFpToG1Benchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsMapFpToG1Benchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + MapFpToG1Precompile.Instance + }; + + protected override string InputsDirectory => "blsmapfptog1"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsPairingCheckBenchmark.cs b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsPairingCheckBenchmark.cs new file mode 100644 index 00000000000..23c7933af1f --- /dev/null +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/BlsPairingCheckBenchmark.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Evm.Precompiles; +using Nethermind.Evm.Precompiles.Bls; + +namespace Nethermind.Precompiles.Benchmark; + +public class BlsPairingCheckBenchmark : PrecompileBenchmarkBase +{ + protected override IEnumerable Precompiles => new[] + { + PairingCheckPrecompile.Instance + }; + + protected override string InputsDirectory => "blspairingcheck"; +} diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/Nethermind.Precompiles.Benchmark.csproj b/src/Nethermind/Nethermind.Precompiles.Benchmark/Nethermind.Precompiles.Benchmark.csproj index c7d79384acf..1fa7275f368 100644 --- a/src/Nethermind/Nethermind.Precompiles.Benchmark/Nethermind.Precompiles.Benchmark.csproj +++ b/src/Nethermind/Nethermind.Precompiles.Benchmark/Nethermind.Precompiles.Benchmark.csproj @@ -18,10 +18,6 @@ %(RecursiveDir)%(FileName)%(Extension) PreserveNewest - - - PreserveNewest - diff --git a/src/Nethermind/Nethermind.Precompiles.Benchmark/modexp/current/file.json b/src/Nethermind/Nethermind.Precompiles.Benchmark/modexp/current/file.json deleted file mode 100644 index a7fc3a30973..00000000000 --- a/src/Nethermind/Nethermind.Precompiles.Benchmark/modexp/current/file.json +++ /dev/null @@ -1,142 +0,0 @@ -[ - { - "Input": "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002003fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "Expected": "0000000000000000000000000000000000000000000000000000000000000001", - "Name": "eip_example1", - "Gas": 13056, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "Expected": "0000000000000000000000000000000000000000000000000000000000000000", - "Name": "eip_example2", - "Gas": 13056, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb502fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", - "Expected": "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", - "Name": "nagydani-1-square", - "Gas": 204, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb503fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", - "Expected": "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", - "Name": "nagydani-1-qube", - "Gas": 204, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000040e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb0033ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c6b", - "Expected": "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", - "Name": "nagydani-1-pow0x10001", - "Gas": 3276, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5102e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", - "Expected": "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", - "Name": "nagydani-2-square", - "Gas": 665, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", - "Expected": "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", - "Name": "nagydani-2-qube", - "Gas": 665, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", - "Expected": "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", - "Name": "nagydani-2-pow0x10001", - "Gas": 10649, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - "Expected": "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", - "Name": "nagydani-3-square", - "Gas": 1894, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - "Expected": "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", - "Name": "nagydani-3-qube", - "Gas": 1894, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - "Expected": "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", - "Name": "nagydani-3-pow0x10001", - "Gas": 30310, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - "Expected": "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", - "Name": "nagydani-4-square", - "Gas": 5580, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - "Expected": "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", - "Name": "nagydani-4-qube", - "Gas": 5580, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - "Expected": "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", - "Name": "nagydani-4-pow0x10001", - "Gas": 89292, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - "Expected": "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", - "Name": "nagydani-5-square", - "Gas": 17868, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - "Expected": "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", - "Name": "nagydani-5-qube", - "Gas": 17868, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - "Expected": "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", - "Name": "nagydani-5-pow0x10001", - "Gas": 285900, - "NoBenchmark": false - }, - { - "Input": "0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000409aaf0cd43ee868a92194c346bac7d5551e97439fb92163e38fbd2699ece0e817a8d70b67a7d39c975e9490645464d21a60e59eb4c1bc00784c294581c1cfbd41acab9ee2bb6dae4afaac402591ff8e2aeb0b3806413456ebd6931b7e8e1bd58ed74eb7a8c6ef5ff33754b9147bdbe74d9a1a96b597b6ff7855b20c285230bbded6b14e4247e664fbac45cfed7a4170fbabcee373851ff7204f3f5313e236a00798db3ec98ca406b4b69a0951e712fd1341117c844c4859b6ba2df30792501876", - "Expected": "97e3edb32d968a33cae520a37fe5cada129980885801e54cc5b45ae2f544711074408dd98174ddfe5b920222091dfc672e59f5001ef526f315726c9597a2f7397", - "Name": "random-1", - "Gas": 10901, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100a562755a0b5a3c0dce5d2f99a3dadc496848d9894565eda1ebbe03a1219a253b561718ba823e8781bff41af6ba2910e9dc98ab32c866674075d46e4d579aeb3337f3438bc121c72ac058ca504bf7e6c2ad19c588eff273efb0d845aecf3a2a2408e6bfc6e58528bad18072709b16f57dedac9f3dd56343448053dae9dc195a862da647643820dc28f10ab76f80025499e2c7c0239bed4fa5a37a4592aa3fdeb725eb43f5ce9145cdcf774ccaabd3c37eff4006145937ea4056a2c0bdda4a7db22cff4994be5ecb5181f35423a085f9cfb7c7926938c95eee01039f2cd088f284f99a9dac8a2ebfa075b49266e3c69fd1a8f10613b8137962a05b93bbbf9e81f900cf2906e74d458807f1f38337f80b3a63ce83de82decce6203a0b2e9a03e1d7339965139c98e95bab7d0cc61ff52ba84f5957f11aa277210243e5ad4116ed03101c509d92f79dcbe5273304d206c62a0f63df41acefbc9022441159b33b1a958f08b502a58d1557bb8c0cf26f724efb7dba9ede37ab572160bf7b4ee64418a90d9d75ca51c385814d87861fe3c921aff515dbf89d3b5de3b093a2e611c671777463e5d1fd84aab9351c90b025383e969dcb59d1681f0bd1409119bd209e6f86150fab7fe450b2706b22c280aaacbd78550b48456da00e545713906b6aec9896ad099a168dbeb24d19e866465c8a2793d3a0681556291975005628d0bf46067e892cd3a012a7eb9758fb2c843ac8f7ccecfd98ca84834083f025153695e28ed2e2e7bc30b786812cb5d0bc5afd953b385e5be4c1ae3bfa06eb50b0ec91b401766c5aa2520bc715667b6c14b3c7e5865ae9ddd839fdcc24a6edb64467d5949a00cf45eea3f3e82d96467d464227463f06c95603b4dcc7789f35fb655583ad91e6d114e481f7e36700af41f38086e27fa6773e9d7f91e62407cc4e6df783d81398ec5f701261bd0ee6ee02049ad4f819537bbedd44101255eaefddb5a876707dd72e5a9327716c45b92fb6908fe28a33d335197bc5face88da098522840866707e852c6b7a65f1e20e7924155981c50865aae1735df4b7dd5fd1b91deb4ac1b1c9", - "Expected": "4aa808af385b8f843aaa63bff77929bcfab802cd8725c696668f00bc1e339438c2613892f1a9469d9b35c18234f33a6c6de477f0e716dcbc763fd2ff134a970576d92b587d0451f92bc47b6fcb0f7f67ddcfc7888868d8df3bf0126556cdbd262b334a15a661116882e211e8a22414172c6b016296b578b246fd967be5b1b2ec2a3154b77c16b55179f08dde77142421cd816649dfca87280e41cd65f8083e407dbc49d08a584b243443f574562965ae489a42076c721e06bd761af6b5a14bcc6082f5b6217519380a0b67af3f3dfde94affc20eec3d7afb97c46ee622f7a24193fe03b8d66850e745215a70989cdb5c4e2eb6eee34ecc050737155ffa228f04", - "Name": "random-2", - "Gas": 695978, - "NoBenchmark": false - }, - { - "Input": "000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000300fca65d7df0af69e7e334361bce8b5493cc610c06045ed7e1d86a097f5de3162cf7cf4ad5b48e2e75dc1a9aaea306eaffa967d2bc3e9b3d9ecf7ca585a5ea89001d0924fe417fdf3e33962ae9c3283dd17f0ad6f6115aed5b92ab0537e9a8cff2919a909ae2ebbd29889489a77154daa63fd3059b1393eec21cc86c9679a7558ac737582997a3ef944cad6d7dd145ea3dd05ff816a334defdabce7e197e27afe03ffee039fcb10e9ce3714e662671f0d6de2fc51aaf3ee793595b7ec924d61525fd8c3ed365b1411f505c3231e577d73adda73b4f61953100174ddfa03245770c5b296999331d712ceda6c9520cb5ade28344eb7d9ac5c3675b3dbb17d1a0ec0ae525d5bb0d3d06bf360e62bacbdff4629ffd1cee59f9729665013df562595b4cbabe6de6dff2d4463d2c468c62099c7c495877d383fdb1ebb33f550c00115dddb316000c710e83ad29d6b12db3e5335c65c1ac09e187c8bc0d81329f9cc8462bce69d84c08900a3bd5c9aaad4c8338ce0bf9a73e6553fb2c4d44fef0dbe3116190865e4a0ce654d4b7703417dc1b708a496493a32007c67b6dacffc391195ff1b8491cc625edc125a4d287f41ee44f32d474a777506e4089bfe3dd5fe790439d11435f93ffa9645ba26efe4e80d15c885dcdef1c72e7a1490893064210613e138eeae3db85bb322a0f4bd5e170f108ee86540219a1262a29a2158a6ba3224a4130eac096ed8103a19ac40d2612bfd9178a17bbbb5f247267bfba396f30c786ec0193b1863172010451607fdd190316e23c73e18c668ee20b7758d632489370d33d20257f0d53e51716b76afea5954ebba45633bce5fa8572b1a60ed89ccebcbdefab9ff8de97d5e98dc975af24e365e2091a8ef260b4ed429ab3cd67450c7b48fbac0ef9f556dd0d81492e1c85d64dcd402bcf813fca5c3bbe5d5d2bc96ea4804e2db477d56cedb33d81711785d1bdc465ff26193dfa083150d23b98f8b14312a5adce5822d6f8d43d1b43be5945b6ed9f99c6ca478a4353248227023155bb8c3d5e72d70264a28e0752db22b649d80a1c4df85feb79cb1a6a7a339faef6a2e860b780fad3dfea3bf637c2f4ae209ddbaa1de5e35c5ca0c492576c883103e011aff3c1ffc8dd363c63bd68b3b6d66b49dc58f49110bf8c0416bc687b736209f257b144bb5f9e12cde6a9809ff3ed0ebceb22abd90f4e00f066bde80c75bac900b3755c0491b0ea3e4626ab673fec1029aeab9050b630e308b15454a1b505582bdf7feb7d90092a3b376d92e8a399b07aacbf5ec92a794a829253533c5b498840cd7f88c0009398a4815418499dfa6517a3daf4f6a78f00653469ea3db6ede0eee4521a2311f0791927b947d66001e597ee3b98805592993e7f228b97a85bee93742a644bdf4106b361dacfed472a053384534e7a545f9852b9dca4ef65e170cbcbb362369c99b7ac35e51f7ac2abf6541d5713550c743f14911191039d164e7f6a032293c7ce96ea9ba2446f401e8f0ee54fd10dce1bbf57744ea58b08b40f1e979f10bf88638c4ec129ba6a2fd0d47281831d4d4541b14f44cb1f79290be2e79225d7a00ed08582c2834ca4605527a47a32098496c2d956eec17bbd431325b547d2d709f83364b1fe9b8ac1f40ceeeea47afff6025d50d04c81b24d55c73eaf5d965d8e7d1dab46cd5786fa371761e1d28c19c9132eb2e98206bd7681175c365ddb635daf607186ceb929aad756993420d07ac8ae759c2fe3730b765f35c5b6fb4f9d5cb30331721b93fa5a0508122b6d8956b09c9388e73e34793b54fa2699f472ae87b83c801c9e07ad1b7b412adb61e008d6de141968c7b51d7f89f61e216c606693598a733109391195e56c6cfd0a9eda73f150bbdf5846bf021f0093a8e88cae1adb9781d66dd7cca40384825b492075eb1caa2f1ca5863a13ca701208a669ac59bd5121731c03061a8316dcd4c364b9fe8ba88c3d5c9275545c6e0cfcc5c4fe99d7299df7fe01ce19a315988eaa664f65b6137dfedd6efc2bdccc16aef60a55bf1842fc9ac7c20089d9729a47f9e6a9f51ea645b146ffbc682c721a31ea1f2299c0d9766869b49ef413f3246ad4b431f523208f50c4a3781885946e55a7ce9eba500087206f418c6a08509e53da52937740250ec990d1d41d605ee3d546484d44546a19d42d1085e29425487304253d3200f61481bfd4297e2b6abf115db623bf0359fe7038bf4a2f9f2c356a8961183b20b0d55033cb8480419ba8c3507484de018870ab27cdf941abe5e2939fbef5ca5184f0ace1d28c37d47e2f9d1686de2ba7576900f06c1353b694a44ee9f1e082a5de71cc83d7fc2ffc38d6a99d5b2a2506b7eb4855f5d9e5719cf54d2e3d4ee39dacf8371512a7d05a3a03b7966ab65b697f483205972cb0058c5ae6e1af00bfeaf30bf063f210f30f1bee1bf1214e06e69ae3aef51cdd0580473288b7c0efaa3944cda25fc5f7766f5422eaa911bee33d540046cc51c9ce1505701bc0873b4e2c43e2ad54aca621a36c3fa1f9f35b6f884e4b63c0a8dee77aff8d5954dbb6e818d4bd662e118f91addea35f1228d421a90193713bd88e8364fa32541e4dcdc7b657882535f54077bfbab5e1057fd5522a6bb0524844ac40453a0133de3a726011dba8ba9fafebb3c11bb28123ac7ee525e02ab29c7a7f71ab48dffbf40c98844755d02ad61ab27a7ffc912d922a1d99bb1b0dfd10bfc3975b370f57cff0bfb4822d39cddc540e0b2eaf33a0e852de802b463fd8fe9b74879138ce9f0a5aab8f3cddaa64f14fbc670d02cf608c23ae09588f2c89b0e7b5e7b9b13ca3866f00d304a3630ba7798edc89d8cbfccb14fb6d8ac4467b2e21df5527c2ab3f1218e19d01fb5be9c0a261de004fffd71bb91ba5b6ce19c12985a5e2c40de4e5ce4fc970f72efad0b811ade763677ade2107396da6068843ff20747c567924b2be7c10b0868fe0edbe4beda420315ec10ac6c0bdd3763ec8c78571d7b97e793eedfb29e657c95892264cae5b19e323cdc1b2718fb867de473e48e9001d093340d1aeb725087769416e2d9687c7e2aee462a315070cea86851ce1a80fe057751c186e896a2bfdc5494930218ad057805b99059d7af42c33e727320826cf596016b87a6f595943b865decbbddcc8fcb4c5614c76dbbafa28eb3aa143f9f49e79efcf0ad156e82601554c389c286e571e2b7d43c583e3855cbe51831eb700bf703f51ce0cbd2a5edd3a4a208e815eff7ce2", - "Expected": "322095f875760be6e68ff243035d11bf24ba5efe065d6f1036756cb9dc71e8c889cc87f614f7e16e694cda1bb12357cb866f7dbd9917c7da6bbd0dcbdc0d923e79d35255a6fba56ae769e8acd80bb6fb55227021649ec00f67c476e88d582dd474100bb9c5e834b13e62d680812a8564e63e94347eb755a129da1e0a7fa33c82eaffcf2faaed039c28ce512df21f987621fe86a48d5a6be2a69f95c3385a8a70ba18a142446f13e1a5d617c84cf5f0d901545eaab402ae8dd15c91216624e67687fd7a2594a3cd8b0e6ed7ba56002a77a6e917561923a503c65fdbc34268daee52d4ec98fa92a0908c0bc5f04bde3a8dac2400d3284b4cf77f3f0929c6352fd242ceaab08afe7b1966afed11ad364bd76b31461559be396fdac25a2bcd8334fa3c43889b5514714e06088a3a3d2b368b8e594cb674a4df2627e2fda6514ddd0c622d627a1f87d14386906da0126c35bb4490f87d71f75d47038096de6a89b9209509385a15bb5dcff3e0d9d493159fdd206df13d34f778aeac9b69e9f2897c93ff587b86e10937c1d6cd7f640c30fe17e0716d74eeea25e18ab1b1aa08e63590261dcab0f247d1bc090dda80e8665109e9ca95f47a5dcecfa0e2bed9864e4dac15afb99d4d1d776d2f65362acb5ca14726d2f3a712f4ed43a1fad18a22f96f470b0ee4877f4820288587e01fd6ef81703776ac27081e9cdf0fba3ca5878f368dc6e06de9fdd2ccc10c9b1da1a3947a9cb87df4740db06fabc21f5082b5c580f1675c48834ab4cc0ff49e8c134f0a0c76b5d0820bc5f2691e7e3ce48a327c311921a8b746fc8192dd2abae3aff0c153af09367f49cbbc39c02984c8c077ddf8dcaf786fac7cc2f3aef8daec0a700172abcdbee72d830c6eaf5def5442658f6f878b581f213565ec085685804b658360366e024dae2de41c49ceb78399de7997d3a6bea66fff501ffd08695c9941a91a1d129d10c1d61b720e0f1f1f9d7c3d06e1304e8d8b7fc243cc9f361af4f75d007c6a7a8580f5af4e898c6644435697f70abf307ea519ef9a140668f145b6741fe1b3e0f14f3638158c6bb81c54b524c122", - "Name": "random-3", - "Gas": 18868224, - "NoBenchmark": false - } -] \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Runner/JsonRpc/Startup.cs b/src/Nethermind/Nethermind.Runner/JsonRpc/Startup.cs index f3c29b1fed8..7e970cda02d 100644 --- a/src/Nethermind/Nethermind.Runner/JsonRpc/Startup.cs +++ b/src/Nethermind/Nethermind.Runner/JsonRpc/Startup.cs @@ -163,7 +163,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IJsonRpc if (jsonRpcUrl.MaxRequestBodySize is not null) ctx.Features.Get().MaxRequestBodySize = jsonRpcUrl.MaxRequestBodySize; - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); CountingPipeReader request = new(ctx.Request.BodyReader); try { @@ -243,7 +243,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IJsonRpc await ctx.Response.CompleteAsync(); } - long handlingTimeMicroseconds = stopwatch.ElapsedMicroseconds(); + long handlingTimeMicroseconds = (long)Stopwatch.GetElapsedTime(startTime).TotalMicroseconds; _ = jsonRpcLocalStats.ReportCall(result.IsCollection ? new RpcReport("# collection serialization #", handlingTimeMicroseconds, true) : result.Report.Value, handlingTimeMicroseconds, resultWriter.WrittenCount); diff --git a/src/Nethermind/Nethermind.Runner/NLog.config b/src/Nethermind/Nethermind.Runner/NLog.config index d99f39d9003..b9e3d620738 100644 --- a/src/Nethermind/Nethermind.Runner/NLog.config +++ b/src/Nethermind/Nethermind.Runner/NLog.config @@ -66,7 +66,7 @@ - + diff --git a/src/Nethermind/Nethermind.Runner/Program.cs b/src/Nethermind/Nethermind.Runner/Program.cs index e6d0b5780a0..fef352adc11 100644 --- a/src/Nethermind/Nethermind.Runner/Program.cs +++ b/src/Nethermind/Nethermind.Runner/Program.cs @@ -412,11 +412,10 @@ private static IConfigProvider BuildConfigProvider( else { _logger.Info($"Loading standard NLog.config file from {"NLog.config".GetApplicationResourcePath()}."); - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); LogManager.Configuration = new XmlLoggingConfiguration("NLog.config".GetApplicationResourcePath()); - stopwatch.Stop(); - _logger.Info($"NLog.config loaded in {stopwatch.ElapsedMilliseconds}ms."); + _logger.Info($"NLog.config loaded in {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms."); } // TODO: dynamically switch log levels from CLI! diff --git a/src/Nethermind/Nethermind.Runner/configs/hive.cfg b/src/Nethermind/Nethermind.Runner/configs/hive.cfg index 4bbfac20a03..d0d51c733ce 100644 --- a/src/Nethermind/Nethermind.Runner/configs/hive.cfg +++ b/src/Nethermind/Nethermind.Runner/configs/hive.cfg @@ -9,7 +9,12 @@ }, "JsonRpc": { "Enabled": true, - "Host": "0.0.0.0" + "Timeout": 20000, + "Host": "127.0.0.1", + "Port": 8545, + "EngineHost": "127.0.0.1", + "EnginePort": 8551, + "EngineEnabledModules": "net,eth,subscribe,engine,web3,client" }, "Network": { "ExternalIp": "127.0.0.1" diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 3c6eba08339..0530f141fd8 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -14,7 +14,7 @@ public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder private readonly HeaderDecoder _headerDecoder = new(); private readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly WithdrawalDecoder _withdrawalDecoder = new(); - private readonly ConsensusRequestDecoder _consensusRequestsDecoder = new(); + private readonly ConsensusRequestDecoder _consensusRequestsDecoder = ConsensusRequestDecoder.Instance; public Block? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs index e06c135bd9b..91ac5675f87 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/ConsensusRequestDecoder.cs @@ -8,12 +8,16 @@ namespace Nethermind.Serialization.Rlp; public class ConsensusRequestDecoder : IRlpStreamDecoder, IRlpValueDecoder, IRlpObjectDecoder { - private readonly WithdrawalRequestDecoder _withdrawalRequestDecoder = new(); + public static ConsensusRequestDecoder Instance { get; } = new(); + private readonly DepositDecoder _depositDecoder = DepositDecoder.Instance; + private readonly WithdrawalRequestDecoder _withdrawalRequestDecoder = WithdrawalRequestDecoder.Instance; + private readonly ConsolidationRequestDecoder _consolidationRequestDecoder = ConsolidationRequestDecoder.Instance; 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}") @@ -25,6 +29,7 @@ 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}") @@ -55,6 +60,7 @@ public int GetLength(ConsensusRequest item, RlpBehaviors rlpBehaviors) ConsensusRequest result = consensusRequestsType switch { + ConsensusRequestsType.ConsolidationRequest => _consolidationRequestDecoder.Decode(rlpStream, rlpBehaviors), ConsensusRequestsType.WithdrawalRequest => _withdrawalRequestDecoder.Decode(rlpStream, rlpBehaviors), ConsensusRequestsType.Deposit => _depositDecoder.Decode(rlpStream, rlpBehaviors), @@ -81,6 +87,7 @@ public ConsensusRequest Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBe 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}") @@ -102,6 +109,9 @@ public void Encode(RlpStream stream, ConsensusRequest item, RlpBehaviors rlpBeha 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; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs new file mode 100644 index 00000000000..99b2f3e49e8 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/ConsolidationRequestDecoder.cs @@ -0,0 +1,58 @@ +// 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 +{ + public static ConsolidationRequestDecoder Instance { get; } = new(); + 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); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip7702/AuthorizationTupleDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7702/AuthorizationTupleDecoder.cs new file mode 100644 index 00000000000..b3e7dfcc80d --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip7702/AuthorizationTupleDecoder.cs @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using DotNetty.Buffers; +using Nethermind.Core; +using Nethermind.Int256; +using Nethermind.Serialization.Rlp.Eip2930; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Nethermind.Serialization.Rlp; + +public class AuthorizationTupleDecoder : IRlpStreamDecoder, IRlpValueDecoder +{ + public static readonly AuthorizationTupleDecoder Instance = new(); + + public AuthorizationTuple Decode(RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int length = stream.ReadSequenceLength(); + int check = length + stream.Position; + ulong chainId = stream.DecodeULong(); + Address? codeAddress = stream.DecodeAddress(); + ulong nonce = stream.DecodeULong(); + ulong yParity = stream.DecodeULong(); + byte[] r = stream.DecodeByteArray(); + byte[] s = stream.DecodeByteArray(); + + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + stream.Check(check); + } + + if (codeAddress is null) + { + ThrowMissingCodeAddressException(); + } + + return new AuthorizationTuple(chainId, codeAddress, nonce, yParity, r, s); + } + + public AuthorizationTuple Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int length = decoderContext.ReadSequenceLength(); + int check = length + decoderContext.Position; + ulong chainId = decoderContext.DecodeULong(); + Address? codeAddress = decoderContext.DecodeAddress(); + ulong nonce = decoderContext.DecodeULong(); + ulong yParity = decoderContext.DecodeULong(); + byte[] r = decoderContext.DecodeByteArray(); + byte[] s = decoderContext.DecodeByteArray(); + + if (!rlpBehaviors.HasFlag(RlpBehaviors.AllowExtraBytes)) + { + decoderContext.Check(check); + } + + if (codeAddress is null) + { + ThrowMissingCodeAddressException(); + } + + return new AuthorizationTuple(chainId, codeAddress, nonce, yParity, r, s); + } + + public RlpStream Encode(AuthorizationTuple item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + RlpStream stream = new(GetLength(item, rlpBehaviors)); + Encode(stream, item, rlpBehaviors); + return stream; + } + + public void Encode(RlpStream stream, AuthorizationTuple item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int contentLength = GetContentLength(item); + stream.StartSequence(contentLength); + stream.Encode(item.ChainId); + stream.Encode(item.CodeAddress); + stream.Encode(item.Nonce); + stream.Encode(item.AuthoritySignature.RecoveryId); + stream.Encode(new UInt256(item.AuthoritySignature.R, true)); + stream.Encode(new UInt256(item.AuthoritySignature.S, true)); + } + + public NettyRlpStream EncodeWithoutSignature(ulong chainId, Address codeAddress, ulong nonce) + { + int contentLength = GetContentLengthWithoutSig(chainId, codeAddress, nonce); + var totalLength = Rlp.LengthOfSequence(contentLength); + IByteBuffer byteBuffer = PooledByteBufferAllocator.Default.Buffer(totalLength); + NettyRlpStream stream = new(byteBuffer); + EncodeWithoutSignature(stream, chainId, codeAddress, nonce); + return stream; + } + + public void EncodeWithoutSignature(RlpStream stream, ulong chainId, Address codeAddress, ulong nonce) + { + int contentLength = GetContentLengthWithoutSig(chainId, codeAddress, nonce); + stream.StartSequence(contentLength); + stream.Encode(chainId); + stream.Encode(codeAddress ?? throw new RlpException($"Invalid tx {nameof(AuthorizationTuple)} format - address is null")); + stream.Encode(nonce); + } + + public int GetLength(AuthorizationTuple item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item)); + + private static int GetContentLength(AuthorizationTuple tuple) => + GetContentLengthWithoutSig(tuple.ChainId, tuple.CodeAddress, tuple.Nonce) + + Rlp.LengthOf(tuple.AuthoritySignature.RecoveryId) + + Rlp.LengthOf(new UInt256(tuple.AuthoritySignature.R.AsSpan(), true)) + + Rlp.LengthOf(new UInt256(tuple.AuthoritySignature.S.AsSpan(), true)); + + private static int GetContentLengthWithoutSig(ulong chainId, Address codeAddress, ulong nonce) => + Rlp.LengthOf(chainId) + + Rlp.LengthOf(codeAddress) + + Rlp.LengthOf(nonce); + + [DoesNotReturn] + [StackTraceHidden] + private static void ThrowMissingCodeAddressException() => throw new RlpException("Missing code address for Authorization"); +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs index 6672a485297..5c1919959ef 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs @@ -27,7 +27,7 @@ public class RlpStream private static readonly BlockInfoDecoder _blockInfoDecoder = new(); private static readonly TxDecoder _txDecoder = TxDecoder.Instance; private static readonly WithdrawalDecoder _withdrawalDecoder = new(); - private static readonly ConsensusRequestDecoder _requestsDecoder = new(); + private static readonly ConsensusRequestDecoder _requestsDecoder = ConsensusRequestDecoder.Instance; private static readonly LogEntryDecoder _logEntryDecoder = LogEntryDecoder.Instance; private readonly CappedArray _data; @@ -56,6 +56,23 @@ public RlpStream(in CappedArray data) _data = data; } + public void EncodeArray(T?[]? items, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (items is null) + { + WriteByte(Rlp.NullObjectByte); + return; + } + IRlpStreamDecoder decoder = Rlp.GetStreamDecoder(); + int contentLength = decoder.GetContentLength(items); + + StartSequence(contentLength); + + foreach (var item in items) + { + decoder.Encode(this, item, rlpBehaviors); + } + } public void Encode(Block value) { _blockDecoder.Encode(this, value); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs index 6a720d6e5e5..0c16876e02f 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs @@ -40,6 +40,7 @@ protected TxDecoder(Func? transactionFactory = null) RegisterDecoder(new AccessListTxDecoder(factory)); RegisterDecoder(new EIP1559TxDecoder(factory)); RegisterDecoder(new BlobTxDecoder(factory)); + RegisterDecoder(new SetCodeTxDecoder(factory)); } public void RegisterDecoder(ITxDecoder decoder) => _decoders[(int)decoder.Type] = decoder; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/BaseTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/BaseTxDecoder.cs index ab72eef75e7..a8918382c1d 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/BaseTxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/BaseTxDecoder.cs @@ -155,7 +155,7 @@ protected virtual void DecodeGasPrice(Transaction transaction, ref Rlp.ValueDeco transaction.GasPrice = decoderContext.DecodeUInt256(); } - private Signature? DecodeSignature(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + protected Signature? DecodeSignature(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { ulong v = rlpStream.DecodeULong(); ReadOnlySpan rBytes = rlpStream.DecodeByteArraySpan(); @@ -163,7 +163,7 @@ protected virtual void DecodeGasPrice(Transaction transaction, ref Rlp.ValueDeco return DecodeSignature(v, rBytes, sBytes, transaction.Signature, rlpBehaviors); } - private Signature? DecodeSignature(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + protected Signature? DecodeSignature(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) { ulong v = decoderContext.DecodeULong(); ReadOnlySpan rBytes = decoderContext.DecodeByteArraySpan(); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/SetCodeTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/SetCodeTxDecoder.cs new file mode 100644 index 00000000000..ffc110f998b --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoders/SetCodeTxDecoder.cs @@ -0,0 +1,38 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public sealed class SetCodeTxDecoder(Func? transactionFactory = null) + : BaseEIP1559TxDecoder(TxType.SetCode, transactionFactory) where T : Transaction, new() +{ + private AuthorizationTupleDecoder _authTupleDecoder = new(); + + protected override void DecodePayload(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.DecodePayload(transaction, rlpStream, rlpBehaviors); + transaction.AuthorizationList = rlpStream.DecodeArray((s) => _authTupleDecoder.Decode(s, rlpBehaviors)); + } + + protected override void DecodePayload(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, + RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.DecodePayload(transaction, ref decoderContext, rlpBehaviors); + transaction.AuthorizationList = decoderContext.DecodeArray(_authTupleDecoder); + } + + protected override void EncodePayload(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.EncodePayload(transaction, stream, rlpBehaviors); + stream.EncodeArray(transaction.AuthorizationList, rlpBehaviors); + } + + protected override int GetPayloadLength(Transaction transaction) + { + return base.GetPayloadLength(transaction) + + (transaction.AuthorizationList is null ? 1 : Rlp.LengthOfSequence(_authTupleDecoder.GetContentLength(transaction.AuthorizationList, RlpBehaviors.None))); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs index 4a0de9f3288..450a842cc9c 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/WithdrawalRequestDecoder.cs @@ -9,6 +9,7 @@ namespace Nethermind.Serialization.Rlp; public class WithdrawalRequestDecoder : IRlpStreamDecoder, IRlpValueDecoder, IRlpObjectDecoder { + public static WithdrawalRequestDecoder Instance { get; } = new(); public int GetLength(WithdrawalRequest item, RlpBehaviors rlpBehaviors) => Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors)); diff --git a/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj b/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj index 2e34e8434b8..5f7acfe37b8 100644 --- a/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj +++ b/src/Nethermind/Nethermind.Shutter/Nethermind.Shutter.csproj @@ -1,6 +1,7 @@  + true Nethermind.Shutter enable diff --git a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs index 0a3c0750b97..540f147fe94 100644 --- a/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs +++ b/src/Nethermind/Nethermind.Shutter/ShutterCrypto.cs @@ -12,6 +12,7 @@ using Nethermind.Merkleization; using System.Text; using Nethermind.Core.Collections; +using System.Runtime.CompilerServices; namespace Nethermind.Shutter; @@ -37,6 +38,7 @@ public readonly ref struct EncryptedMessage public class ShutterCryptoException(string message, Exception? innerException = null) : Exception(message, innerException); + [SkipLocalsInit] public static void ComputeIdentity(G1 p, scoped ReadOnlySpan identityPrefix, in Address sender) { Span preimage = stackalloc byte[52]; @@ -97,18 +99,19 @@ public static EncryptedMessage DecodeEncryptedMessage(ReadOnlySpan bytes) } } - public static void RecoverSigma(out Span res, EncryptedMessage encryptedMessage, G1Affine decryptionKey) + public static void RecoverSigma(out Span sigma, EncryptedMessage encryptedMessage, G1Affine decryptionKey) { using ArrayPoolList buf = new(GT.Sz, GT.Sz); GT p = new(buf.AsSpan()); p.MillerLoop(encryptedMessage.C1.ToAffine(), decryptionKey); - res = Hash2(p); // key - res.Xor(encryptedMessage.C2); + sigma = Hash2(p); // key + sigma.Xor(encryptedMessage.C2); } - public static void ComputeBlockKeys(Span res, ReadOnlySpan sigma, int n) + [SkipLocalsInit] + public static void ComputeBlockKeys(Span blockKeys, ReadOnlySpan sigma, int n) { - if (res.Length != 32 * n) + if (blockKeys.Length != 32 * n) { throw new ArgumentException("Block keys buffer was the wrong size."); } @@ -132,7 +135,7 @@ public static void ComputeBlockKeys(Span res, ReadOnlySpan sigma, in sigma.CopyTo(preimage); suffix[(4 - suffixLength)..4].CopyTo(preimage[32..]); - Hash4(preimage).Bytes.CopyTo(res[(i * 32)..]); + Hash4(preimage).Bytes.CopyTo(blockKeys[(i * 32)..]); } } @@ -156,7 +159,6 @@ public static Span Hash2(GT p) return HashBytesToBlock(preimage); } - // res should be intialised to one public static void GTExp(ref GT x, UInt256 exp) { if (exp == 0) @@ -178,6 +180,7 @@ public static void GTExp(ref GT x, UInt256 exp) } } + [SkipLocalsInit] public static bool CheckDecryptionKey(G1Affine decryptionKey, G2Affine eonPublicKey, G1Affine identity) { int len = GT.Sz * 2; @@ -189,8 +192,6 @@ public static bool CheckDecryptionKey(G1Affine decryptionKey, G2Affine eonPublic return GT.FinalVerify(p1, p2); } - private static readonly Ecdsa _ecdsa = new(); - public static bool CheckSlotDecryptionIdentitiesSignature( ulong instanceId, ulong eon, @@ -215,6 +216,7 @@ public static bool CheckSlotDecryptionIdentitiesSignature( return expectedPubkey is not null && keyperAddress == expectedPubkey.Address; } + [SkipLocalsInit] public static bool CheckValidatorRegistrySignature(ReadOnlySpan pkBytes, ReadOnlySpan sigBytes, ReadOnlySpan msgBytes) { BlsSigner.Signature sig = new(sigBytes); @@ -255,6 +257,8 @@ public static Span EncodeEncryptedMessage(EncryptedMessage encryptedMessag return encoded; } + private static readonly Ecdsa _ecdsa = new(); + private static G2 ComputeC1(UInt256 r) => G2.Generator().Mult(r.ToLittleEndian()); @@ -284,37 +288,37 @@ private static Span ComputeC2(scoped ReadOnlySpan sigma, UInt256 r, GT p = new(buf.AsSpan()); p.MillerLoop(eonKey, identity); GTExp(ref p, r); - Span res = Hash2(p); //key - res.Xor(sigma); - return res; + Span c2 = Hash2(p); //key + c2.Xor(sigma); + return c2; } - private static void ComputeC3(Span res, scoped ReadOnlySpan msgBlocks, ReadOnlySpan sigma) + private static void ComputeC3(Span c3, scoped ReadOnlySpan msgBlocks, ReadOnlySpan sigma) { - // res = keys ^ msgs - ComputeBlockKeys(res, sigma, msgBlocks.Length / 32); - res.Xor(msgBlocks); + // c3 = keys ^ msgs + ComputeBlockKeys(c3, sigma, msgBlocks.Length / 32); + c3.Xor(msgBlocks); } private static int PaddedLength(ReadOnlySpan bytes) => bytes.Length + (32 - (bytes.Length % 32)); - private static Span PadAndSplit(Span res, scoped ReadOnlySpan bytes) + private static Span PadAndSplit(Span paddedBytes, scoped ReadOnlySpan bytes) { int n = 32 - (bytes.Length % 32); - bytes.CopyTo(res); - res[bytes.Length..].Fill((byte)n); - return res; + bytes.CopyTo(paddedBytes); + paddedBytes[bytes.Length..].Fill((byte)n); + return paddedBytes; } - private static void ComputeR(scoped ReadOnlySpan sigma, scoped ReadOnlySpan msg, out UInt256 res) + private static void ComputeR(scoped ReadOnlySpan sigma, scoped ReadOnlySpan msg, out UInt256 r) { int len = 32 + msg.Length; using ArrayPoolList buf = new(len, len); Span preimage = buf.AsSpan(); sigma.CopyTo(preimage); msg.CopyTo(preimage[32..]); - Hash3(preimage, out res); + Hash3(preimage, out r); } internal static ValueHash256 GenerateHash(ulong instanceId, ulong eon, ulong slot, ulong txPointer, IEnumerable> identityPreimages) diff --git a/src/Nethermind/Nethermind.Shutter/ShutterKeyValidator.cs b/src/Nethermind/Nethermind.Shutter/ShutterKeyValidator.cs index 22978b32b92..f76ca05a323 100644 --- a/src/Nethermind/Nethermind.Shutter/ShutterKeyValidator.cs +++ b/src/Nethermind/Nethermind.Shutter/ShutterKeyValidator.cs @@ -10,6 +10,7 @@ using Nethermind.Logging; using Google.Protobuf; using Nethermind.Core.Collections; +using System.Runtime.CompilerServices; namespace Nethermind.Shutter; @@ -65,6 +66,7 @@ public class ShutterKeyValidator( } } + [SkipLocalsInit] private bool CheckDecryptionKeys(in Dto.DecryptionKeys decryptionKeys, in IShutterEon.Info eonInfo) { if (decryptionKeys.InstanceID != _instanceId) diff --git a/src/Nethermind/Nethermind.Shutter/ShutterTxLoader.cs b/src/Nethermind/Nethermind.Shutter/ShutterTxLoader.cs index eee65b30253..0e8d2f814b3 100644 --- a/src/Nethermind/Nethermind.Shutter/ShutterTxLoader.cs +++ b/src/Nethermind/Nethermind.Shutter/ShutterTxLoader.cs @@ -17,6 +17,7 @@ using System.IO; using Nethermind.Abi; using Nethermind.Facade.Find; +using System.Runtime.CompilerServices; namespace Nethermind.Shutter; @@ -127,6 +128,7 @@ private Transaction[] FilterTransactions(IEnumerable transactions, .ToPooledList(sequencedTransactions.Count); } + [SkipLocalsInit] private Transaction? DecryptSequencedTransaction(SequencedTransaction sequencedTransaction, (ReadOnlyMemory IdentityPreimage, ReadOnlyMemory Key) decryptionKey) { try @@ -209,6 +211,7 @@ private IEnumerable GetNextTransactions(ulong eon, ulong t } } + [SkipLocalsInit] private static SequencedTransaction EventToSequencedTransaction(ISequencerContract.TransactionSubmitted e, int index, ulong eon) { byte[] identityPreimage = new byte[52]; diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index 72f9b114458..ba794b320b1 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -708,7 +708,9 @@ public void Eip_transitions_loaded_correctly() Eip3855TransitionTimestamp = 1000000012, Eip3860TransitionTimestamp = 1000000012, Eip1153TransitionTimestamp = 1000000024, - Eip2537TransitionTimestamp = 1000000024 + Eip2537TransitionTimestamp = 1000000024, + + Eip7702TransitionTimestamp = 1000000032, } }; @@ -787,6 +789,7 @@ void TestTransitions(ForkActivation activation, Action changes) r.IsEip3860Enabled = true; }); TestTransitions((40001L, 1000000024), r => { r.IsEip1153Enabled = r.IsEip2537Enabled = true; }); + TestTransitions((40001L, 1000000032), r => { r.IsEip7702Enabled = true; }); } [TestCaseSource(nameof(BlockNumbersAndTimestampsNearForkActivations))] diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 2a611193b51..f46c3b5f6fb 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -158,9 +158,12 @@ public ulong Eip4844TransitionTimestamp 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; + public bool IsEip7702Enabled => _spec.IsEip7702Enabled; public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index 5ddba657095..e5cb4cd37a5 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -123,9 +123,12 @@ public class ChainParameters 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; } + public ulong? Eip7702TransitionTimestamp { get; set; } public ulong? OpGraniteTransitionTimestamp { get; set; } #region EIP-4844 parameters diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 7a344e6dc77..7e2bd8cf7ba 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -259,12 +259,17 @@ private static ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseSt 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; + releaseSpec.IsOntakeEnabled = (chainSpec.Parameters.OntakeTransition ?? long.MaxValue) <= releaseStartBlock; return releaseSpec; } diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs index 8d150227c96..e9b575bd649 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs @@ -146,6 +146,7 @@ bool GetForInnerPathExistence(KeyValuePair o) => Rip7212TransitionTimestamp = chainSpecJson.Params.Rip7212TransitionTimestamp, OpGraniteTransitionTimestamp = chainSpecJson.Params.OpGraniteTransitionTimestamp, Eip4788TransitionTimestamp = chainSpecJson.Params.Eip4788TransitionTimestamp, + Eip7702TransitionTimestamp = chainSpecJson.Params.Eip7702TransitionTimestamp, Eip4788ContractAddress = chainSpecJson.Params.Eip4788ContractAddress ?? Eip4788Constants.BeaconRootsAddress, Eip2935TransitionTimestamp = chainSpecJson.Params.Eip2935TransitionTimestamp, Eip2935ContractAddress = chainSpecJson.Params.Eip2935ContractAddress ?? Eip2935Constants.BlockHashHistoryAddress, @@ -162,6 +163,8 @@ bool GetForInnerPathExistence(KeyValuePair o) => 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, FeeCollector = chainSpecJson.Params.FeeCollector, Eip1559FeeCollectorTransition = chainSpecJson.Params.Eip1559FeeCollectorTransition, Eip1559BaseFeeMinValueTransition = chainSpecJson.Params.Eip1559BaseFeeMinValueTransition, @@ -431,10 +434,11 @@ private static void LoadGenesis(ChainSpecJson chainSpecJson, ChainSpec chainSpec 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; - bool requestsEnabled = depositsEnabled || withdrawalRequestsEnabled; + var requestsEnabled = depositsEnabled || withdrawalRequestsEnabled || consolidationRequestsEnabled; if (requestsEnabled) genesisHeader.RequestsRoot = Keccak.EmptyTreeHash; ; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index ad77173f320..b4749be1ab5 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -155,6 +155,9 @@ internal class ChainSpecParamsJson 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; } public ulong? OpGraniteTransitionTimestamp { get; set; } } diff --git a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs index 2aaffc45998..d456bde53be 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/00_Olympic.cs @@ -28,6 +28,7 @@ protected Olympic() 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; } diff --git a/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs b/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs index d88d482ea47..7fd7e8c348d 100644 --- a/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs +++ b/src/Nethermind/Nethermind.Specs/Forks/18_Prague.cs @@ -16,8 +16,10 @@ protected Prague() Name = "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/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 68bf7731ae1..dfb34cf9faf 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -86,10 +86,18 @@ public bool IsEip1559Enabled public bool IsEip5656Enabled { get; set; } public bool IsEip6780Enabled { get; set; } public bool IsEip4788Enabled { get; set; } + public bool IsEip7702Enabled { get; set; } public bool IsEip4844FeeCollectorEnabled { get; set; } public bool IsEip7002Enabled { get; set; } + public bool IsEip7251Enabled { get; set; } public bool IsOntakeEnabled { get; set; } + private Address _eip7251ContractAddress; + public Address Eip7251ContractAddress + { + get => IsEip7251Enabled ? _eip7251ContractAddress : null; + set => _eip7251ContractAddress = value; + } private Address _eip7002ContractAddress; public Address Eip7002ContractAddress { @@ -123,7 +131,5 @@ public Address Eip2935ContractAddress get => IsEip2935Enabled ? _eip2935ContractAddress : null; set => _eip2935ContractAddress = value; } - - } } diff --git a/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs b/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs index f1e5d5eaad1..e47aab57922 100644 --- a/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StateProviderTests.cs @@ -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/StateReaderTests.cs b/src/Nethermind/Nethermind.State.Test/StateReaderTests.cs index 5e56dd22f44..bedccef85ce 100644 --- a/src/Nethermind/Nethermind.State.Test/StateReaderTests.cs +++ b/src/Nethermind/Nethermind.State.Test/StateReaderTests.cs @@ -248,5 +248,114 @@ public void Can_collect_stats() var stats = stateReader.CollectStats(provider.StateRoot, new MemDb(), Logger); stats.AccountCount.Should().Be(1); } + + [Test] + public void IsInvalidContractSender_AccountHasCode_ReturnsTrue() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip3607Enabled.Returns(true); + releaseSpec.IsEip7702Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + sut.InsertCode(TestItem.AddressA, ValueKeccak.Compute(new byte[1]), new byte[1], releaseSpec, false); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA); + + Assert.That(result, Is.True); + } + + [Test] + public void IsInvalidContractSender_AccountHasNoCode_ReturnsFalse() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip3607Enabled.Returns(true); + releaseSpec.IsEip7702Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA); + + Assert.That(result, Is.False); + } + + [Test] + public void IsInvalidContractSender_AccountHasDelegatedCode_ReturnsFalse() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip3607Enabled.Returns(true); + releaseSpec.IsEip7702Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + byte[] code = [.. Eip7702Constants.DelegationHeader, .. new byte[20]]; + sut.InsertCode(TestItem.AddressA, ValueKeccak.Compute(code), code, releaseSpec, false); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA); + + Assert.That(result, Is.False); + } + + [Test] + public void IsInvalidContractSender_AccountHasCodeButDelegateReturnsTrue_ReturnsFalse() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip3607Enabled.Returns(true); + releaseSpec.IsEip7702Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + byte[] code = new byte[20]; + sut.InsertCode(TestItem.AddressA, ValueKeccak.Compute(code), code, releaseSpec, false); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA, () => true); + + Assert.That(result, Is.False); + } + + [Test] + public void IsInvalidContractSender_AccountHasDelegatedCodeBut7702IsNotEnabled_ReturnsTrue() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip3607Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + byte[] code = [.. Eip7702Constants.DelegationHeader, .. new byte[20]]; + sut.InsertCode(TestItem.AddressA, ValueKeccak.Compute(code), code, releaseSpec, false); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA); + + Assert.That(result, Is.True); + } + + [Test] + public void IsInvalidContractSender_AccountHasDelegatedCodeBut3807IsNotEnabled_ReturnsFalse() + { + IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.IsEip7702Enabled.Returns(true); + TrieStore trieStore = new TrieStore(new MemDb(), Logger); + WorldState sut = new(trieStore, new MemDb(), Logger); + sut.CreateAccount(TestItem.AddressA, 0); + byte[] code = [.. Eip7702Constants.DelegationHeader, .. new byte[20]]; + sut.InsertCode(TestItem.AddressA, ValueKeccak.Compute(code), code, releaseSpec, false); + sut.Commit(MuirGlacier.Instance); + sut.CommitTree(0); + + bool result = sut.IsInvalidContractSender(releaseSpec, TestItem.AddressA); + + Assert.That(result, Is.False); + } } } diff --git a/src/Nethermind/Nethermind.State/IReadOnlyStateProviderExtensions.cs b/src/Nethermind/Nethermind.State/IReadOnlyStateProviderExtensions.cs new file mode 100644 index 00000000000..2fdf091a047 --- /dev/null +++ b/src/Nethermind/Nethermind.State/IReadOnlyStateProviderExtensions.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Specs; +using System; + +namespace Nethermind.State +{ + public static class IReadOnlyStateProviderExtensions + { + public static byte[] GetCode(this IReadOnlyStateProvider stateProvider, Address address) + { + stateProvider.TryGetAccount(address, out AccountStruct account); + return !account.HasCode ? Array.Empty() : stateProvider.GetCode(account.CodeHash) ?? Array.Empty(); + } + /// + /// Checks if has code that is not a delegation, according to the rules of eip-3607 and eip-7702. + /// Where possible a cache for code lookup should be used, since the fallback will read from . + /// + /// + /// + /// + /// + /// + public static bool IsInvalidContractSender( + this IReadOnlyStateProvider stateProvider, + IReleaseSpec spec, + Address sender, + Func? isDelegatedCode = null) => + spec.IsEip3607Enabled + && stateProvider.HasCode(sender) + && (!spec.IsEip7702Enabled + || (!isDelegatedCode?.Invoke() ?? !Eip7702Constants.IsDelegatedCode(GetCode(stateProvider, sender)))); + } + +} diff --git a/src/Nethermind/Nethermind.State/IWorldState.cs b/src/Nethermind/Nethermind.State/IWorldState.cs index 21ac37a2a64..ac5bf705ab6 100644 --- a/src/Nethermind/Nethermind.State/IWorldState.cs +++ b/src/Nethermind/Nethermind.State/IWorldState.cs @@ -87,7 +87,7 @@ 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, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false); void AddToBalance(Address address, in UInt256 balanceChange, IReleaseSpec spec); diff --git a/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs b/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs index f6ffa4250d1..2cdeadddb5e 100644 --- a/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs +++ b/src/Nethermind/Nethermind.State/IWorldStateExtensions.cs @@ -13,15 +13,9 @@ namespace Nethermind.State { public static class WorldStateExtensions { - public static byte[] GetCode(this IWorldState stateProvider, Address address) - { - stateProvider.TryGetAccount(address, out AccountStruct account); - return !account.HasCode ? Array.Empty() : stateProvider.GetCode(account.CodeHash) ?? Array.Empty(); - } - public static void InsertCode(this IWorldState worldState, Address address, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { - Hash256 codeHash = code.Length == 0 ? Keccak.OfAnEmptyString : Keccak.Compute(code.Span); + ValueHash256 codeHash = code.Length == 0 ? ValueKeccak.OfAnEmptyString : ValueKeccak.Compute(code.Span); worldState.InsertCode(address, codeHash, code, spec, isGenesis); } diff --git a/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs index f33d484ef48..d69e1d0c8d7 100644 --- a/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs +++ b/src/Nethermind/Nethermind.State/Proofs/RequestsTrie.cs @@ -14,7 +14,7 @@ namespace Nethermind.State.Proofs; public class RequestsTrie(ConsensusRequest[]? requests, bool canBuildProof = false, ICappedArrayPool? bufferPool = null) : PatriciaTrie(requests, canBuildProof, bufferPool) { - private static readonly ConsensusRequestDecoder _codec = new(); + private static readonly ConsensusRequestDecoder _codec = ConsensusRequestDecoder.Instance; protected override void Initialize(ConsensusRequest[] requests) { diff --git a/src/Nethermind/Nethermind.State/StateProvider.cs b/src/Nethermind/Nethermind.State/StateProvider.cs index 0afd1fdef0c..c4689da5d04 100644 --- a/src/Nethermind/Nethermind.State/StateProvider.cs +++ b/src/Nethermind/Nethermind.State/StateProvider.cs @@ -29,7 +29,7 @@ internal class StateProvider // Note: // False negatives are fine as they will just result in a overwrite set // False positives would be problematic as the code _must_ be persisted - private readonly ClockKeyCacheNonConcurrent _codeInsertFilter = new(1_024); + private readonly ClockKeyCacheNonConcurrent _codeInsertFilter = new(1_024); private readonly Dictionary _blockCache = new(4_096); private readonly ConcurrentDictionary? _preBlockCache; @@ -37,7 +37,7 @@ internal class StateProvider private readonly ILogger _logger; private readonly IKeyValueStore _codeDb; - private List _changes = new(Resettable.StartCapacity); + private readonly List _changes = new(Resettable.StartCapacity); internal readonly StateTree _tree; private readonly Func _getStateFromTrie; @@ -116,7 +116,7 @@ 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, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { _needsStateRootUpdate = true; @@ -149,7 +149,7 @@ public void InsertCode(Address address, Hash256 codeHash, ReadOnlyMemory c throw new InvalidOperationException($"Account {address} is null when updating code hash"); } - if (account.CodeHash != codeHash) + if (account.CodeHash.ValueHash256 != codeHash) { if (_logger.IsDebug) _logger.Debug($" Update {address} C {account.CodeHash} -> {codeHash}"); Account changedAccount = account.WithChangedCodeHash(codeHash); diff --git a/src/Nethermind/Nethermind.State/WorldState.cs b/src/Nethermind/Nethermind.State/WorldState.cs index fa3753df79c..c18fb219381 100644 --- a/src/Nethermind/Nethermind.State/WorldState.cs +++ b/src/Nethermind/Nethermind.State/WorldState.cs @@ -143,7 +143,7 @@ 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, in ValueHash256 codeHash, ReadOnlyMemory code, IReleaseSpec spec, bool isGenesis = false) { _stateProvider.InsertCode(address, codeHash, code, spec, isGenesis); } diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs index e3540377184..95cde2d1a4e 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncServerTests.cs @@ -702,8 +702,11 @@ public void GetNodeData_returns_cached_trie_nodes() Hash256 nodeKey = TestItem.KeccakA; TrieNode node = new(NodeType.Leaf, nodeKey, TestItem.KeccakB.Bytes); IScopedTrieStore scopedTrieStore = trieStore.GetTrieStore(null); - scopedTrieStore.CommitNode(1, new NodeCommitInfo(node, TreePath.Empty)); - scopedTrieStore.FinishBlockCommit(TrieType.State, 1, node); + using (ICommitter committer = scopedTrieStore.BeginCommit(TrieType.State, 1, node)) + { + TreePath path = TreePath.Empty; + committer.CommitNode(ref path, new NodeCommitInfo(node)); + } stateDb.KeyExists(nodeKey).Should().BeFalse(); ctx.SyncServer.GetNodeData(new[] { nodeKey }, CancellationToken.None, NodeDataType.All).Should().BeEquivalentTo(new[] { TestItem.KeccakB.BytesToArray() }); diff --git a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs index fb3f5927289..c86dfbb240b 100644 --- a/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs +++ b/src/Nethermind/Nethermind.Synchronization.Test/SyncThreadTests.cs @@ -269,15 +269,15 @@ private SyncTestContext CreateSyncManager(int index) ITransactionComparerProvider transactionComparerProvider = new TransactionComparerProvider(specProvider, tree); + CodeInfoRepository codeInfoRepository = new(); TxPool.TxPool txPool = new(ecdsa, new BlobTxStorage(), - new ChainHeadInfoProvider(specProvider, tree, stateReader), + new ChainHeadInfoProvider(specProvider, tree, stateReader, codeInfoRepository), new TxPoolConfig(), new TxValidator(specProvider.ChainId), logManager, transactionComparerProvider.GetDefaultComparer()); BlockhashProvider blockhashProvider = new(tree, specProvider, stateProvider, LimboLogs.Instance); - CodeInfoRepository codeInfoRepository = new(); VirtualMachine virtualMachine = new(blockhashProvider, specProvider, codeInfoRepository, logManager); Always sealValidator = Always.Valid; diff --git a/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs b/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs index 765c0f7e7a4..77f78aa7420 100644 --- a/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs +++ b/src/Nethermind/Nethermind.Synchronization/Blocks/BlockDownloader.cs @@ -167,7 +167,7 @@ bool HasMoreToSync() } if (_logger.IsDebug) _logger.Debug($"Headers request {currentNumber}+{headersToRequest} to peer {bestPeer} with {bestPeer.HeadNumber} blocks. Got {currentNumber} and asking for {headersToRequest} more."); - Stopwatch sw = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); using IOwnedReadOnlyList headers = await RequestHeaders(bestPeer, cancellation, currentNumber, headersToRequest); Hash256? startHeaderHash = headers[0]?.Hash; @@ -188,7 +188,7 @@ bool HasMoreToSync() } ancestorLookupLevel = 0; - AdjustSyncBatchSize(sw.Elapsed); + AdjustSyncBatchSize(Stopwatch.GetElapsedTime(startTime)); for (int i = 1; i < headers.Count; i++) { diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/PendingSyncItems.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/PendingSyncItems.cs index 2cd79d10912..cfc36ce4509 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastSync/PendingSyncItems.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/PendingSyncItems.cs @@ -202,7 +202,7 @@ public List TakeBatch(int maxSize) [MethodImpl(MethodImplOptions.Synchronized)] public string RecalculatePriorities() { - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); string reviewMessage = $"Node sync queues review ({LevelsDescription}):" + Environment.NewLine; reviewMessage += $" before {Description}" + Environment.NewLine; @@ -225,8 +225,7 @@ public string RecalculatePriorities() reviewMessage += $" after {Description}" + Environment.NewLine; - stopwatch.Stop(); - reviewMessage += $" time spent in review: {stopwatch.ElapsedMilliseconds}ms"; + reviewMessage += $" time spent in review: {Stopwatch.GetElapsedTime(startTime).TotalMilliseconds:N0}ms"; return reviewMessage; } } diff --git a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs index 4cc449cb47b..0c4b7b35568 100644 --- a/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs +++ b/src/Nethermind/Nethermind.Synchronization/FastSync/TreeSync.cs @@ -167,7 +167,7 @@ void AddAgainAllItems() if (_logger.IsDebug) _logger.Debug(reviewMessage); } - Stopwatch handleWatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); bool isMissingRequestData = batch.RequestedNodes is null; if (isMissingRequestData) @@ -289,24 +289,24 @@ shorter than the request */ _data.DisplayProgressReport(_pendingRequests.Count, _branchProgress, _logger); - handleWatch.Stop(); - long total = handleWatch.ElapsedMilliseconds + _networkWatch.ElapsedMilliseconds; + long elapsedTime = (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds; + long total = elapsedTime + _networkWatch.ElapsedMilliseconds; if (total != 0) { // calculate averages if (_logger.IsTrace) _logger.Trace( - $"Prepare batch {_networkWatch.ElapsedMilliseconds}ms ({(decimal)_networkWatch.ElapsedMilliseconds / total:P0}) - Handle {handleWatch.ElapsedMilliseconds}ms ({(decimal)handleWatch.ElapsedMilliseconds / total:P0})"); + $"Prepare batch {_networkWatch.ElapsedMilliseconds}ms ({(decimal)_networkWatch.ElapsedMilliseconds / total:P0}) - Handle {elapsedTime:N0}ms ({(decimal)elapsedTime / total:P0})"); } - if (handleWatch.ElapsedMilliseconds > 250) + if (Stopwatch.GetElapsedTime(startTime).TotalMilliseconds > 250) { if (_logger.IsDebug) _logger.Debug( - $"Handle watch {handleWatch.ElapsedMilliseconds}, DB reads {_data.DbChecks - _data.LastDbReads}, ratio {(decimal)handleWatch.ElapsedMilliseconds / Math.Max(1, _data.DbChecks - _data.LastDbReads)}"); + $"Handle watch {elapsedTime:N0}, DB reads {_data.DbChecks - _data.LastDbReads}, ratio {(decimal)elapsedTime / Math.Max(1, _data.DbChecks - _data.LastDbReads)}"); } - Interlocked.Add(ref _handleWatch, handleWatch.ElapsedMilliseconds); + Interlocked.Add(ref _handleWatch, (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds); _data.LastDbReads = _data.DbChecks; _data.AverageTimeInHandler = _handleWatch / (decimal)_data.ProcessedRequestsCount; diff --git a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs index a452d58a7ca..7d78cc8443f 100644 --- a/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/Pruning/TreeStoreTests.cs @@ -70,7 +70,11 @@ public void Memory_with_one_node_is_288() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode, TreePath.Empty)); + TreePath emptyPath = TreePath.Empty; + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, null)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); + } fullTrieStore.MemoryUsedByDirtyCache.Should().Be( trieNode.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize); } @@ -85,10 +89,16 @@ public void Pruning_off_cache_should_not_change_commit_node() using TrieStore fullTrieStore = CreateTrieStore(); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1234, trieNode); - trieStore.CommitNode(124, new NodeCommitInfo(trieNode2, TreePath.Empty)); - trieStore.CommitNode(11234, new NodeCommitInfo(trieNode3, TreePath.Empty)); + TreePath emptyPath = TreePath.Empty; + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); + } + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); + } fullTrieStore.MemoryUsedByDirtyCache.Should().Be(0); } @@ -101,8 +111,12 @@ public void When_commit_forward_write_flag_if_available() using TrieStore fullTrieStore = CreateTrieStore(kvStore: testMemDb); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode, TreePath.Empty), WriteFlags.LowPriority); - trieStore.FinishBlockCommit(TrieType.State, 1234, trieNode, WriteFlags.LowPriority); + + TreePath emptyPath = TreePath.Empty; + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode, WriteFlags.LowPriority)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); + } if (_scheme == INodeStorage.KeyScheme.HalfPath) { @@ -123,13 +137,13 @@ public void Should_always_announce_block_number_when_pruning_disabled_and_persis using TrieStore fullTrieStore = CreateTrieStore(persistenceStrategy: Archive.Instance); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); fullTrieStore.ReorgBoundaryReached += (_, e) => reorgBoundaryCount += e.BlockNumber; - trieStore.FinishBlockCommit(TrieType.State, 1, trieNode); + trieStore.BeginCommit(TrieType.State, 1, trieNode).Dispose(); reorgBoundaryCount.Should().Be(0); - trieStore.FinishBlockCommit(TrieType.State, 2, trieNode); + trieStore.BeginCommit(TrieType.State, 2, trieNode).Dispose(); reorgBoundaryCount.Should().Be(1); - trieStore.FinishBlockCommit(TrieType.State, 3, trieNode); + trieStore.BeginCommit(TrieType.State, 3, trieNode).Dispose(); reorgBoundaryCount.Should().Be(3); - trieStore.FinishBlockCommit(TrieType.State, 4, trieNode); + trieStore.BeginCommit(TrieType.State, 4, trieNode).Dispose(); reorgBoundaryCount.Should().Be(6); } @@ -142,10 +156,10 @@ public void Should_always_announce_zero_when_not_persisting() using TrieStore fullTrieStore = CreateTrieStore(); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); fullTrieStore.ReorgBoundaryReached += (_, e) => reorgBoundaryCount += e.BlockNumber; - trieStore.FinishBlockCommit(TrieType.State, 1, trieNode); - trieStore.FinishBlockCommit(TrieType.State, 2, trieNode); - trieStore.FinishBlockCommit(TrieType.State, 3, trieNode); - trieStore.FinishBlockCommit(TrieType.State, 4, trieNode); + trieStore.BeginCommit(TrieType.State, 1, trieNode).Dispose(); + trieStore.BeginCommit(TrieType.State, 2, trieNode).Dispose(); + trieStore.BeginCommit(TrieType.State, 3, trieNode).Dispose(); + trieStore.BeginCommit(TrieType.State, 4, trieNode).Dispose(); reorgBoundaryCount.Should().Be(0L); } @@ -189,8 +203,12 @@ public void Memory_with_two_nodes_is_correct() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode1, TreePath.Empty)); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode2, TreePath.Empty)); + TreePath emptyPath = TreePath.Empty; + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, null)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); + } fullTrieStore.MemoryUsedByDirtyCache.Should().Be( trieNode1.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + trieNode2.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize); @@ -206,11 +224,18 @@ public void Memory_with_two_times_two_nodes_is_correct() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(true)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode1, TreePath.Empty)); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode2, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1234, trieNode2); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode3, TreePath.Empty)); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode4, TreePath.Empty)); + TreePath emptyPath = TreePath.Empty; + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); + } + + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); + } // depending on whether the node gets resolved it gives different values here in debugging and run // needs some attention @@ -236,13 +261,21 @@ public void Dispatcher_will_try_to_clear_memory() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(640)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode1, TreePath.Empty)); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode2, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1234, trieNode2); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode3, TreePath.Empty)); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode4, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1235, trieNode2); - trieStore.FinishBlockCommit(TrieType.State, 1236, trieNode2); + + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); + } + + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); + } + + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1236, trieNode2)) { } + fullTrieStore.MemoryUsedByDirtyCache.Should().Be( trieNode1.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + trieNode2.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + @@ -267,11 +300,19 @@ public void Dispatcher_will_try_to_clear_memory_the_soonest_possible() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(512)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode1, TreePath.Empty)); - trieStore.CommitNode(1234, new NodeCommitInfo(trieNode2, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1234, trieNode2); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode3, TreePath.Empty)); - trieStore.CommitNode(1235, new NodeCommitInfo(trieNode4, TreePath.Empty)); + + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1234, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode1)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode2)); + } + + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1235, trieNode2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode3)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode4)); + } + fullTrieStore.MemoryUsedByDirtyCache.Should().Be( trieNode1.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + trieNode2.GetMemorySize(false) + ExpectedPerNodeKeyMemorySize + @@ -287,16 +328,17 @@ public void Dispatcher_will_always_try_to_clear_memory() TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 1024; i++) { - for (int j = 0; j < 1 + i % 3; j++) - { - TrieNode trieNode = new(NodeType.Leaf, new byte[0]); // 192B - trieNode.ResolveKey(NullTrieNodeResolver.Instance, ref emptyPath, true); - trieStore.CommitNode(i, new NodeCommitInfo(trieNode, TreePath.Empty)); - } - TrieNode fakeRoot = new(NodeType.Leaf, new byte[0]); // 192B fakeRoot.ResolveKey(NullTrieNodeResolver.Instance, ref emptyPath, true); - trieStore.FinishBlockCommit(TrieType.State, i, fakeRoot); + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, i, fakeRoot)) + { + for (int j = 0; j < 1 + i % 3; j++) + { + TrieNode trieNode = new(NodeType.Leaf, new byte[0]); // 192B + trieNode.ResolveKey(NullTrieNodeResolver.Instance, ref emptyPath, true); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); + } + } } fullTrieStore.MemoryUsedByDirtyCache.Should().BeLessThan(512 * 2); @@ -318,12 +360,14 @@ public void Dispatcher_will_save_to_db_everything_from_snapshot_blocks() persistenceStrategy: new ConstantInterval(4)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(0, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 0, a); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.FinishBlockCommit(TrieType.State, 3, a); - trieStore.FinishBlockCommit(TrieType.State, 4, a); + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 0, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } storage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -342,11 +386,13 @@ public void Stays_in_memory_until_persisted() using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new MemoryLimit(16.MB())); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(0, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 0, a); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.FinishBlockCommit(TrieType.State, 3, a); + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 0, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } // <- do not persist in this test storage.Get(null, TreePath.Empty, a.Keccak).Should().BeNull(); @@ -381,16 +427,18 @@ public void Will_get_persisted_on_snapshot_if_referenced() ); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.FinishBlockCommit(TrieType.State, 3, a); - trieStore.FinishBlockCommit(TrieType.State, 4, a); - trieStore.FinishBlockCommit(TrieType.State, 5, a); - trieStore.FinishBlockCommit(TrieType.State, 6, a); - trieStore.FinishBlockCommit(TrieType.State, 7, a); - trieStore.FinishBlockCommit(TrieType.State, 8, a); + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } storage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -416,17 +464,21 @@ public void Will_not_get_dropped_on_snapshot_if_unreferenced_in_later_blocks() IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.FinishBlockCommit(TrieType.State, 3, a); - trieStore.FinishBlockCommit(TrieType.State, 4, a); - trieStore.FinishBlockCommit(TrieType.State, 5, a); - trieStore.FinishBlockCommit(TrieType.State, 6, a); - trieStore.CommitNode(7, new NodeCommitInfo(b, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 7, b); - trieStore.FinishBlockCommit(TrieType.State, 8, b); + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 7, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } nodeStorage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -451,17 +503,21 @@ public void Will_get_dropped_on_snapshot_if_it_was_a_transient_node() IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.CommitNode(3, new NodeCommitInfo(b, TreePath.Empty)); // <- new root - trieStore.FinishBlockCommit(TrieType.State, 3, b); - trieStore.FinishBlockCommit(TrieType.State, 4, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 5, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 6, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 7, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 8, b); // should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 3, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); // <- new root + } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, b)) { } // should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, b)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, b)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, b)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, b)) { } memDb[a.Keccak!.Bytes].Should().BeNull(); fullTrieStore.IsNodeCached(null, TreePath.Empty, a.Keccak).Should().BeTrue(); @@ -544,21 +600,24 @@ public void Will_store_storage_on_snapshot() persistenceStrategy: new ConstantInterval(4)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - fullTrieStore.GetTrieStore(TestItem.KeccakA) - .CommitNode(1, new NodeCommitInfo(storage1, TreePath.Empty)); - fullTrieStore.GetTrieStore(TestItem.KeccakA) - .FinishBlockCommit(TrieType.Storage, 1, storage1); - trieStore.FinishBlockCommit(TrieType.Storage, 1, storage1); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.FinishBlockCommit(TrieType.State, 3, a); - trieStore.FinishBlockCommit(TrieType.State, 4, a); - trieStore.FinishBlockCommit(TrieType.State, 5, a); - trieStore.FinishBlockCommit(TrieType.State, 6, a); - trieStore.FinishBlockCommit(TrieType.State, 7, a); - trieStore.FinishBlockCommit(TrieType.State, 8, a); + + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + using (ICommitter committer = fullTrieStore.GetTrieStore(TestItem.KeccakA).BeginCommit(TrieType.Storage, 1, storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, a)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + } + + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, a)) { } asStorage.Get(null, TreePath.Empty, a.Keccak).Should().NotBeNull(); asStorage.Get(TestItem.KeccakA, TreePath.Empty, storage1.Keccak).Should().NotBeNull(); @@ -591,19 +650,27 @@ public void Will_drop_transient_storage() IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.CommitNode(1, new NodeCommitInfo(storage1, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.Storage, 1, storage1); - trieStore.FinishBlockCommit(TrieType.State, 1, a); - trieStore.FinishBlockCommit(TrieType.State, 2, a); - trieStore.CommitNode(3, new NodeCommitInfo(b, TreePath.Empty)); // <- new root - trieStore.FinishBlockCommit(TrieType.State, 3, b); - trieStore.FinishBlockCommit(TrieType.State, 4, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 5, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 6, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 7, b); // should be 'a' to test properly - trieStore.FinishBlockCommit(TrieType.State, 8, b); // should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + + using (ICommitter committer = trieStore.BeginCommit(TrieType.Storage, 1, storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } + + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 1, a)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, a)) { } + + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 2, b)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); // <- new root + } + + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, b)) { } // Should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, b)) { } // Should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, b)) { } // Should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, b)) { } // Should be 'a' to test properly + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, b)) { } // Should be 'a' to test properly memDb[a.Keccak!.Bytes].Should().BeNull(); memDb[storage1.Keccak!.Bytes].Should().BeNull(); @@ -653,27 +720,32 @@ public void Will_combine_same_storage() IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.FinishBlockCommit(TrieType.State, 0, null); + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 0, null)) { } + + using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage1Nib))).BeginCommit(TrieType.Storage, 1, storage1)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage1)); + } - IScopedTrieStore storageTrieStore = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage1Nib))); - storageTrieStore.CommitNode(1, new NodeCommitInfo(storage1, TreePath.Empty)); - storageTrieStore.FinishBlockCommit(TrieType.Storage, 1, storage1); + using (ICommitter committer = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage2Nib))).BeginCommit(TrieType.Storage, 1, storage2)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(storage2)); + } - storageTrieStore = fullTrieStore.GetTrieStore(new Hash256(Nibbles.ToBytes(storage2Nib))); - storageTrieStore.CommitNode(1, new NodeCommitInfo(storage2, TreePath.Empty)); - storageTrieStore.FinishBlockCommit(TrieType.Storage, 1, storage2); + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, 1, branch)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(a)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(b)); + committer.CommitNode(ref emptyPath, new NodeCommitInfo(branch)); + } - trieStore.CommitNode(1, new NodeCommitInfo(a, TreePath.Empty)); - trieStore.CommitNode(1, new NodeCommitInfo(b, TreePath.Empty)); - trieStore.CommitNode(1, new NodeCommitInfo(branch, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 1, branch); - trieStore.FinishBlockCommit(TrieType.State, 2, branch); - trieStore.FinishBlockCommit(TrieType.State, 3, branch); - trieStore.FinishBlockCommit(TrieType.State, 4, branch); - trieStore.FinishBlockCommit(TrieType.State, 5, branch); - trieStore.FinishBlockCommit(TrieType.State, 6, branch); - trieStore.FinishBlockCommit(TrieType.State, 7, branch); - trieStore.FinishBlockCommit(TrieType.State, 8, branch); + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 2, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 3, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 4, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 5, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 6, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 7, branch)) { } + using (ICommitter _ = trieStore.BeginCommit(TrieType.State, 8, branch)) { } storage.Get(null, TreePath.FromNibble(new byte[] { 0 }), a.Keccak).Should().NotBeNull(); storage.Get(new Hash256(Nibbles.ToBytes(storage1Nib)), TreePath.Empty, storage1.Keccak).Should().NotBeNull(); @@ -702,7 +774,10 @@ public async Task Read_only_trie_store_is_allowing_many_thread_to_work_with_the_ IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); TreePath emptyPath = TreePath.Empty; trieNode.ResolveKey(trieStore, ref emptyPath, false); - trieStore.CommitNode(1, new NodeCommitInfo(trieNode, TreePath.Empty)); + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, trieNode)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(trieNode)); + } if (beThreadSafe) { @@ -755,8 +830,12 @@ public void ReadOnly_store_returns_copies(bool pruning) using TrieStore fullTrieStore = CreateTrieStore(pruningStrategy: new TestPruningStrategy(pruning)); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); - trieStore.CommitNode(0, new NodeCommitInfo(node, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 0, node); + + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, node)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); + } + var originalNode = trieStore.FindCachedOrUnknown(TreePath.Empty, node.Keccak); IReadOnlyTrieStore readOnlyTrieStore = fullTrieStore.AsReadOnly(); @@ -774,7 +853,7 @@ public void ReadOnly_store_returns_copies(bool pruning) readOnlyNode.Key?.ToString().Should().Be(originalNode.Key?.ToString()); } - private long ExpectedPerNodeKeyMemorySize => _scheme == INodeStorage.KeyScheme.Hash ? 0 : TrieStore.TrieStoreDirtyNodesCache.Key.MemoryUsage; + private long ExpectedPerNodeKeyMemorySize => _scheme == INodeStorage.KeyScheme.Hash ? 0 : TrieStoreDirtyNodesCache.Key.MemoryUsage; [Test] public void After_commit_should_have_has_root() @@ -807,12 +886,15 @@ public async Task Will_RemovePastKeys_OnSnapshot() persistenceStrategy: No.Persistence); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); + TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, i, node); + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); + } // Pruning is done in background await Task.Delay(TimeSpan.FromMilliseconds(10)); @@ -842,12 +924,15 @@ public async Task Will_Trigger_ReorgBoundaryEvent_On_Prune() fullTrieStore.ReorgBoundaryReached += (sender, reached) => reorgBoundary = reached.BlockNumber; IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); + TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, i, node); + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); + } if (i > 4) { @@ -875,12 +960,15 @@ public async Task Will_Not_RemovePastKeys_OnSnapshot_DuringFullPruning() persistenceStrategy: isPruningPersistenceStrategy); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); + TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i], new byte[2]); - trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, i, node); + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, i, node)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); + } // Pruning is done in background await Task.Delay(TimeSpan.FromMilliseconds(10)); @@ -902,13 +990,15 @@ public async Task Will_NotRemove_ReCommittedNode() persistenceStrategy: No.Persistence); IScopedTrieStore trieStore = fullTrieStore.GetTrieStore(null); + TreePath emptyPath = TreePath.Empty; for (int i = 0; i < 64; i++) { TrieNode node = new(NodeType.Leaf, TestItem.Keccaks[i % 4], new byte[2]); - trieStore.CommitNode(i, new NodeCommitInfo(node, TreePath.Empty)); - node = trieStore.FindCachedOrUnknown(TreePath.Empty, node.Keccak); - trieStore.FinishBlockCommit(TrieType.State, i, node); + using (ICommitter committer = trieStore.BeginCommit(TrieType.State, i, node)) + { + committer.CommitNode(ref emptyPath, new NodeCommitInfo(node)); + } // Pruning is done in background await Task.Delay(TimeSpan.FromMilliseconds(10)); diff --git a/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs b/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs index 6ee564db88a..001be11d7d7 100644 --- a/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs +++ b/src/Nethermind/Nethermind.Trie.Test/TrieNodeTests.cs @@ -933,15 +933,20 @@ public void Rlp_is_cloned_when_cloning() TreePath emptyPath = TreePath.Empty; leaf1.ResolveKey(trieStore, ref emptyPath, false); leaf1.Seal(); - trieStore.CommitNode(0, new NodeCommitInfo(leaf1, TreePath.Empty)); TrieNode leaf2 = new(NodeType.Leaf); leaf2.Key = Bytes.FromHexString("abd"); leaf2.Value = new byte[222]; leaf2.ResolveKey(trieStore, ref emptyPath, false); leaf2.Seal(); - trieStore.CommitNode(0, new NodeCommitInfo(leaf2, TreePath.Empty)); - trieStore.FinishBlockCommit(TrieType.State, 0, leaf2); + + TreePath path = TreePath.Empty; + + using (ICommitter? committer = trieStore.BeginCommit(TrieType.State, 0, leaf2)) + { + committer.CommitNode(ref path, new NodeCommitInfo(leaf1)); + committer.CommitNode(ref path, new NodeCommitInfo(leaf2)); + } TrieNode trieNode = new(NodeType.Branch); trieNode.SetChild(1, leaf1); diff --git a/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs b/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs index ef0706a09cf..99bff648354 100644 --- a/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs +++ b/src/Nethermind/Nethermind.Trie/BatchedTrieVisitor.cs @@ -142,11 +142,12 @@ public void Start( try { + // TODO: .Net 9 stackalloc Task[]? tasks = Enumerable.Range(0, trieVisitContext.MaxDegreeOfParallelism) - .Select((_) => Task.Run(BatchedThread)) + .Select(_ => Task.Run(BatchedThread)) .ToArray(); - Task.WhenAll(tasks).Wait(); + Task.WaitAll(tasks); } catch (Exception) { diff --git a/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs b/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs index d9a1769c5aa..5b23ba6ad92 100644 --- a/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/CachedTrieStore.cs @@ -20,46 +20,25 @@ public class CachedTrieStore(IScopedTrieStore @base) : IScopedTrieStore { private readonly NonBlocking.ConcurrentDictionary<(TreePath path, Hash256 hash), TrieNode> _cachedNode = new(); - public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) - { - return _cachedNode.GetOrAdd((path, hash), (key) => @base.FindCachedOrUnknown(key.path, key.hash)); - } + public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) => + _cachedNode.GetOrAdd((path, hash), (key) => @base.FindCachedOrUnknown(key.path, key.hash)); - public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return @base.LoadRlp(in path, hash, flags); - } + public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => + @base.LoadRlp(in path, hash, flags); - public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return @base.TryLoadRlp(in path, hash, flags); - } + public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => + @base.TryLoadRlp(in path, hash, flags); - public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) - { + public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) => throw new InvalidOperationException("unsupported"); - } public INodeStorage.KeyScheme Scheme => @base.Scheme; - public void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) - { - @base.CommitNode(blockNumber, nodeCommitInfo, writeFlags); - } + public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + @base.BeginCommit(trieType, blockNumber, root, writeFlags); - public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) - { - @base.FinishBlockCommit(trieType, blockNumber, root, writeFlags); - } + public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => @base.IsPersisted(in path, in keccak); - public bool IsPersisted(in TreePath path, in ValueHash256 keccak) - { - return @base.IsPersisted(in path, in keccak); - } - - public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - @base.Set(in path, in keccak, rlp); - } + public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) => @base.Set(in path, in keccak, rlp); } diff --git a/src/Nethermind/Nethermind.Trie/NodeCommitInfo.cs b/src/Nethermind/Nethermind.Trie/NodeCommitInfo.cs index bf85db2709d..265d81631fb 100644 --- a/src/Nethermind/Nethermind.Trie/NodeCommitInfo.cs +++ b/src/Nethermind/Nethermind.Trie/NodeCommitInfo.cs @@ -1,37 +1,35 @@ +using System.Diagnostics.CodeAnalysis; + namespace Nethermind.Trie { public readonly struct NodeCommitInfo { public NodeCommitInfo( - TrieNode node, - in TreePath path + TrieNode node ) { ChildPositionAtParent = 0; Node = node; - Path = path; NodeParent = null; } public NodeCommitInfo( TrieNode node, TrieNode nodeParent, - in TreePath path, int childPositionAtParent) { ChildPositionAtParent = childPositionAtParent; Node = node; - Path = path; NodeParent = nodeParent; } public TrieNode? Node { get; } - public readonly TreePath Path; public TrieNode? NodeParent { get; } public int ChildPositionAtParent { get; } + [MemberNotNullWhen(false, nameof(Node))] public bool IsEmptyBlockMarker => Node is null; public bool IsRoot => !IsEmptyBlockMarker && NodeParent is null; diff --git a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs index 3ab8c9d1127..88b443a3983 100644 --- a/src/Nethermind/Nethermind.Trie/PatriciaTree.cs +++ b/src/Nethermind/Nethermind.Trie/PatriciaTree.cs @@ -8,13 +8,14 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; using Nethermind.Core; using Nethermind.Core.Buffers; -using Nethermind.Core.Cpu; +using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Logging; @@ -39,10 +40,6 @@ public class PatriciaTree public TrieType TrieType { get; init; } private Stack? _nodeStack; - - private ConcurrentQueue? _commitExceptions; - private ConcurrentQueue? _currentCommit; - public IScopedTrieStore TrieStore { get; } public ICappedArrayPool? _bufferPool; @@ -56,6 +53,9 @@ public class PatriciaTree public TrieNode? RootRef { get; set; } + // Used to estimate if parallelization is needed during commit + private long _writeBeforeCommit = 0; + /// /// Only used in EthereumTests /// @@ -138,37 +138,30 @@ public void Commit(long blockNumber, bool skipRoot = false, WriteFlags writeFlag ThrowReadOnlyTrieException(); } - if (RootRef is not null && RootRef.IsDirty) + int maxLevelForConcurrentCommit = _writeBeforeCommit switch { - Commit(new NodeCommitInfo(RootRef, TreePath.Empty), skipSelf: skipRoot); - while (TryDequeueCommit(out NodeCommitInfo node)) - { - if (_logger.IsTrace) Trace(blockNumber, node); - TrieStore.CommitNode(blockNumber, node, writeFlags: writeFlags); - } - - // reset objects - TreePath path = TreePath.Empty; - RootRef!.ResolveKey(TrieStore, ref path, true, bufferPool: _bufferPool); - SetRootHash(RootRef.Keccak!, true); - } - - TrieStore.FinishBlockCommit(TrieType, blockNumber, RootRef, writeFlags); + > 64 * 16 => 1, // we separate at two top levels + > 64 => 0, // we separate at top level + _ => -1 + }; - if (_logger.IsDebug) Debug(blockNumber); + _writeBeforeCommit = 0; - bool TryDequeueCommit(out NodeCommitInfo value) + using (ICommitter committer = TrieStore.BeginCommit(TrieType, blockNumber, RootRef, writeFlags)) { - Unsafe.SkipInit(out value); - return _currentCommit?.TryDequeue(out value) ?? false; - } + if (RootRef is not null && RootRef.IsDirty) + { + TreePath path = TreePath.Empty; + Commit(committer, ref path, new NodeCommitInfo(RootRef), skipSelf: skipRoot, maxLevelForConcurrentCommit: maxLevelForConcurrentCommit); - [MethodImpl(MethodImplOptions.NoInlining)] - void Trace(long blockNumber, in NodeCommitInfo node) - { - _logger.Trace($"Committing {node} in {blockNumber}"); + // reset objects + RootRef!.ResolveKey(TrieStore, ref path, true, bufferPool: _bufferPool); + SetRootHash(RootRef.Keccak!, true); + } } + if (_logger.IsDebug) Debug(blockNumber); + [MethodImpl(MethodImplOptions.NoInlining)] void Debug(long blockNumber) { @@ -176,7 +169,7 @@ void Debug(long blockNumber) } } - private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) + private void Commit(ICommitter committer, ref TreePath path, NodeCommitInfo nodeCommitInfo, int maxLevelForConcurrentCommit, bool skipSelf = false) { if (!_allowCommits) { @@ -184,18 +177,18 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) } TrieNode node = nodeCommitInfo.Node; - TreePath path = nodeCommitInfo.Path; if (node!.IsBranch) { - // idea from EthereumJ - testing parallel branches - if (!_parallelBranches || !nodeCommitInfo.IsRoot) + if (path.Length > maxLevelForConcurrentCommit) { for (int i = 0; i < 16; i++) { if (node.IsChildDirty(i)) { - TreePath childPath = node.GetChildPath(nodeCommitInfo.Path, i); - Commit(new NodeCommitInfo(node.GetChildWithChildPath(TrieStore, ref childPath, i)!, node, childPath, i)); + path.AppendMut(i); + TrieNode childNode = node.GetChildWithChildPath(TrieStore, ref path, i); + Commit(committer, ref path, new NodeCommitInfo(childNode!, node, i), maxLevelForConcurrentCommit); + path.TruncateOne(); } else { @@ -208,13 +201,33 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) } else { - List nodesToCommit = new(16); + Task CreateTaskForPath(TreePath childPath, TrieNode childNode, int idx) => Task.Run(() => + { + Commit(committer, ref childPath, new NodeCommitInfo(childNode!, node, idx), maxLevelForConcurrentCommit); + committer.ReturnConcurrencyQuota(); + }); + + // TODO: .Net 9 stackalloc + ArrayPoolList? childTasks = null; + for (int i = 0; i < 16; i++) { if (node.IsChildDirty(i)) { - TreePath childPath = node.GetChildPath(nodeCommitInfo.Path, i); - nodesToCommit.Add(new NodeCommitInfo(node.GetChildWithChildPath(TrieStore, ref childPath, i)!, node, childPath, i)); + if (i < 15 && committer.CanSpawnTask()) + { + childTasks ??= new ArrayPoolList(15); + TreePath childPath = path.Append(i); + TrieNode childNode = node.GetChildWithChildPath(TrieStore, ref childPath, i); + childTasks.Add(CreateTaskForPath(childPath, childNode, i)); + } + else + { + path.AppendMut(i); + TrieNode childNode = node.GetChildWithChildPath(TrieStore, ref path, i); + Commit(committer, ref path, new NodeCommitInfo(childNode!, node, i), maxLevelForConcurrentCommit); + path.TruncateOne(); + } } else { @@ -225,39 +238,17 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) } } - if (nodesToCommit.Count >= 4) - { - ClearExceptions(); - Parallel.For(0, nodesToCommit.Count, RuntimeInformation.ParallelOptionsLogicalCores, i => - { - try - { - Commit(nodesToCommit[i]); - } - catch (Exception e) - { - AddException(e); - } - }); - - if (WereExceptions()) - { - ThrowAggregateExceptions(); - } - } - else + if (childTasks is not null) { - for (int i = 0; i < nodesToCommit.Count; i++) - { - Commit(nodesToCommit[i]); - } + Task.WaitAll(childTasks.ToArray()); + childTasks.Dispose(); } } } else if (node.NodeType == NodeType.Extension) { - TreePath childPath = node.GetChildPath(nodeCommitInfo.Path, 0); - TrieNode extensionChild = node.GetChildWithChildPath(TrieStore, ref childPath, 0); + int previousPathLength = node.AppendChildPath(ref path, 0); + TrieNode extensionChild = node.GetChildWithChildPath(TrieStore, ref path, 0); if (extensionChild is null) { ThrowInvalidExtension(); @@ -265,12 +256,13 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) if (extensionChild.IsDirty) { - Commit(new NodeCommitInfo(extensionChild, node, childPath, 0)); + Commit(committer, ref path, new NodeCommitInfo(extensionChild, node, 0), maxLevelForConcurrentCommit); } else { if (_logger.IsTrace) TraceExtensionSkip(extensionChild); } + path.TruncateMut(previousPathLength); } node.ResolveKey(TrieStore, ref path, nodeCommitInfo.IsRoot, bufferPool: _bufferPool); @@ -280,7 +272,7 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) { if (!skipSelf) { - EnqueueCommit(nodeCommitInfo); + committer.CommitNode(ref path, nodeCommitInfo); } } else @@ -288,37 +280,6 @@ private void Commit(NodeCommitInfo nodeCommitInfo, bool skipSelf = false) if (_logger.IsTrace) TraceSkipInlineNode(node); } - void EnqueueCommit(in NodeCommitInfo value) - { - ConcurrentQueue queue = Volatile.Read(ref _currentCommit); - // Allocate queue if first commit made - queue ??= CreateQueue(ref _currentCommit); - queue.Enqueue(value); - } - - void ClearExceptions() => _commitExceptions?.Clear(); - bool WereExceptions() => _commitExceptions?.IsEmpty == false; - - void AddException(Exception value) - { - ConcurrentQueue queue = Volatile.Read(ref _commitExceptions); - // Allocate queue if first exception thrown - queue ??= CreateQueue(ref _commitExceptions); - queue.Enqueue(value); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - ConcurrentQueue CreateQueue(ref ConcurrentQueue queueRef) - { - ConcurrentQueue queue = new(); - ConcurrentQueue current = Interlocked.CompareExchange(ref queueRef, queue, null); - return (current is null) ? queue : current; - } - - [DoesNotReturn] - [StackTraceHidden] - void ThrowAggregateExceptions() => throw new AggregateException(_commitExceptions); - [DoesNotReturn] [StackTraceHidden] static void ThrowInvalidExtension() => throw new InvalidOperationException("An attempt to store an extension without a child."); @@ -493,6 +454,8 @@ public virtual void Set(ReadOnlySpan rawKey, in CappedArray value) ThrowNonConcurrentWrites(); } + _writeBeforeCommit++; + try { int nibblesCount = 2 * rawKey.Length; diff --git a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs index efb9d242748..82d83a3e8f3 100644 --- a/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/PreCachedTrieStore.cs @@ -34,14 +34,9 @@ public void Dispose() _inner.Dispose(); } - public void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) + public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) { - _inner.CommitNode(blockNumber, address, in nodeCommitInfo, writeFlags); - } - - public void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) - { - _inner.FinishBlockCommit(trieType, blockNumber, address, root, writeFlags); + return _inner.BeginCommit(trieType, blockNumber, address, root, writeFlags); } public bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak) diff --git a/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPath.cs b/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPath.cs new file mode 100644 index 00000000000..09138b330be --- /dev/null +++ b/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPath.cs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.InteropServices; +using Nethermind.Core.Crypto; + +namespace Nethermind.Trie.Pruning; + +[StructLayout(LayoutKind.Auto)] +internal readonly struct HashAndTinyPath : IEquatable +{ + public readonly ValueHash256 addr; + public readonly TinyTreePath path; + + public HashAndTinyPath(Hash256? hash, in TinyTreePath path) + { + addr = hash ?? default; + this.path = path; + } + public HashAndTinyPath(in ValueHash256 hash, in TinyTreePath path) + { + addr = hash; + this.path = path; + } + + public bool Equals(HashAndTinyPath other) => addr == other.addr && path.Equals(in other.path); + public override bool Equals(object? obj) => obj is HashAndTinyPath other && Equals(other); + public override int GetHashCode() + { + var addressHash = addr != default ? addr.GetHashCode() : 1; + return path.GetHashCode() ^ addressHash; + } +} diff --git a/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPathAndHash.cs b/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPathAndHash.cs new file mode 100644 index 00000000000..56f3c2baff0 --- /dev/null +++ b/src/Nethermind/Nethermind.Trie/Pruning/HashAndTinyPathAndHash.cs @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Runtime.InteropServices; +using Nethermind.Core.Crypto; + +namespace Nethermind.Trie.Pruning; + +[StructLayout(LayoutKind.Auto)] +internal readonly struct HashAndTinyPathAndHash : IEquatable +{ + public readonly ValueHash256 hash; + public readonly TinyTreePath path; + public readonly ValueHash256 valueHash; + + public HashAndTinyPathAndHash(Hash256? hash, in TinyTreePath path, in ValueHash256 valueHash) + { + this.hash = hash ?? default; + this.path = path; + this.valueHash = valueHash; + } + public HashAndTinyPathAndHash(in ValueHash256 hash, in TinyTreePath path, in ValueHash256 valueHash) + { + this.hash = hash; + this.path = path; + this.valueHash = valueHash; + } + + public bool Equals(HashAndTinyPathAndHash other) => hash == other.hash && path.Equals(in other.path) && valueHash.Equals(in other.valueHash); + public override bool Equals(object? obj) => obj is HashAndTinyPath other && Equals(other); + public override int GetHashCode() + { + var hashHash = hash != default ? hash.GetHashCode() : 1; + return valueHash.GetChainedHashCode((uint)path.GetHashCode()) ^ hashHash; + } +} diff --git a/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs index f8d2384fd0e..3dec5d49e25 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/IScopedTrieStore.cs @@ -13,11 +13,7 @@ namespace Nethermind.Trie.Pruning; /// public interface IScopedTrieStore : ITrieNodeResolver { - // TODO: Commit and FinishBlockCommit is unnecessary. Geth just compile the changes and return it in a batch, - // which get committed in a single call. - void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None); - - void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None); + ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None); // Only used by snap provider, so ValueHash instead of Hash bool IsPersisted(in TreePath path, in ValueHash256 keccak); @@ -25,3 +21,11 @@ public interface IScopedTrieStore : ITrieNodeResolver // Used for trie node recovery void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp); } + +public interface ICommitter : IDisposable +{ + void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo); + + bool CanSpawnTask() => false; + void ReturnConcurrencyQuota() { } +} diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs index a7ec0699ec1..f9f2f59d55e 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ITrieStore.cs @@ -13,10 +13,6 @@ namespace Nethermind.Trie.Pruning /// public interface ITrieStore : IDisposable { - void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None); - - void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None); - bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak); IReadOnlyTrieStore AsReadOnly(INodeStorage? keyValueStore = null); @@ -37,6 +33,7 @@ public interface ITrieStore : IDisposable byte[]? LoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None); byte[]? TryLoadRlp(Hash256? address, in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None); INodeStorage.KeyScheme Scheme { get; } + ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags); } public interface IPruningTrieStore diff --git a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs index 4982d7166b8..585050f7150 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/NullTrieStore.cs @@ -15,27 +15,27 @@ private NullTrieStore() { } public static NullTrieStore Instance { get; } = new(); - public void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags flags = WriteFlags.None) { } - - public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags flags = WriteFlags.None) { } - public TrieNode FindCachedOrUnknown(in TreePath treePath, Hash256 hash) => new(NodeType.Unknown, hash); - public byte[] LoadRlp(in TreePath treePath, Hash256 hash, ReadFlags flags = ReadFlags.None) => Array.Empty(); + public byte[] LoadRlp(in TreePath treePath, Hash256 hash, ReadFlags flags = ReadFlags.None) => []; + + public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => []; - public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => Array.Empty(); + public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => new NullCommitter(); public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => true; - public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - } + public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) { } - public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256 storageRoot) - { - return this; - } + public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256 storageRoot) => this; public INodeStorage.KeyScheme Scheme => INodeStorage.KeyScheme.HalfPath; + + internal class NullCommitter : ICommitter + { + public void Dispose() { } + + public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) { } + } } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs index 3152aad59cd..4bb2c8d537f 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ReadOnlyTrieStore.cs @@ -12,36 +12,25 @@ namespace Nethermind.Trie.Pruning /// /// Safe to be reused for the same wrapped store. /// - public class ReadOnlyTrieStore : IReadOnlyTrieStore + public class ReadOnlyTrieStore(TrieStore trieStore, INodeStorage? readOnlyStore) : IReadOnlyTrieStore { - private readonly TrieStore _trieStore; - private readonly INodeStorage? _readOnlyStore; + private readonly TrieStore _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); public INodeStorage.KeyScheme Scheme => _trieStore.Scheme; - public ReadOnlyTrieStore(TrieStore trieStore, INodeStorage? readOnlyStore) - { - _trieStore = trieStore ?? throw new ArgumentNullException(nameof(trieStore)); - _readOnlyStore = readOnlyStore; - } - public TrieNode FindCachedOrUnknown(Hash256? address, in TreePath treePath, Hash256 hash) => _trieStore.FindCachedOrUnknown(address, treePath, hash, true); public byte[] LoadRlp(Hash256? address, in TreePath treePath, Hash256 hash, ReadFlags flags) => - _trieStore.LoadRlp(address, treePath, hash, _readOnlyStore, flags); + _trieStore.LoadRlp(address, treePath, hash, readOnlyStore, flags); public byte[]? TryLoadRlp(Hash256? address, in TreePath treePath, Hash256 hash, ReadFlags flags) => - _trieStore.TryLoadRlp(address, treePath, hash, _readOnlyStore, flags); + _trieStore.TryLoadRlp(address, treePath, hash, readOnlyStore, flags); public bool IsPersisted(Hash256? address, in TreePath path, in ValueHash256 keccak) => _trieStore.IsPersisted(address, path, keccak); - public IReadOnlyTrieStore AsReadOnly(INodeStorage nodeStore) - { - return new ReadOnlyTrieStore(_trieStore, nodeStore); - } - - public void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nodeCommitInfo, WriteFlags flags = WriteFlags.None) { } + public IReadOnlyTrieStore AsReadOnly(INodeStorage nodeStore) => new ReadOnlyTrieStore(_trieStore, nodeStore); - public void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags flags = WriteFlags.None) { } + public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) => + new NullTrieStore.NullCommitter(); public event EventHandler ReorgBoundaryReached { @@ -51,72 +40,36 @@ public event EventHandler ReorgBoundaryReached public IReadOnlyKeyValueStore TrieNodeRlpStore => _trieStore.TrieNodeRlpStore; - public void Set(Hash256? address, in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - } + public void Set(Hash256? address, in TreePath path, in ValueHash256 keccak, byte[] rlp) { } - public IScopedTrieStore GetTrieStore(Hash256? address) - { - return new ScopedReadOnlyTrieStore(this, address); - } + public IScopedTrieStore GetTrieStore(Hash256? address) => new ScopedReadOnlyTrieStore(this, address); - public bool HasRoot(Hash256 stateRoot) - { - return _trieStore.HasRoot(stateRoot); - } + public bool HasRoot(Hash256 stateRoot) => _trieStore.HasRoot(stateRoot); public void Dispose() { } - private class ScopedReadOnlyTrieStore : IScopedTrieStore + private class ScopedReadOnlyTrieStore(ReadOnlyTrieStore fullTrieStore, Hash256? address) : IScopedTrieStore { - private readonly ReadOnlyTrieStore _trieStoreImplementation; - private readonly Hash256? _address; - - public ScopedReadOnlyTrieStore(ReadOnlyTrieStore fullTrieStore, Hash256? address) - { - _trieStoreImplementation = fullTrieStore; - _address = address; - } - - public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) - { - return _trieStoreImplementation.FindCachedOrUnknown(_address, path, hash); - } - - public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return _trieStoreImplementation.LoadRlp(_address, path, hash, flags); - } - - public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return _trieStoreImplementation.TryLoadRlp(_address, path, hash, flags); - } - - public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) - { - if (address == _address) return this; - return new ScopedReadOnlyTrieStore(_trieStoreImplementation, address); - } - - public INodeStorage.KeyScheme Scheme => _trieStoreImplementation.Scheme; - - public void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) - { - } - - public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) - { - } - - public bool IsPersisted(in TreePath path, in ValueHash256 keccak) - { - return _trieStoreImplementation.IsPersisted(_address, path, in keccak); - } - - public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - } + public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) => + fullTrieStore.FindCachedOrUnknown(address, path, hash); + + public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => + fullTrieStore.LoadRlp(address, path, hash, flags); + + public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => fullTrieStore.TryLoadRlp(address, path, hash, flags); + + public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address1) => + address1 == address ? this : new ScopedReadOnlyTrieStore(fullTrieStore, address1); + + public INodeStorage.KeyScheme Scheme => fullTrieStore.Scheme; + + public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + new NullTrieStore.NullCommitter(); + + public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => + fullTrieStore.IsPersisted(address, path, in keccak); + + public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) { } } } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs index 02dc3e4d85e..650ee399086 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/ScopedTrieStore.cs @@ -6,57 +6,28 @@ namespace Nethermind.Trie.Pruning; -public sealed class ScopedTrieStore : IScopedTrieStore +public sealed class ScopedTrieStore(ITrieStore fullTrieStore, Hash256? address) : IScopedTrieStore { - private readonly ITrieStore _trieStoreImplementation; - private readonly Hash256? _address; - - public ScopedTrieStore(ITrieStore fullTrieStore, Hash256? address) - { - _trieStoreImplementation = fullTrieStore; - _address = address; - } - - public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) - { - return _trieStoreImplementation.FindCachedOrUnknown(_address, path, hash); - } - - public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return _trieStoreImplementation.LoadRlp(_address, path, hash, flags); - } - - public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) - { - return _trieStoreImplementation.TryLoadRlp(_address, path, hash, flags); - } - - public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address) - { - if (address == _address) return this; - return new ScopedTrieStore(_trieStoreImplementation, address); - } - - public INodeStorage.KeyScheme Scheme => _trieStoreImplementation.Scheme; - - public void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) - { - _trieStoreImplementation.CommitNode(blockNumber, _address, nodeCommitInfo, writeFlags); - } - - public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) - { - _trieStoreImplementation.FinishBlockCommit(trieType, blockNumber, _address, root, writeFlags); - } - - public bool IsPersisted(in TreePath path, in ValueHash256 keccak) - { - return _trieStoreImplementation.IsPersisted(_address, path, in keccak); - } - - public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - _trieStoreImplementation.Set(_address, path, keccak, rlp); - } + public TrieNode FindCachedOrUnknown(in TreePath path, Hash256 hash) => + fullTrieStore.FindCachedOrUnknown(address, path, hash); + + public byte[]? LoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => + fullTrieStore.LoadRlp(address, path, hash, flags); + + public byte[]? TryLoadRlp(in TreePath path, Hash256 hash, ReadFlags flags = ReadFlags.None) => + fullTrieStore.TryLoadRlp(address, path, hash, flags); + + public ITrieNodeResolver GetStorageTrieNodeResolver(Hash256? address1) => + address1 == address ? this : new ScopedTrieStore(fullTrieStore, address1); + + public INodeStorage.KeyScheme Scheme => fullTrieStore.Scheme; + + public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + fullTrieStore.BeginCommit(trieType, blockNumber, address, root, writeFlags); + + public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => + fullTrieStore.IsPersisted(address, path, in keccak); + + public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) => + fullTrieStore.Set(address, path, keccak, rlp); } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs b/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs index ba768f5844a..36306f16241 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TreePath.cs @@ -27,7 +27,7 @@ public struct TreePath : IEquatable public const int MemorySize = 36; public ValueHash256 Path; - public static TreePath Empty => new TreePath(); + public static TreePath Empty => new(); public readonly Span Span => Path.BytesAsSpan; @@ -38,7 +38,7 @@ public TreePath(in ValueHash256 path, int length) Length = length; } - public int Length { get; internal set; } + public int Length { get; private set; } public static TreePath FromPath(ReadOnlySpan pathHash) { @@ -46,7 +46,7 @@ public static TreePath FromPath(ReadOnlySpan pathHash) if (pathHash.Length == 32) return new TreePath(new ValueHash256(pathHash), 64); // Some of the test passes path directly to PatriciaTrie, but its not 32 byte. - TreePath newTreePath = new TreePath(); + TreePath newTreePath = new(); pathHash.CopyTo(newTreePath.Span); newTreePath.Length = pathHash.Length * 2; return newTreePath; diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs index 4ef602fa244..6ae6ca164fc 100644 --- a/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs +++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStore.cs @@ -3,21 +3,17 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Nethermind.Core; -using Nethermind.Core.Caching; using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Logging; -using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; namespace Nethermind.Trie.Pruning { @@ -27,469 +23,6 @@ namespace Nethermind.Trie.Pruning /// public class TrieStore : ITrieStore, IPruningTrieStore { - internal class TrieStoreDirtyNodesCache - { - private readonly TrieStore _trieStore; - private int _count = 0; - private readonly ILogger _logger; - private readonly bool _storeByHash; - private readonly ConcurrentDictionary _byKeyObjectCache; - private readonly ConcurrentDictionary _byHashObjectCache; - - // Track some of the persisted path hash. Used to be able to remove keys when it is replaced. - // If null, disable removing key. - private readonly ClockCache? _pastPathHash; - - // Track ALL of the recently re-committed persisted nodes. This is so that we don't accidentally remove - // recommitted persisted nodes (which will not get re-persisted). - private Dictionary? _persistedLastSeen; - - public readonly long KeyMemoryUsage; - - public TrieStoreDirtyNodesCache(TrieStore trieStore, int trackedPastKeyCount, bool storeByHash, ILogger logger) - { - _trieStore = trieStore; - _logger = logger; - // If the nodestore indicated that path is not required, - // we will use a map with hash as its key instead of the full Key to reduce memory usage. - _storeByHash = storeByHash; - int initialBuckets = TrieStore.HashHelpers.GetPrime(Math.Max(31, Environment.ProcessorCount * 16)); - if (_storeByHash) - { - _byHashObjectCache = new(CollectionExtensions.LockPartitions, initialBuckets); - } - else - { - _byKeyObjectCache = new(CollectionExtensions.LockPartitions, initialBuckets); - } - KeyMemoryUsage = _storeByHash ? 0 : Key.MemoryUsage; // 0 because previously it was not counted. - - if (trackedPastKeyCount > 0 && !storeByHash) - { - _persistedLastSeen = new(); - _pastPathHash = new(trackedPastKeyCount); - } - } - - public void SaveInCache(in Key key, TrieNode node) - { - Debug.Assert(node.Keccak is not null, "Cannot store in cache nodes without resolved key."); - if (TryAdd(key, node)) - { - Metrics.CachedNodesCount = Interlocked.Increment(ref _count); - _trieStore.MemoryUsedByDirtyCache += node.GetMemorySize(false) + KeyMemoryUsage; - } - } - - public TrieNode FindCachedOrUnknown(in Key key) - { - if (TryGetValue(key, out TrieNode trieNode)) - { - Metrics.LoadedFromCacheNodesCount++; - } - else - { - trieNode = new TrieNode(NodeType.Unknown, key.Keccak); - if (_logger.IsTrace) Trace(trieNode); - SaveInCache(key, trieNode); - } - - return trieNode; - - [MethodImpl(MethodImplOptions.NoInlining)] - void Trace(TrieNode trieNode) - { - _logger.Trace($"Creating new node {trieNode}"); - } - } - - public TrieNode FromCachedRlpOrUnknown(in Key key) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (TryGetValue(key, out TrieNode trieNode)) - { - if (trieNode!.FullRlp.IsNull) - { - // // this happens in SyncProgressResolver - // throw new InvalidAsynchronousStateException("Read only trie store is trying to read a transient node."); - return new TrieNode(NodeType.Unknown, key.Keccak); - } - - // we returning a copy to avoid multithreaded access - trieNode = new TrieNode(NodeType.Unknown, key.Keccak, trieNode.FullRlp); - trieNode.ResolveNode(_trieStore.GetTrieStore(key.AddressAsHash256), key.Path); - trieNode.Keccak = key.Keccak; - - Metrics.LoadedFromCacheNodesCount++; - } - else - { - trieNode = new TrieNode(NodeType.Unknown, key.Keccak); - } - - if (_logger.IsTrace) Trace(trieNode); - return trieNode; - - [MethodImpl(MethodImplOptions.NoInlining)] - void Trace(TrieNode trieNode) - { - _logger.Trace($"Creating new node {trieNode}"); - } - } - - public bool IsNodeCached(in Key key) - { - if (_storeByHash) return _byHashObjectCache.ContainsKey(key.Keccak); - return _byKeyObjectCache.ContainsKey(key); - } - - public IEnumerable> AllNodes - { - get - { - if (_storeByHash) - { - return _byHashObjectCache.Select( - pair => new KeyValuePair(new Key(null, TreePath.Empty, pair.Key.Value), pair.Value)); - } - - return _byKeyObjectCache; - } - } - - public bool TryGetValue(in Key key, out TrieNode node) - { - if (_storeByHash) - { - return _byHashObjectCache.TryGetValue(key.Keccak, out node); - } - return _byKeyObjectCache.TryGetValue(key, out node); - } - - public bool TryAdd(in Key key, TrieNode node) - { - if (_storeByHash) - { - return _byHashObjectCache.TryAdd(key.Keccak, node); - } - return _byKeyObjectCache.TryAdd(key, node); - } - - public void Remove(in Key key) - { - if (_storeByHash) - { - if (_byHashObjectCache.Remove(key.Keccak, out _)) - { - Metrics.CachedNodesCount = Interlocked.Decrement(ref _count); - } - - return; - } - if (_byKeyObjectCache.Remove(key, out _)) - { - Metrics.CachedNodesCount = Interlocked.Decrement(ref _count); - } - } - - private MapLock AcquireMapLock() - { - if (_storeByHash) - { - return new MapLock() - { - _storeByHash = _storeByHash, - _byHashLock = _byHashObjectCache.AcquireLock() - }; - } - return new MapLock() - { - _storeByHash = _storeByHash, - _byKeyLock = _byKeyObjectCache.AcquireLock() - }; - } - - public int Count => _count; - - /// - /// This method is responsible for reviewing the nodes that are directly in the cache and - /// removing ones that are either no longer referenced or already persisted. - /// - /// - public long PruneCache(bool skipRecalculateMemory = false) - { - bool shouldTrackPersistedNode = _pastPathHash is not null && !_trieStore.IsCurrentlyFullPruning; - long newMemory = 0; - - using (AcquireMapLock()) - { - foreach ((Key key, TrieNode node) in AllNodes) - { - if (node.IsPersisted) - { - if (_logger.IsTrace) _logger.Trace($"Removing persisted {node} from memory."); - - if (shouldTrackPersistedNode) - { - TrackPersistedNode(key, node); - } - - Hash256? keccak = node.Keccak; - if (keccak is null) - { - TreePath path2 = key.Path; - keccak = node.GenerateKey(_trieStore.GetTrieStore(key.AddressAsHash256), ref path2, isRoot: true); - if (keccak != key.Keccak) - { - throw new InvalidOperationException($"Persisted {node} {key} != {keccak}"); - } - - node.Keccak = keccak; - } - Remove(key); - - Metrics.PrunedPersistedNodesCount++; - } - else if (_trieStore.IsNoLongerNeeded(node)) - { - if (_logger.IsTrace) _logger.Trace($"Removing {node} from memory (no longer referenced)."); - if (node.Keccak is null) - { - throw new InvalidOperationException($"Removed {node}"); - } - Remove(key); - - Metrics.PrunedTransientNodesCount++; - } - else if (!skipRecalculateMemory) - { - node.PrunePersistedRecursively(1); - newMemory += node.GetMemorySize(false) + KeyMemoryUsage; - } - } - } - - return newMemory + (_persistedLastSeen?.Count ?? 0) * 48; - - void TrackPersistedNode(in TrieStoreDirtyNodesCache.Key key, TrieNode node) - { - if (key.Path.Length > TinyTreePath.MaxNibbleLength) return; - TinyTreePath treePath = new(key.Path); - // Persisted node with LastSeen is a node that has been re-committed, likely due to processing - // recalculated to the same hash. - if (node.LastSeen >= 0) - { - // Update _persistedLastSeen to later value. - HashAndTinyPathAndHash plsKey = new(key.Address, in treePath, key.Keccak); - if (!_persistedLastSeen.TryGetValue(plsKey, out var currentLastSeen) || currentLastSeen <= node.LastSeen) - { - _persistedLastSeen[plsKey] = node.LastSeen; - } - } - - // This persisted node is being removed from cache. Keep it in mind in case of an update to the same - // path. - _pastPathHash.Set(new(key.Address, in treePath), key.Keccak); - } - } - - - public void RemovePastKeys(ConcurrentDictionary persistedHashes, INodeStorage nodeStorage) - { - bool CanRemove(in ValueHash256 address, TinyTreePath path, in TreePath fullPath, in ValueHash256 keccak, Hash256? currentlyPersistingKeccak) - { - // Multiple current hash that we don't keep track for simplicity. Just ignore this case. - if (currentlyPersistingKeccak is null) return false; - - // The persisted hash is the same as currently persisting hash. Do nothing. - if ((ValueHash256)currentlyPersistingKeccak == keccak) return false; - - // We have it in cache and it is still needed. - if (TryGetValue(new TrieStoreDirtyNodesCache.Key(address, fullPath, keccak.ToCommitment()), out TrieNode node) && - !_trieStore.IsNoLongerNeeded(node)) return false; - - // We don't have it in cache, but we know it was re-committed, so if it is still needed, don't remove - if (_persistedLastSeen.TryGetValue(new(address, in path, in keccak), out long commitBlock) && - !_trieStore.IsNoLongerNeeded(commitBlock)) return false; - - return true; - } - - using (AcquireMapLock()) - { - INodeStorage.WriteBatch writeBatch = nodeStorage.StartWriteBatch(); - try - { - int round = 0; - foreach (KeyValuePair keyValuePair in persistedHashes) - { - HashAndTinyPath key = keyValuePair.Key; - if (_pastPathHash.TryGet(key, out ValueHash256 prevHash)) - { - TreePath fullPath = key.path.ToTreePath(); // Micro op to reduce double convert - if (CanRemove(key.addr, key.path, fullPath, prevHash, keyValuePair.Value)) - { - Metrics.RemovedNodeCount++; - Hash256? address = key.addr == default ? null : key.addr.ToCommitment(); - writeBatch.Set(address, fullPath, prevHash, default, WriteFlags.DisableWAL); - round++; - } - } - - // Batches of 256 - if (round > 256) - { - writeBatch.Dispose(); - writeBatch = nodeStorage.StartWriteBatch(); - round = 0; - } - } - } - catch (Exception ex) - { - if (_logger.IsError) _logger.Error($"Failed to remove past keys. {ex}"); - } - finally - { - writeBatch.Dispose(); - } - } - } - - public void CleanObsoletePersistedLastSeen() - { - Dictionary? persistedLastSeen = _persistedLastSeen; - - // The amount of nodes that is no longer needed is so high that creating a new dictionary is faster. - Dictionary newPersistedLastSeen = new(); - - foreach (KeyValuePair keyValuePair in persistedLastSeen) - { - if (!_trieStore.IsNoLongerNeeded(keyValuePair.Value)) - { - newPersistedLastSeen.Add(keyValuePair.Key, keyValuePair.Value); - } - } - - _persistedLastSeen = newPersistedLastSeen; - } - - public void PersistAll(INodeStorage nodeStorage, CancellationToken cancellationToken) - { - ConcurrentDictionary wasPersisted = new(); - - void PersistNode(TrieNode n, Hash256? address, TreePath path) - { - if (n.Keccak is null) return; - TrieStoreDirtyNodesCache.Key key = new TrieStoreDirtyNodesCache.Key(address, path, n.Keccak); - if (wasPersisted.TryAdd(key, true)) - { - nodeStorage.Set(address, path, n.Keccak, n.FullRlp); - n.IsPersisted = true; - } - } - - using (AcquireMapLock()) - { - foreach (KeyValuePair kv in AllNodes) - { - if (cancellationToken.IsCancellationRequested) return; - TrieStoreDirtyNodesCache.Key key = kv.Key; - TreePath path = key.Path; - Hash256? address = key.AddressAsHash256; - kv.Value.CallRecursively(PersistNode, address, ref path, _trieStore.GetTrieStore(address), false, _logger, resolveStorageRoot: false); - } - } - } - - public void Dump() - { - if (_logger.IsTrace) - { - _logger.Trace($"Trie node dirty cache ({Count})"); - foreach (KeyValuePair keyValuePair in AllNodes) - { - _logger.Trace($" {keyValuePair.Value}"); - } - } - } - - public void ClearLivePruningTracking() - { - _persistedLastSeen.Clear(); - _pastPathHash?.Clear(); - } - - public void Clear() - { - _byHashObjectCache.NoResizeClear(); - _byKeyObjectCache.NoResizeClear(); - Interlocked.Exchange(ref _count, 0); - Metrics.CachedNodesCount = 0; - _trieStore.MemoryUsedByDirtyCache = 0; - } - - internal readonly struct Key : IEquatable - { - internal const long MemoryUsage = 8 + 36 + 8; // (address (probably shared), path, keccak pointer (shared with TrieNode)) - public readonly ValueHash256 Address; - public Hash256? AddressAsHash256 => Address == default ? null : Address.ToCommitment(); - // Direct member rather than property for large struct, so members are called directly, - // rather than struct copy through the property. Could also return a ref through property. - public readonly TreePath Path; - public Hash256 Keccak { get; } - - public Key(Hash256? address, in TreePath path, Hash256 keccak) - { - Address = address ?? default; - Path = path; - Keccak = keccak; - } - public Key(in ValueHash256 address, in TreePath path, Hash256 keccak) - { - Address = address; - Path = path; - Keccak = keccak; - } - - [SkipLocalsInit] - public override int GetHashCode() - { - var addressHash = Address != default ? Address.GetHashCode() : 1; - return Keccak.ValueHash256.GetChainedHashCode((uint)Path.GetHashCode()) ^ addressHash; - } - - public bool Equals(Key other) - { - return other.Keccak == Keccak && other.Path == Path && other.Address == Address; - } - - public override bool Equals(object? obj) - { - return obj is Key other && Equals(other); - } - } - - internal ref struct MapLock - { - public bool _storeByHash; - public ConcurrentDictionaryLock.Lock _byHashLock; - public ConcurrentDictionaryLock.Lock _byKeyLock; - - public readonly void Dispose() - { - if (_storeByHash) - { - _byHashLock.Dispose(); - } - else - { - _byKeyLock.Dispose(); - } - } - } - } - private const int ShardedDirtyNodeCount = 256; private int _isFirst; @@ -616,37 +149,33 @@ public int CachedNodesCount } } - public void CommitNode(long blockNumber, Hash256? address, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) + private void CommitNode(long blockNumber, Hash256? address, ref TreePath path, in NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) { - ArgumentOutOfRangeException.ThrowIfNegative(blockNumber); - EnsureCommitSetExistsForBlock(blockNumber); - if (_logger.IsTrace) Trace(blockNumber, in nodeCommitInfo); if (!nodeCommitInfo.IsEmptyBlockMarker && !nodeCommitInfo.Node.IsBoundaryProofNode) { - TrieNode node = nodeCommitInfo.Node!; + TrieNode node = nodeCommitInfo.Node; - if (node!.Keccak is null) + if (node.Keccak is null) { ThrowUnknownHash(node); } - if (CurrentPackage is null) + if (node.LastSeen >= 0) { - ThrowUnknownPackage(blockNumber, node); + ThrowNodeHasBeenSeen(blockNumber, node); } - if (node!.LastSeen >= 0) + if (_pruningStrategy.PruningEnabled) { - ThrowNodeHasBeenSeen(blockNumber, node); + node = SaveOrReplaceInDirtyNodesCache(address, ref path, nodeCommitInfo, node); } - node = SaveOrReplaceInDirtyNodesCache(address, nodeCommitInfo, node); node.LastSeen = Math.Max(blockNumber, node.LastSeen); if (!_pruningStrategy.PruningEnabled) { - PersistNode(address, nodeCommitInfo.Path, node, blockNumber, writeFlags); + PersistNode(address, path, node, blockNumber, writeFlags); } CommittedNodesCount++; @@ -662,10 +191,6 @@ void Trace(long blockNumber, in NodeCommitInfo nodeCommitInfo) [StackTraceHidden] static void ThrowUnknownHash(TrieNode node) => throw new TrieStoreException($"The hash of {node} should be known at the time of committing."); - [DoesNotReturn] - [StackTraceHidden] - static void ThrowUnknownPackage(long blockNumber, TrieNode node) => throw new TrieStoreException($"{nameof(CurrentPackage)} is NULL when committing {node} at {blockNumber}."); - [DoesNotReturn] [StackTraceHidden] static void ThrowNodeHasBeenSeen(long blockNumber, TrieNode node) => throw new TrieStoreException($"{nameof(TrieNode.LastSeen)} set on {node} committed at {blockNumber}."); @@ -706,38 +231,34 @@ private TrieNode DirtyNodesFromCachedRlpOrUnknown(TrieStoreDirtyNodesCache.Key k private TrieNode DirtyNodesFindCachedOrUnknown(TrieStoreDirtyNodesCache.Key key) => GetDirtyNodeShard(key).FindCachedOrUnknown(key); - private TrieNode SaveOrReplaceInDirtyNodesCache(Hash256? address, NodeCommitInfo nodeCommitInfo, TrieNode node) + private TrieNode SaveOrReplaceInDirtyNodesCache(Hash256? address, ref TreePath path, NodeCommitInfo nodeCommitInfo, TrieNode node) { - if (_pruningStrategy.PruningEnabled) + TrieStoreDirtyNodesCache.Key key = new(address, path, node.Keccak); + if (DirtyNodesTryGetValue(in key, out TrieNode cachedNodeCopy)) { - TrieStoreDirtyNodesCache.Key key = new TrieStoreDirtyNodesCache.Key(address, nodeCommitInfo.Path, node.Keccak); - if (DirtyNodesTryGetValue(in key, out TrieNode cachedNodeCopy)) + Metrics.LoadedFromCacheNodesCount++; + if (!ReferenceEquals(cachedNodeCopy, node)) { - Metrics.LoadedFromCacheNodesCount++; - if (!ReferenceEquals(cachedNodeCopy, node)) + if (_logger.IsTrace) Trace(node, cachedNodeCopy); + cachedNodeCopy.ResolveKey(GetTrieStore(address), ref path, nodeCommitInfo.IsRoot); + if (node.Keccak != cachedNodeCopy.Keccak) { - if (_logger.IsTrace) Trace(node, cachedNodeCopy); - TreePath path = nodeCommitInfo.Path; - cachedNodeCopy.ResolveKey(GetTrieStore(address), ref path, nodeCommitInfo.IsRoot); - if (node.Keccak != cachedNodeCopy.Keccak) - { - ThrowNodeIsNotSame(node, cachedNodeCopy); - } - - if (!nodeCommitInfo.IsRoot) - { - nodeCommitInfo.NodeParent!.ReplaceChildRef(nodeCommitInfo.ChildPositionAtParent, cachedNodeCopy); - } + ThrowNodeIsNotSame(node, cachedNodeCopy); + } - node = cachedNodeCopy; - Metrics.ReplacedNodesCount++; + if (!nodeCommitInfo.IsRoot) + { + nodeCommitInfo.NodeParent!.ReplaceChildRef(nodeCommitInfo.ChildPositionAtParent, cachedNodeCopy); } - } - else - { - DirtyNodesSaveInCache(key, node); + + node = cachedNodeCopy; + Metrics.ReplacedNodesCount++; } } + else + { + DirtyNodesSaveInCache(key, node); + } return node; @@ -753,11 +274,20 @@ static void ThrowNodeIsNotSame(TrieNode node, TrieNode cachedNodeCopy) => throw new InvalidOperationException($"The hash of replacement node {cachedNodeCopy} is not the same as the original {node}."); } - public void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) + public ICommitter BeginCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags) { ArgumentOutOfRangeException.ThrowIfNegative(blockNumber); EnsureCommitSetExistsForBlock(blockNumber); + int concurrency = _pruningStrategy.PruningEnabled + ? Environment.ProcessorCount + : 0; // The write batch when pruning is not enabled is not concurrent safe + + return new TrieStoreCommitter(this, trieType, blockNumber, address, root, writeFlags, concurrency); + } + + private void FinishBlockCommit(TrieType trieType, long blockNumber, Hash256? address, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) + { try { if (trieType == TrieType.State) // storage tries happen before state commits @@ -1585,6 +1115,50 @@ public bool HasRoot(Hash256 stateRoot) return true; } + private class TrieStoreCommitter( + TrieStore trieStore, + TrieType trieType, + long blockNumber, + Hash256? address, + TrieNode? root, + WriteFlags writeFlags, + int concurrency + ) : ICommitter + { + private readonly bool _needToResetRoot = root is not null && root.IsDirty; + private int _concurrency = concurrency; + private TrieNode? _root = root; + + public void Dispose() + { + if (_needToResetRoot) + { + // During commit it PatriciaTrie, the root may get resolved to an existing node (same keccak). + // This ensure that the root that we use here is the same. + _root = trieStore.FindCachedOrUnknown(address, TreePath.Empty, _root?.Keccak); + } + + trieStore.FinishBlockCommit(trieType, blockNumber, address, _root, writeFlags); + } + + public void CommitNode(ref TreePath path, NodeCommitInfo nodeCommitInfo) => + trieStore.CommitNode(blockNumber, address, ref path, nodeCommitInfo, writeFlags: writeFlags); + + public bool CanSpawnTask() + { + if (Interlocked.Decrement(ref _concurrency) >= 0) + { + return true; + } + + ReturnConcurrencyQuota(); + return false; + } + + public void ReturnConcurrencyQuota() => Interlocked.Increment(ref _concurrency); + } + + internal static class HashHelpers { private const int HashPrime = 101; @@ -1711,59 +1285,4 @@ public static int GetPrime(int min) ]; } } - - [StructLayout(LayoutKind.Auto)] - internal readonly struct HashAndTinyPathAndHash : IEquatable - { - public readonly ValueHash256 hash; - public readonly TinyTreePath path; - public readonly ValueHash256 valueHash; - - public HashAndTinyPathAndHash(Hash256? hash, in TinyTreePath path, in ValueHash256 valueHash) - { - this.hash = hash ?? default; - this.path = path; - this.valueHash = valueHash; - } - public HashAndTinyPathAndHash(in ValueHash256 hash, in TinyTreePath path, in ValueHash256 valueHash) - { - this.hash = hash; - this.path = path; - this.valueHash = valueHash; - } - - public bool Equals(HashAndTinyPathAndHash other) => hash == other.hash && path.Equals(in other.path) && valueHash.Equals(in other.valueHash); - public override bool Equals(object? obj) => obj is HashAndTinyPath other && Equals(other); - public override int GetHashCode() - { - var hashHash = hash != default ? hash.GetHashCode() : 1; - return valueHash.GetChainedHashCode((uint)path.GetHashCode()) ^ hashHash; - } - } - - [StructLayout(LayoutKind.Auto)] - internal readonly struct HashAndTinyPath : IEquatable - { - public readonly ValueHash256 addr; - public readonly TinyTreePath path; - - public HashAndTinyPath(Hash256? hash, in TinyTreePath path) - { - addr = hash ?? default; - this.path = path; - } - public HashAndTinyPath(in ValueHash256 hash, in TinyTreePath path) - { - addr = hash; - this.path = path; - } - - public bool Equals(HashAndTinyPath other) => addr == other.addr && path.Equals(in other.path); - public override bool Equals(object? obj) => obj is HashAndTinyPath other && Equals(other); - public override int GetHashCode() - { - var addressHash = addr != default ? addr.GetHashCode() : 1; - return path.GetHashCode() ^ addressHash; - } - } } diff --git a/src/Nethermind/Nethermind.Trie/Pruning/TrieStoreDirtyNodesCache.cs b/src/Nethermind/Nethermind.Trie/Pruning/TrieStoreDirtyNodesCache.cs new file mode 100644 index 00000000000..57d3d7f83da --- /dev/null +++ b/src/Nethermind/Nethermind.Trie/Pruning/TrieStoreDirtyNodesCache.cs @@ -0,0 +1,481 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using Nethermind.Core; +using Nethermind.Core.Caching; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Logging; +using CollectionExtensions = Nethermind.Core.Collections.CollectionExtensions; + +namespace Nethermind.Trie.Pruning; + +internal class TrieStoreDirtyNodesCache +{ + private readonly TrieStore _trieStore; + private int _count = 0; + private readonly ILogger _logger; + private readonly bool _storeByHash; + private readonly ConcurrentDictionary _byKeyObjectCache; + private readonly ConcurrentDictionary _byHashObjectCache; + + // Track some of the persisted path hash. Used to be able to remove keys when it is replaced. + // If null, disable removing key. + private readonly ClockCache? _pastPathHash; + + // Track ALL of the recently re-committed persisted nodes. This is so that we don't accidentally remove + // recommitted persisted nodes (which will not get re-persisted). + private Dictionary? _persistedLastSeen; + + public readonly long KeyMemoryUsage; + + public TrieStoreDirtyNodesCache(TrieStore trieStore, int trackedPastKeyCount, bool storeByHash, ILogger logger) + { + _trieStore = trieStore; + _logger = logger; + // If the nodestore indicated that path is not required, + // we will use a map with hash as its key instead of the full Key to reduce memory usage. + _storeByHash = storeByHash; + int initialBuckets = TrieStore.HashHelpers.GetPrime(Math.Max(31, Environment.ProcessorCount * 16)); + if (_storeByHash) + { + _byHashObjectCache = new(CollectionExtensions.LockPartitions, initialBuckets); + } + else + { + _byKeyObjectCache = new(CollectionExtensions.LockPartitions, initialBuckets); + } + KeyMemoryUsage = _storeByHash ? 0 : Key.MemoryUsage; // 0 because previously it was not counted. + + if (trackedPastKeyCount > 0 && !storeByHash) + { + _persistedLastSeen = new(); + _pastPathHash = new(trackedPastKeyCount); + } + } + + public void SaveInCache(in Key key, TrieNode node) + { + Debug.Assert(node.Keccak is not null, "Cannot store in cache nodes without resolved key."); + if (TryAdd(key, node)) + { + Metrics.CachedNodesCount = Interlocked.Increment(ref _count); + _trieStore.MemoryUsedByDirtyCache += node.GetMemorySize(false) + KeyMemoryUsage; + } + } + + public TrieNode FindCachedOrUnknown(in Key key) + { + if (TryGetValue(key, out TrieNode trieNode)) + { + Metrics.LoadedFromCacheNodesCount++; + } + else + { + trieNode = new TrieNode(NodeType.Unknown, key.Keccak); + if (_logger.IsTrace) Trace(trieNode); + SaveInCache(key, trieNode); + } + + return trieNode; + + [MethodImpl(MethodImplOptions.NoInlining)] + void Trace(TrieNode trieNode) + { + _logger.Trace($"Creating new node {trieNode}"); + } + } + + public TrieNode FromCachedRlpOrUnknown(in Key key) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (TryGetValue(key, out TrieNode trieNode)) + { + if (trieNode!.FullRlp.IsNull) + { + // // this happens in SyncProgressResolver + // throw new InvalidAsynchronousStateException("Read only trie store is trying to read a transient node."); + return new TrieNode(NodeType.Unknown, key.Keccak); + } + + // we returning a copy to avoid multithreaded access + trieNode = new TrieNode(NodeType.Unknown, key.Keccak, trieNode.FullRlp); + trieNode.ResolveNode(_trieStore.GetTrieStore(key.AddressAsHash256), key.Path); + trieNode.Keccak = key.Keccak; + + Metrics.LoadedFromCacheNodesCount++; + } + else + { + trieNode = new TrieNode(NodeType.Unknown, key.Keccak); + } + + if (_logger.IsTrace) Trace(trieNode); + return trieNode; + + [MethodImpl(MethodImplOptions.NoInlining)] + void Trace(TrieNode trieNode) + { + _logger.Trace($"Creating new node {trieNode}"); + } + } + + public bool IsNodeCached(in Key key) + { + if (_storeByHash) return _byHashObjectCache.ContainsKey(key.Keccak); + return _byKeyObjectCache.ContainsKey(key); + } + + public IEnumerable> AllNodes + { + get + { + if (_storeByHash) + { + return _byHashObjectCache.Select( + pair => new KeyValuePair(new Key(null, TreePath.Empty, pair.Key.Value), pair.Value)); + } + + return _byKeyObjectCache; + } + } + + public bool TryGetValue(in Key key, out TrieNode node) + { + if (_storeByHash) + { + return _byHashObjectCache.TryGetValue(key.Keccak, out node); + } + return _byKeyObjectCache.TryGetValue(key, out node); + } + + public bool TryAdd(in Key key, TrieNode node) + { + if (_storeByHash) + { + return _byHashObjectCache.TryAdd(key.Keccak, node); + } + return _byKeyObjectCache.TryAdd(key, node); + } + + public void Remove(in Key key) + { + if (_storeByHash) + { + if (_byHashObjectCache.Remove(key.Keccak, out _)) + { + Metrics.CachedNodesCount = Interlocked.Decrement(ref _count); + } + + return; + } + if (_byKeyObjectCache.Remove(key, out _)) + { + Metrics.CachedNodesCount = Interlocked.Decrement(ref _count); + } + } + + private MapLock AcquireMapLock() + { + if (_storeByHash) + { + return new MapLock() + { + _storeByHash = _storeByHash, + _byHashLock = _byHashObjectCache.AcquireLock() + }; + } + return new MapLock() + { + _storeByHash = _storeByHash, + _byKeyLock = _byKeyObjectCache.AcquireLock() + }; + } + + public int Count => _count; + + /// + /// This method is responsible for reviewing the nodes that are directly in the cache and + /// removing ones that are either no longer referenced or already persisted. + /// + /// + public long PruneCache(bool skipRecalculateMemory = false) + { + bool shouldTrackPersistedNode = _pastPathHash is not null && !_trieStore.IsCurrentlyFullPruning; + long newMemory = 0; + + using (AcquireMapLock()) + { + foreach ((Key key, TrieNode node) in AllNodes) + { + if (node.IsPersisted) + { + if (_logger.IsTrace) _logger.Trace($"Removing persisted {node} from memory."); + + if (shouldTrackPersistedNode) + { + TrackPersistedNode(key, node); + } + + Hash256? keccak = node.Keccak; + if (keccak is null) + { + TreePath path2 = key.Path; + keccak = node.GenerateKey(_trieStore.GetTrieStore(key.AddressAsHash256), ref path2, isRoot: true); + if (keccak != key.Keccak) + { + throw new InvalidOperationException($"Persisted {node} {key} != {keccak}"); + } + + node.Keccak = keccak; + } + Remove(key); + + Metrics.PrunedPersistedNodesCount++; + } + else if (_trieStore.IsNoLongerNeeded(node)) + { + if (_logger.IsTrace) _logger.Trace($"Removing {node} from memory (no longer referenced)."); + if (node.Keccak is null) + { + throw new InvalidOperationException($"Removed {node}"); + } + Remove(key); + + Metrics.PrunedTransientNodesCount++; + } + else if (!skipRecalculateMemory) + { + node.PrunePersistedRecursively(1); + newMemory += node.GetMemorySize(false) + KeyMemoryUsage; + } + } + } + + return newMemory + (_persistedLastSeen?.Count ?? 0) * 48; + + void TrackPersistedNode(in TrieStoreDirtyNodesCache.Key key, TrieNode node) + { + if (key.Path.Length > TinyTreePath.MaxNibbleLength) return; + TinyTreePath treePath = new(key.Path); + // Persisted node with LastSeen is a node that has been re-committed, likely due to processing + // recalculated to the same hash. + if (node.LastSeen >= 0) + { + // Update _persistedLastSeen to later value. + HashAndTinyPathAndHash plsKey = new(key.Address, in treePath, key.Keccak); + if (!_persistedLastSeen.TryGetValue(plsKey, out var currentLastSeen) || currentLastSeen <= node.LastSeen) + { + _persistedLastSeen[plsKey] = node.LastSeen; + } + } + + // This persisted node is being removed from cache. Keep it in mind in case of an update to the same + // path. + _pastPathHash.Set(new(key.Address, in treePath), key.Keccak); + } + } + + + public void RemovePastKeys(ConcurrentDictionary persistedHashes, INodeStorage nodeStorage) + { + bool CanRemove(in ValueHash256 address, TinyTreePath path, in TreePath fullPath, in ValueHash256 keccak, Hash256? currentlyPersistingKeccak) + { + // Multiple current hash that we don't keep track for simplicity. Just ignore this case. + if (currentlyPersistingKeccak is null) return false; + + // The persisted hash is the same as currently persisting hash. Do nothing. + if ((ValueHash256)currentlyPersistingKeccak == keccak) return false; + + // We have it in cache and it is still needed. + if (TryGetValue(new TrieStoreDirtyNodesCache.Key(address, fullPath, keccak.ToCommitment()), out TrieNode node) && + !_trieStore.IsNoLongerNeeded(node)) return false; + + // We don't have it in cache, but we know it was re-committed, so if it is still needed, don't remove + if (_persistedLastSeen.TryGetValue(new(address, in path, in keccak), out long commitBlock) && + !_trieStore.IsNoLongerNeeded(commitBlock)) return false; + + return true; + } + + using (AcquireMapLock()) + { + INodeStorage.WriteBatch writeBatch = nodeStorage.StartWriteBatch(); + try + { + int round = 0; + foreach (KeyValuePair keyValuePair in persistedHashes) + { + HashAndTinyPath key = keyValuePair.Key; + if (_pastPathHash.TryGet(key, out ValueHash256 prevHash)) + { + TreePath fullPath = key.path.ToTreePath(); // Micro op to reduce double convert + if (CanRemove(key.addr, key.path, fullPath, prevHash, keyValuePair.Value)) + { + Metrics.RemovedNodeCount++; + Hash256? address = key.addr == default ? null : key.addr.ToCommitment(); + writeBatch.Set(address, fullPath, prevHash, default, WriteFlags.DisableWAL); + round++; + } + } + + // Batches of 256 + if (round > 256) + { + writeBatch.Dispose(); + writeBatch = nodeStorage.StartWriteBatch(); + round = 0; + } + } + } + catch (Exception ex) + { + if (_logger.IsError) _logger.Error($"Failed to remove past keys. {ex}"); + } + finally + { + writeBatch.Dispose(); + } + } + } + + public void CleanObsoletePersistedLastSeen() + { + Dictionary? persistedLastSeen = _persistedLastSeen; + + // The amount of nodes that is no longer needed is so high that creating a new dictionary is faster. + Dictionary newPersistedLastSeen = new(); + + foreach (KeyValuePair keyValuePair in persistedLastSeen) + { + if (!_trieStore.IsNoLongerNeeded(keyValuePair.Value)) + { + newPersistedLastSeen.Add(keyValuePair.Key, keyValuePair.Value); + } + } + + _persistedLastSeen = newPersistedLastSeen; + } + + public void PersistAll(INodeStorage nodeStorage, CancellationToken cancellationToken) + { + ConcurrentDictionary wasPersisted = new(); + + void PersistNode(TrieNode n, Hash256? address, TreePath path) + { + if (n.Keccak is null) return; + TrieStoreDirtyNodesCache.Key key = new TrieStoreDirtyNodesCache.Key(address, path, n.Keccak); + if (wasPersisted.TryAdd(key, true)) + { + nodeStorage.Set(address, path, n.Keccak, n.FullRlp); + n.IsPersisted = true; + } + } + + using (AcquireMapLock()) + { + foreach (KeyValuePair kv in AllNodes) + { + if (cancellationToken.IsCancellationRequested) return; + TrieStoreDirtyNodesCache.Key key = kv.Key; + TreePath path = key.Path; + Hash256? address = key.AddressAsHash256; + kv.Value.CallRecursively(PersistNode, address, ref path, _trieStore.GetTrieStore(address), false, _logger, resolveStorageRoot: false); + } + } + } + + public void Dump() + { + if (_logger.IsTrace) + { + _logger.Trace($"Trie node dirty cache ({Count})"); + foreach (KeyValuePair keyValuePair in AllNodes) + { + _logger.Trace($" {keyValuePair.Value}"); + } + } + } + + public void ClearLivePruningTracking() + { + _persistedLastSeen.Clear(); + _pastPathHash?.Clear(); + } + + public void Clear() + { + _byHashObjectCache.NoResizeClear(); + _byKeyObjectCache.NoResizeClear(); + Interlocked.Exchange(ref _count, 0); + Metrics.CachedNodesCount = 0; + _trieStore.MemoryUsedByDirtyCache = 0; + } + + internal readonly struct Key : IEquatable + { + internal const long MemoryUsage = 8 + 36 + 8; // (address (probably shared), path, keccak pointer (shared with TrieNode)) + public readonly ValueHash256 Address; + public Hash256? AddressAsHash256 => Address == default ? null : Address.ToCommitment(); + // Direct member rather than property for large struct, so members are called directly, + // rather than struct copy through the property. Could also return a ref through property. + public readonly TreePath Path; + public Hash256 Keccak { get; } + + public Key(Hash256? address, in TreePath path, Hash256 keccak) + { + Address = address ?? default; + Path = path; + Keccak = keccak; + } + public Key(in ValueHash256 address, in TreePath path, Hash256 keccak) + { + Address = address; + Path = path; + Keccak = keccak; + } + + [SkipLocalsInit] + public override int GetHashCode() + { + var addressHash = Address != default ? Address.GetHashCode() : 1; + return Keccak.ValueHash256.GetChainedHashCode((uint)Path.GetHashCode()) ^ addressHash; + } + + public bool Equals(Key other) + { + return other.Keccak == Keccak && other.Path == Path && other.Address == Address; + } + + public override bool Equals(object? obj) + { + return obj is Key other && Equals(other); + } + } + + internal ref struct MapLock + { + public bool _storeByHash; + public ConcurrentDictionaryLock.Lock _byHashLock; + public ConcurrentDictionaryLock.Lock _byKeyLock; + + public readonly void Dispose() + { + if (_storeByHash) + { + _byHashLock.Dispose(); + } + else + { + _byKeyLock.Dispose(); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs index 557e519a36e..e93f1096d06 100644 --- a/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs +++ b/src/Nethermind/Nethermind.Trie/TrieStoreWithReadFlags.cs @@ -7,32 +7,15 @@ namespace Nethermind.Trie; -public class TrieStoreWithReadFlags : TrieNodeResolverWithReadFlags, IScopedTrieStore +public class TrieStoreWithReadFlags(IScopedTrieStore implementation, ReadFlags flags) + : TrieNodeResolverWithReadFlags(implementation, flags), IScopedTrieStore { - private readonly IScopedTrieStore _baseImplementation; + public ICommitter BeginCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) => + implementation.BeginCommit(trieType, blockNumber, root, writeFlags); - public TrieStoreWithReadFlags(IScopedTrieStore implementation, ReadFlags flags) : base(implementation, flags) - { - _baseImplementation = implementation; - } + public bool IsPersisted(in TreePath path, in ValueHash256 keccak) => + implementation.IsPersisted(in path, in keccak); - public void CommitNode(long blockNumber, NodeCommitInfo nodeCommitInfo, WriteFlags writeFlags = WriteFlags.None) - { - _baseImplementation.CommitNode(blockNumber, nodeCommitInfo, writeFlags); - } - - public void FinishBlockCommit(TrieType trieType, long blockNumber, TrieNode? root, WriteFlags writeFlags = WriteFlags.None) - { - _baseImplementation.FinishBlockCommit(trieType, blockNumber, root, writeFlags); - } - - public bool IsPersisted(in TreePath path, in ValueHash256 keccak) - { - return _baseImplementation.IsPersisted(in path, in keccak); - } - - public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) - { - _baseImplementation.Set(in path, in keccak, rlp); - } + public void Set(in TreePath path, in ValueHash256 keccak, byte[] rlp) => + implementation.Set(in path, in keccak, rlp); } diff --git a/src/Nethermind/Nethermind.TxPool.Test/NonceManagerTests.cs b/src/Nethermind/Nethermind.TxPool.Test/NonceManagerTests.cs index 574d7b7e0c0..a78fcb377b2 100644 --- a/src/Nethermind/Nethermind.TxPool.Test/NonceManagerTests.cs +++ b/src/Nethermind/Nethermind.TxPool.Test/NonceManagerTests.cs @@ -11,6 +11,7 @@ using Nethermind.Core.Specs; using Nethermind.Core.Test.Builders; using Nethermind.Db; +using Nethermind.Evm; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Specs; @@ -42,8 +43,8 @@ public void Setup() _blockTree.Head.Returns(block); _blockTree.FindBestSuggestedHeader().Returns(Build.A.BlockHeader.WithNumber(10000000).TestObject); - _headInfo = new ChainHeadInfoProvider(_specProvider, _blockTree, _stateProvider); - _nonceManager = new NonceManager(_headInfo.AccountStateProvider); + _headInfo = new ChainHeadInfoProvider(_specProvider, _blockTree, _stateProvider, new CodeInfoRepository()); + _nonceManager = new NonceManager(_headInfo.ReadOnlyStateProvider); } [Test] diff --git a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.Blobs.cs b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.Blobs.cs index f95d2eb8c31..b2e21635f62 100644 --- a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.Blobs.cs +++ b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.Blobs.cs @@ -12,6 +12,7 @@ using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Evm; +using Nethermind.Evm.Tracing.GethStyle.Custom.JavaScript; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.TxPool.Collections; @@ -679,6 +680,65 @@ public void should_index_blobs_when_adding_txs([Values(true, false)] bool isPers } } + [Test] + [Repeat(3)] + public void should_handle_indexing_blobs_when_adding_txs_in_parallel([Values(true, false)] bool isPersistentStorage) + { + const int txsPerSender = 10; + int poolSize = TestItem.PrivateKeys.Length * txsPerSender; + TxPoolConfig txPoolConfig = new() + { + BlobsSupport = isPersistentStorage ? BlobsSupportMode.Storage : BlobsSupportMode.InMemory, + PersistentBlobStorageSize = isPersistentStorage ? poolSize : 0, + InMemoryBlobPoolSize = isPersistentStorage ? 0 : poolSize + }; + + IComparer comparer = new TransactionComparerProvider(_specProvider, _blockTree).GetDefaultComparer(); + + BlobTxDistinctSortedPool blobPool = isPersistentStorage + ? new PersistentBlobTxDistinctSortedPool(new BlobTxStorage(), txPoolConfig, comparer, LimboLogs.Instance) + : new BlobTxDistinctSortedPool(txPoolConfig.InMemoryBlobPoolSize, comparer, LimboLogs.Instance); + + byte[] expectedBlobVersionedHash = null; + + foreach (PrivateKey privateKey in TestItem.PrivateKeys) + { + EnsureSenderBalance(privateKey.Address, UInt256.MaxValue); + } + + // adding, getting and removing txs in parallel + Parallel.ForEach(TestItem.PrivateKeys, privateKey => + { + for (int i = 0; i < txsPerSender; i++) + { + Transaction tx = Build.A.Transaction + .WithNonce((UInt256)i) + .WithShardBlobTxTypeAndFields() + .WithMaxFeePerGas(1.GWei()) + .WithMaxPriorityFeePerGas(1.GWei()) + .WithMaxFeePerBlobGas(1000.Wei()) + .SignedAndResolved(_ethereumEcdsa, privateKey).TestObject; + + expectedBlobVersionedHash ??= tx.BlobVersionedHashes[0]!; + + blobPool.TryInsert(tx.Hash, tx, out _).Should().BeTrue(); + + for (int j = 0; j < 100; j++) + { + blobPool.TryGetBlobAndProof(expectedBlobVersionedHash.ToBytes(), out _, out _).Should().BeTrue(); + } + + // removing 50% of txs + if (i % 2 == 0) blobPool.TryRemove(tx.Hash, out _).Should().BeTrue(); + } + }); + + // we expect index to have 1 key with poolSize/2 values (50% of txs were removed) + blobPool.BlobIndex.Count.Should().Be(1); + blobPool.BlobIndex.TryGetValue(expectedBlobVersionedHash, out List values).Should().BeTrue(); + values.Count.Should().Be(poolSize / 2); + } + private Transaction GetTx(PrivateKey sender) { return Build.A.Transaction diff --git a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs index bdb1a26a5a6..4e7f10b33b2 100644 --- a/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs +++ b/src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs @@ -1660,7 +1660,7 @@ public void Should_correctly_add_tx_to_local_pool_when_underpaid([Values] TxType // No need to check for deposit tx if (txType == TxType.DepositTx) return; - ISpecProvider specProvider = GetCancunSpecProvider(); + ISpecProvider specProvider = GetPragueSpecProvider(); TxPoolConfig txPoolConfig = new TxPoolConfig { Size = 30, PersistentBlobStorageSize = 0 }; _txPool = CreatePool(txPoolConfig, specProvider); @@ -1684,8 +1684,10 @@ public void Should_correctly_add_tx_to_local_pool_when_underpaid([Values] TxType .WithNonce(0) .WithType(txType) .WithShardBlobTxTypeAndFieldsIfBlobTx() + .WithAuthorizationCodeIfAuthorizationListTx() .WithMaxFeePerGas(9.GWei()) .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(txType != TxType.SetCode ? GasCostOf.Transaction : GasCostOf.Transaction + GasCostOf.NewAccount) .WithTo(TestItem.AddressB) .SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; @@ -1698,6 +1700,35 @@ public void Should_correctly_add_tx_to_local_pool_when_underpaid([Values] TxType _txPool.GetPendingTransactions().Should().NotContain(testTx); } + static IEnumerable<(byte[], AcceptTxResult)> CodeCases() + { + yield return (new byte[16], AcceptTxResult.SenderIsContract); + //Delegation code + yield return ([.. Eip7702Constants.DelegationHeader, .. new byte[20]], AcceptTxResult.Accepted); + } + [TestCaseSource(nameof(CodeCases))] + public void SubmitTx_CodeIsNotDelegationAndDelegation_DelegationIsAccepted((byte[] code, AcceptTxResult expected) testCase) + { + ISpecProvider specProvider = GetPragueSpecProvider(); + TxPoolConfig txPoolConfig = new TxPoolConfig { Size = 30, PersistentBlobStorageSize = 0 }; + _txPool = CreatePool(txPoolConfig, specProvider); + + Transaction testTx = Build.A.Transaction + .WithNonce(0) + .WithMaxFeePerGas(9.GWei()) + .WithMaxPriorityFeePerGas(9.GWei()) + .WithGasLimit(100_000) + .WithTo(TestItem.AddressB) + .SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject; + + EnsureSenderBalance(TestItem.PrivateKeyA.Address, UInt256.MaxValue); + + _stateProvider.InsertCode(TestItem.PrivateKeyA.Address, testCase.code, Prague.Instance); + + AcceptTxResult result = _txPool.SubmitTx(testTx, TxHandlingOptions.PersistentBroadcast); + result.Should().Be(testCase.expected); + } + private IDictionary GetPeers(int limit = 100) { var peers = new Dictionary(); @@ -1726,7 +1757,7 @@ private TxPool CreatePool( txStorage ??= new BlobTxStorage(); _headInfo = chainHeadInfoProvider; - _headInfo ??= new ChainHeadInfoProvider(specProvider, _blockTree, _stateProvider); + _headInfo ??= new ChainHeadInfoProvider(specProvider, _blockTree, _stateProvider, new CodeInfoRepository()); return new TxPool( _ethereumEcdsa, @@ -1763,6 +1794,13 @@ private static ISpecProvider GetCancunSpecProvider() return specProvider; } + private static ISpecProvider GetPragueSpecProvider() + { + var specProvider = Substitute.For(); + specProvider.GetSpec(Arg.Any()).Returns(Prague.Instance); + return specProvider; + } + private Transaction[] AddTransactionsToPool(bool sameTransactionSenderPerPeer = true, bool sameNoncePerPeer = false, int transactionsPerPeer = 10) { var transactions = GetTransactions(GetPeers(transactionsPerPeer), sameTransactionSenderPerPeer, sameNoncePerPeer); diff --git a/src/Nethermind/Nethermind.TxPool/Collections/BlobTxDistinctSortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/BlobTxDistinctSortedPool.cs index d447cf4af9f..e924b9e5733 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/BlobTxDistinctSortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/BlobTxDistinctSortedPool.cs @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -17,7 +17,7 @@ public class BlobTxDistinctSortedPool(int capacity, IComparer compa { protected override string ShortPoolName => "BlobPool"; - internal readonly ConcurrentDictionary> BlobIndex = new(Bytes.EqualityComparer); + internal readonly Dictionary> BlobIndex = new(Bytes.EqualityComparer); protected override IComparer GetReplacementComparer(IComparer comparer) => comparer.GetBlobReplacementComparer(); @@ -26,19 +26,24 @@ public bool TryGetBlobAndProof(byte[] requestedBlobVersionedHash, [NotNullWhen(true)] out byte[]? blob, [NotNullWhen(true)] out byte[]? proof) { - if (BlobIndex.TryGetValue(requestedBlobVersionedHash, out List? txHashes) - && txHashes[0] is not null - && TryGetValue(txHashes[0], out Transaction? blobTx) - && blobTx.BlobVersionedHashes?.Length > 0) + using var lockRelease = Lock.Acquire(); + + if (BlobIndex.TryGetValue(requestedBlobVersionedHash, out List? txHashes)) { - for (int indexOfBlob = 0; indexOfBlob < blobTx.BlobVersionedHashes.Length; indexOfBlob++) + foreach (Hash256 hash in CollectionsMarshal.AsSpan(txHashes)) { - if (Bytes.AreEqual(blobTx.BlobVersionedHashes[indexOfBlob], requestedBlobVersionedHash) - && blobTx.NetworkWrapper is ShardBlobNetworkWrapper wrapper) + if (TryGetValueNonLocked(hash, out Transaction? blobTx) && blobTx.BlobVersionedHashes?.Length > 0) { - blob = wrapper.Blobs[indexOfBlob]; - proof = wrapper.Proofs[indexOfBlob]; - return true; + for (int indexOfBlob = 0; indexOfBlob < blobTx.BlobVersionedHashes.Length; indexOfBlob++) + { + if (Bytes.AreEqual(blobTx.BlobVersionedHashes[indexOfBlob], requestedBlobVersionedHash) + && blobTx.NetworkWrapper is ShardBlobNetworkWrapper wrapper) + { + blob = wrapper.Blobs[indexOfBlob]; + proof = wrapper.Proofs[indexOfBlob]; + return true; + } + } } } } @@ -48,18 +53,18 @@ public bool TryGetBlobAndProof(byte[] requestedBlobVersionedHash, return false; } - public override bool TryInsert(ValueHash256 hash, Transaction blobTx, out Transaction? removed) + protected override bool InsertCore(ValueHash256 key, Transaction value, AddressAsKey groupKey) { - if (base.TryInsert(blobTx.Hash, blobTx, out removed)) + if (base.InsertCore(key, value, groupKey)) { - AddToBlobIndex(blobTx); + AddToBlobIndex(value); return true; } return false; } - protected void AddToBlobIndex(Transaction blobTx) + private void AddToBlobIndex(Transaction blobTx) { if (blobTx.BlobVersionedHashes?.Length > 0) { @@ -67,13 +72,9 @@ protected void AddToBlobIndex(Transaction blobTx) { if (blobVersionedHash?.Length == KzgPolynomialCommitments.BytesPerBlobVersionedHash) { - BlobIndex.AddOrUpdate(blobVersionedHash, - k => [blobTx.Hash!], - (k, b) => - { - b.Add(blobTx.Hash!); - return b; - }); + ref List? list = ref CollectionsMarshal.GetValueRefOrAddDefault(BlobIndex, blobVersionedHash, out _); + list ??= new List(); + list.Add(blobTx.Hash!); } } } @@ -99,8 +100,7 @@ private void RemoveFromBlobIndex(Transaction blobTx) { foreach (var blobVersionedHash in blobTx.BlobVersionedHashes) { - if (blobVersionedHash is not null - && BlobIndex.TryGetValue(blobVersionedHash, out List? txHashes)) + if (blobVersionedHash is not null && BlobIndex.TryGetValue(blobVersionedHash, out List? txHashes)) { if (txHashes.Count < 2) { @@ -119,5 +119,5 @@ private void RemoveFromBlobIndex(Transaction blobTx) /// For tests only - to test sorting /// internal void TryGetBlobTxSortingEquivalent(Hash256 hash, out Transaction? lightBlobTx) - => base.TryGetValue(hash, out lightBlobTx); + => base.TryGetValueNonLocked(hash, out lightBlobTx); } diff --git a/src/Nethermind/Nethermind.TxPool/Collections/DistinctValueSortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/DistinctValueSortedPool.cs index c37a14346d0..86821529b2d 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/DistinctValueSortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/DistinctValueSortedPool.cs @@ -45,16 +45,20 @@ protected DistinctValueSortedPool( protected virtual IComparer GetReplacementComparer(IComparer comparer) => comparer; - protected override void InsertCore(TKey key, TValue value, TGroupKey groupKey) + protected override bool InsertCore(TKey key, TValue value, TGroupKey groupKey) { if (_distinctDictionary.TryGetValue(value, out KeyValuePair oldKvp)) { TryRemoveNonLocked(oldKvp.Key, evicted: false, out _, out _); } - base.InsertCore(key, value, groupKey); + if (base.InsertCore(key, value, groupKey)) + { + _distinctDictionary[value] = new KeyValuePair(key, value); + return true; + } - _distinctDictionary[value] = new KeyValuePair(key, value); + return false; } protected override bool Remove(TKey key, out TValue? value) diff --git a/src/Nethermind/Nethermind.TxPool/Collections/PersistentBlobTxDistinctSortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/PersistentBlobTxDistinctSortedPool.cs index d0f7eb34d92..f17b0f51866 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/PersistentBlobTxDistinctSortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/PersistentBlobTxDistinctSortedPool.cs @@ -33,12 +33,12 @@ private void RecreateLightTxCollectionAndCache(ITxStorage blobTxStorage) if (_logger.IsDebug) _logger.Debug("Recreating light collection of blob transactions and cache"); int numberOfTxsInDb = 0; int numberOfBlobsInDb = 0; - Stopwatch stopwatch = Stopwatch.StartNew(); + long startTime = Stopwatch.GetTimestamp(); foreach (LightTransaction lightBlobTx in blobTxStorage.GetAll()) { - if (base.TryInsert(lightBlobTx.Hash, lightBlobTx, out _)) + if (lightBlobTx.SenderAddress is not null + && base.InsertCore(lightBlobTx.Hash, lightBlobTx, lightBlobTx.SenderAddress)) { - AddToBlobIndex(lightBlobTx); numberOfTxsInDb++; numberOfBlobsInDb += lightBlobTx.BlobVersionedHashes?.Length ?? 0; } @@ -46,16 +46,15 @@ private void RecreateLightTxCollectionAndCache(ITxStorage blobTxStorage) if (_logger.IsInfo && numberOfTxsInDb != 0) { - long loadingTime = stopwatch.ElapsedMilliseconds; - _logger.Info($"Loaded {numberOfTxsInDb} blob txs from persistent db, containing {numberOfBlobsInDb} blobs, in {loadingTime}ms"); + long loadingTime = (long)Stopwatch.GetElapsedTime(startTime).TotalMilliseconds; + _logger.Info($"Loaded {numberOfTxsInDb} blob txs from persistent db, containing {numberOfBlobsInDb} blobs, in {loadingTime:N0}ms"); _logger.Info($"There are {BlobIndex.Count} unique blobs indexed"); } - stopwatch.Stop(); } - public override bool TryInsert(ValueHash256 hash, Transaction fullBlobTx, out Transaction? removed) + protected override bool InsertCore(ValueHash256 hash, Transaction fullBlobTx, AddressAsKey groupKey) { - if (base.TryInsert(fullBlobTx.Hash, new LightTransaction(fullBlobTx), out removed)) + if (base.InsertCore(hash, new LightTransaction(fullBlobTx), groupKey)) { _blobTxCache.Set(fullBlobTx.Hash, fullBlobTx); _blobTxStorage.Add(fullBlobTx); @@ -65,11 +64,11 @@ public override bool TryInsert(ValueHash256 hash, Transaction fullBlobTx, out Tr return false; } - public override bool TryGetValue(ValueHash256 hash, [NotNullWhen(true)] out Transaction? fullBlobTx) + protected override bool TryGetValueNonLocked(ValueHash256 hash, [NotNullWhen(true)] out Transaction? fullBlobTx) { // Firstly check if tx is present in in-memory collection of light blob txs (without actual blobs). // If not, just return false - if (base.TryGetValue(hash, out Transaction? lightTx)) + if (base.TryGetValueNonLocked(hash, out Transaction? lightTx)) { // tx is present in light collection. Try to get full blob tx from cache if (_blobTxCache.TryGet(hash, out fullBlobTx)) diff --git a/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs b/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs index 98a32ce04e3..f5ebedda4d4 100644 --- a/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs +++ b/src/Nethermind/Nethermind.TxPool/Collections/SortedPool.cs @@ -336,13 +336,15 @@ public bool ContainsKey(TKey key) /// Key to be returned. /// Returned element or null. /// If element retrieval succeeded. True if element was present in pool. - public virtual bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) + public bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) { using var lockRelease = Lock.Acquire(); - return _cacheMap.TryGetValue(key, out value) && value is not null; + return TryGetValueNonLocked(key, out value); } + protected virtual bool TryGetValueNonLocked(TKey key, [NotNullWhen(true)] out TValue? value) => _cacheMap.TryGetValue(key, out value) && value is not null; + /// /// Tries to insert element. /// @@ -350,7 +352,7 @@ public virtual bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) /// Element to insert. /// Element removed because of exceeding capacity /// If element was inserted. False if element was already present in pool. - public virtual bool TryInsert(TKey key, TValue value, out TValue? removed) + public bool TryInsert(TKey key, TValue value, out TValue? removed) { using var lockRelease = Lock.Acquire(); @@ -360,7 +362,7 @@ public virtual bool TryInsert(TKey key, TValue value, out TValue? removed) if (group is not null) { - InsertCore(key, value, group); + bool inserted = InsertCore(key, value, group); if (_cacheMap.Count > _capacity) { @@ -372,11 +374,11 @@ public virtual bool TryInsert(TKey key, TValue value, out TValue? removed) RemoveLast(out removed); } - return true; + return inserted; } removed = default; - return true; + return inserted; } } @@ -430,7 +432,7 @@ protected virtual bool CanInsert(TKey key, TValue value) /// /// Actual insert mechanism. /// - protected virtual void InsertCore(TKey key, TValue value, TGroupKey groupKey) + protected virtual bool InsertCore(TKey key, TValue value, TGroupKey groupKey) { if (!_buckets.TryGetValue(groupKey, out EnhancedSortedSet? bucket)) { @@ -445,7 +447,10 @@ protected virtual void InsertCore(TKey key, TValue value, TGroupKey groupKey) UpdateSortedValues(bucket, last); _snapshot = null; Inserted?.Invoke(this, new SortedPoolEventArgs(key, value)); + return true; } + + return false; } private void UpdateSortedValues(EnhancedSortedSet bucket, TValue? previousLast) diff --git a/src/Nethermind/Nethermind.TxPool/Filters/DeployedCodeFilter.cs b/src/Nethermind/Nethermind.TxPool/Filters/DeployedCodeFilter.cs index 12d6e5a7035..1eb06f8dcc8 100644 --- a/src/Nethermind/Nethermind.TxPool/Filters/DeployedCodeFilter.cs +++ b/src/Nethermind/Nethermind.TxPool/Filters/DeployedCodeFilter.cs @@ -3,23 +3,21 @@ using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Evm; +using Nethermind.State; namespace Nethermind.TxPool.Filters { /// /// Filters out transactions that sender has any code deployed. If is enabled. /// - internal sealed class DeployedCodeFilter : IIncomingTxFilter + internal sealed class DeployedCodeFilter(IReadOnlyStateProvider worldState, ICodeInfoRepository codeInfoRepository, IChainHeadSpecProvider specProvider) : IIncomingTxFilter { - private readonly IChainHeadSpecProvider _specProvider; - - public DeployedCodeFilter(IChainHeadSpecProvider specProvider) - { - _specProvider = specProvider; - } public AcceptTxResult Accept(Transaction tx, ref TxFilteringState state, TxHandlingOptions txHandlingOptions) { - return _specProvider.GetCurrentHeadSpec().IsEip3607Enabled && state.SenderAccount.HasCode + return worldState.IsInvalidContractSender(specProvider.GetCurrentHeadSpec(), + tx.SenderAddress!, + () => codeInfoRepository.TryGetDelegation(worldState, tx.SenderAddress!, out _)) ? AcceptTxResult.SenderIsContract : AcceptTxResult.Accepted; } diff --git a/src/Nethermind/Nethermind.TxPool/Filters/IsValidTxSender.cs b/src/Nethermind/Nethermind.TxPool/Filters/IsValidTxSender.cs new file mode 100644 index 00000000000..f9077d6c966 --- /dev/null +++ b/src/Nethermind/Nethermind.TxPool/Filters/IsValidTxSender.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Evm; +using Nethermind.State; + +namespace Nethermind.TxPool.Filters; +public class IsValidTxSender(IWorldState worldState, ICodeInfoRepository codeInfoRepository, IChainHeadSpecProvider specProvider) +{ + public bool IsValid(Address sender) + { + return specProvider.GetCurrentHeadSpec().IsEip3607Enabled + && worldState.HasCode(sender) + && (!specProvider.GetCurrentHeadSpec().IsEip7702Enabled || !codeInfoRepository.TryGetDelegation(worldState, sender, out _)); + } +} diff --git a/src/Nethermind/Nethermind.TxPool/Filters/RecoverAuthorityFilter.cs b/src/Nethermind/Nethermind.TxPool/Filters/RecoverAuthorityFilter.cs new file mode 100644 index 00000000000..8075d9d14dd --- /dev/null +++ b/src/Nethermind/Nethermind.TxPool/Filters/RecoverAuthorityFilter.cs @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Crypto; + +namespace Nethermind.TxPool.Filters +{ + /// + /// Will recover authority from transactions with authority_list + /// /// + internal sealed class RecoverAuthorityFilter(IEthereumEcdsa ecdsa) : IIncomingTxFilter + { + public AcceptTxResult Accept(Transaction tx, ref TxFilteringState state, TxHandlingOptions handlingOptions) + { + if (tx.HasAuthorizationList) + { + foreach (AuthorizationTuple tuple in tx.AuthorizationList) + { + tuple.Authority ??= ecdsa.RecoverAddress(tuple); + } + } + + return AcceptTxResult.Accepted; + } + } +} diff --git a/src/Nethermind/Nethermind.TxPool/IChainHeadInfoProvider.cs b/src/Nethermind/Nethermind.TxPool/IChainHeadInfoProvider.cs index 2387ac91427..ca89bd82aec 100644 --- a/src/Nethermind/Nethermind.TxPool/IChainHeadInfoProvider.cs +++ b/src/Nethermind/Nethermind.TxPool/IChainHeadInfoProvider.cs @@ -4,7 +4,9 @@ using System; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Evm; using Nethermind.Int256; +using Nethermind.State; namespace Nethermind.TxPool { @@ -12,13 +14,15 @@ public interface IChainHeadInfoProvider { IChainHeadSpecProvider SpecProvider { get; } - IAccountStateProvider AccountStateProvider { get; } + IReadOnlyStateProvider ReadOnlyStateProvider { get; } - public long HeadNumber { get; } + ICodeInfoRepository CodeInfoRepository { get; } - public long? BlockGasLimit { get; } + long HeadNumber { get; } - public UInt256 CurrentBaseFee { get; } + long? BlockGasLimit { get; } + + UInt256 CurrentBaseFee { get; } public UInt256 CurrentFeePerBlobGas { get; } diff --git a/src/Nethermind/Nethermind.TxPool/Metrics.cs b/src/Nethermind/Nethermind.TxPool/Metrics.cs index 53721d957fe..d825ad8060b 100644 --- a/src/Nethermind/Nethermind.TxPool/Metrics.cs +++ b/src/Nethermind/Nethermind.TxPool/Metrics.cs @@ -122,6 +122,10 @@ public static class Metrics [Description("Ratio of 1559-type transactions in the block.")] public static float Eip1559TransactionsRatio { get; set; } + [GaugeMetric] + [Description("Ratio of 7702-type transactions in the block.")] + public static long Eip7702TransactionsInBlock { get; set; } + [GaugeMetric] [Description("Number of blob transactions in the block.")] public static long BlobTransactionsInBlock { get; set; } diff --git a/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj b/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj index 2f6f825141f..7c667ed34e8 100644 --- a/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj +++ b/src/Nethermind/Nethermind.TxPool/Nethermind.TxPool.csproj @@ -13,6 +13,8 @@ + + diff --git a/src/Nethermind/Nethermind.TxPool/TxPool.cs b/src/Nethermind/Nethermind.TxPool/TxPool.cs index 956e7a55397..aea6460a8ff 100644 --- a/src/Nethermind/Nethermind.TxPool/TxPool.cs +++ b/src/Nethermind/Nethermind.TxPool/TxPool.cs @@ -103,7 +103,7 @@ public TxPool(IEthereumEcdsa ecdsa, _headInfo = chainHeadInfoProvider ?? throw new ArgumentNullException(nameof(chainHeadInfoProvider)); _txPoolConfig = txPoolConfig; _blobReorgsSupportEnabled = txPoolConfig.BlobsSupport.SupportsReorgs(); - _accounts = _accountCache = new AccountCache(_headInfo.AccountStateProvider); + _accounts = _accountCache = new AccountCache(_headInfo.ReadOnlyStateProvider); _specProvider = _headInfo.SpecProvider; MemoryAllowance.MemPoolSize = txPoolConfig.Size; @@ -124,34 +124,36 @@ public TxPool(IEthereumEcdsa ecdsa, _headInfo.HeadChanged += OnHeadChange; - _preHashFilters = new IIncomingTxFilter[] - { + _preHashFilters = + [ new NotSupportedTxFilter(txPoolConfig, _logger), new GasLimitTxFilter(_headInfo, txPoolConfig, _logger), new PriorityFeeTooLowFilter(_logger), new FeeTooLowFilter(_headInfo, _transactions, _blobTransactions, thereIsPriorityContract, _logger), new MalformedTxFilter(_specProvider, validator, _logger) - }; + ]; - List postHashFilters = new() - { + List postHashFilters = + [ new NullHashTxFilter(), // needs to be first as it assigns the hash new AlreadyKnownTxFilter(_hashCache, _logger), new UnknownSenderFilter(ecdsa, _logger), - new TxTypeTxFilter(_transactions, _blobTransactions), // has to be after UnknownSenderFilter as it uses sender + new TxTypeTxFilter(_transactions, + _blobTransactions), // has to be after UnknownSenderFilter as it uses sender new BalanceZeroFilter(thereIsPriorityContract, _logger), new BalanceTooLowFilter(_transactions, _blobTransactions, _logger), new LowNonceFilter(_logger), // has to be after UnknownSenderFilter as it uses sender new FutureNonceFilter(txPoolConfig), new GapNonceFilter(_transactions, _blobTransactions, _logger), - }; + new RecoverAuthorityFilter(ecdsa) + ]; if (incomingTxFilter is not null) { postHashFilters.Add(incomingTxFilter); } - postHashFilters.Add(new DeployedCodeFilter(_specProvider)); + postHashFilters.Add(new DeployedCodeFilter(chainHeadInfoProvider.ReadOnlyStateProvider, chainHeadInfoProvider.CodeInfoRepository, _specProvider)); _postHashFilters = postHashFilters.ToArray(); @@ -304,6 +306,7 @@ private void RemoveProcessedTransactions(Block block) long discoveredForPendingTxs = 0; long discoveredForHashCache = 0; long eip1559Txs = 0; + long eip7702Txs = 0; long blobTxs = 0; long blobs = 0; @@ -333,6 +336,11 @@ private void RemoveProcessedTransactions(Block block) } } + if (blockTx.Type == TxType.SetCode) + { + eip7702Txs++; + } + if (!IsKnown(txHash)) { discoveredForHashCache++; @@ -355,6 +363,7 @@ private void RemoveProcessedTransactions(Block block) Metrics.DarkPoolRatioLevel1 = (float)discoveredForHashCache / transactionsInBlock; Metrics.DarkPoolRatioLevel2 = (float)discoveredForPendingTxs / transactionsInBlock; Metrics.Eip1559TransactionsRatio = (float)eip1559Txs / transactionsInBlock; + Metrics.Eip7702TransactionsInBlock = eip7702Txs; Metrics.BlobTransactionsInBlock = blobTxs; Metrics.BlobsInBlock = blobs; } @@ -905,6 +914,7 @@ private static void WriteTxPoolReport(in ILogger logger) ------------------------------------------------ Ratios in last block: * Eip1559 Transactions: {Metrics.Eip1559TransactionsRatio,24:P5} +* Eip7702 Transactions: {Metrics.Eip7702TransactionsInBlock,24:P5} * DarkPool Level1: {Metrics.DarkPoolRatioLevel1,24:P5} * DarkPool Level2: {Metrics.DarkPoolRatioLevel2,24:P5} Amounts: diff --git a/src/bench_precompiles b/src/bench_precompiles index d083d17d367..c1089350ef0 160000 --- a/src/bench_precompiles +++ b/src/bench_precompiles @@ -1 +1 @@ -Subproject commit d083d17d3679b82585877a2d18829535d972546a +Subproject commit c1089350ef0daa3f0bd437ce9d1626de973f9a93 diff --git a/tools/docgen/.editorconfig b/tools/DocGen/.editorconfig similarity index 100% rename from tools/docgen/.editorconfig rename to tools/DocGen/.editorconfig diff --git a/tools/docgen/ConfigGenerator.cs b/tools/DocGen/ConfigGenerator.cs similarity index 81% rename from tools/docgen/ConfigGenerator.cs rename to tools/DocGen/ConfigGenerator.cs index 15e329b4e7a..fd529ad2350 100644 --- a/tools/docgen/ConfigGenerator.cs +++ b/tools/DocGen/ConfigGenerator.cs @@ -88,13 +88,7 @@ private static void WriteMarkdown(StreamWriter file, Type configType) var moduleName = configType.Name[1..].Replace("Config", null); file.WriteLine($""" -
- - - #### {moduleName} - - -

+ ### {moduleName} """); @@ -107,10 +101,32 @@ private static void WriteMarkdown(StreamWriter file, Type configType) var description = itemAttr.Description.Replace("\n", "\n ").TrimEnd(' '); - file.Write($""" - - **`--{moduleName}.{prop.Name} `** `NETHERMIND_{moduleName.ToUpperInvariant()}CONFIG_{prop.Name.ToUpperInvariant()}` - - {description} + file.Write($$""" + - #### `{{moduleName}}.{{prop.Name}}` \{#{{moduleName.ToLowerInvariant()}}-{{prop.Name.ToLowerInvariant()}}\} + + + + ``` + --{{moduleName}}.{{prop.Name}} + ``` + + + ``` + NETHERMIND_{{moduleName.ToUpperInvariant()}}CONFIG_{{prop.Name.ToUpperInvariant()}}= + ``` + + + ```json + { + "{{moduleName}}": { + "{{prop.Name}}": + } + } + ``` + + + + {{description}} """); var startsFromNewLine = WriteAllowedValues(file, prop.PropertyType) || description.EndsWith('\n'); @@ -121,11 +137,7 @@ private static void WriteMarkdown(StreamWriter file, Type configType) file.WriteLine(); } - file.WriteLine(""" -

-
- - """); + file.WriteLine(); } private static bool WriteAllowedValues(StreamWriter file, Type type) diff --git a/tools/docgen/DBSizeGenerator.cs b/tools/DocGen/DBSizeGenerator.cs similarity index 78% rename from tools/docgen/DBSizeGenerator.cs rename to tools/DocGen/DBSizeGenerator.cs index 761cad555ea..f78ce87ae20 100644 --- a/tools/docgen/DBSizeGenerator.cs +++ b/tools/DocGen/DBSizeGenerator.cs @@ -8,7 +8,6 @@ namespace Nethermind.DocGen; internal static class DBSizeGenerator { - private const string _chainSizesDir = "chainSizes"; private const string _startMark = ""; private const string _endMark = ""; @@ -23,7 +22,7 @@ internal static class DBSizeGenerator "blobTransactions" ]; - internal static void Generate(string path) + internal static void Generate(string docsPath, string? dbSizeSourcePath) { IList chainOrder = [ @@ -36,9 +35,10 @@ internal static void Generate(string path) "volta" ]; - var chainSizesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, _chainSizesDir); + dbSizeSourcePath ??= AppDomain.CurrentDomain.BaseDirectory; + var chains = Directory - .GetFiles(chainSizesPath) + .GetFiles(dbSizeSourcePath) .Select(Path.GetFileNameWithoutExtension) .OrderBy(c => { @@ -48,14 +48,14 @@ internal static void Generate(string path) }) .ToList(); - GenerateFile(Path.Join(path, "docs", "fundamentals"), chains!); - GenerateFile(Path.Join(path, "versioned_docs", $"version-{GetLatestVersion(path)}", "fundamentals"), chains!); + GenerateFile(Path.Join(docsPath, "docs", "fundamentals"), dbSizeSourcePath, chains!); + GenerateFile(Path.Join(docsPath, "versioned_docs", $"version-{GetLatestVersion(docsPath)}", "fundamentals"), dbSizeSourcePath, chains!); } - private static void GenerateFile(string path, IList chains) + private static void GenerateFile(string docsPath, string dbSizeSourcePath, IList chains) { - var fileName = Path.Join(path, "database.md"); - var tempFileName = Path.Join(path, "~database.md"); + var fileName = Path.Join(docsPath, "database.md"); + var tempFileName = Path.Join(docsPath, "~database.md"); // Delete the temp file if it exists File.Delete(tempFileName); @@ -77,7 +77,7 @@ private static void GenerateFile(string path, IList chains) writeStream.WriteLine(); - WriteMarkdown(writeStream, chains!); + WriteMarkdown(writeStream, dbSizeSourcePath, chains!); var skip = true; @@ -103,12 +103,12 @@ private static void GenerateFile(string path, IList chains) AnsiConsole.MarkupLine($"[green]Updated[/] {fileName}"); } - private static void WriteMarkdown(StreamWriter file, IList chains) + private static void WriteMarkdown(StreamWriter file, string dbSizeSourcePath, IList chains) { file.WriteLine(""); foreach (var chain in chains) - WriteChainSize(file, chain); + WriteChainSize(file, dbSizeSourcePath, chain); file.WriteLine(""" @@ -116,9 +116,10 @@ private static void WriteMarkdown(StreamWriter file, IList chains) """); } - private static void WriteChainSize(StreamWriter file, string chain) + private static void WriteChainSize(StreamWriter file, string dbSizeSourcePath, string chain) { - using var json = JsonDocument.Parse(File.ReadAllText($"{_chainSizesDir}/{chain}.json")); + var path = Path.Join(dbSizeSourcePath, $"{chain}.json"); + using var json = JsonDocument.Parse(File.ReadAllText(path)); if (json.RootElement.ValueKind != JsonValueKind.Object) return; diff --git a/tools/docgen/DocGen.csproj b/tools/DocGen/DocGen.csproj similarity index 100% rename from tools/docgen/DocGen.csproj rename to tools/DocGen/DocGen.csproj diff --git a/tools/docgen/DocGen.sln b/tools/DocGen/DocGen.sln similarity index 100% rename from tools/docgen/DocGen.sln rename to tools/DocGen/DocGen.sln diff --git a/tools/docgen/JsonRpcGenerator.cs b/tools/DocGen/JsonRpcGenerator.cs similarity index 100% rename from tools/docgen/JsonRpcGenerator.cs rename to tools/DocGen/JsonRpcGenerator.cs diff --git a/tools/docgen/MetricsGenerator.cs b/tools/DocGen/MetricsGenerator.cs similarity index 91% rename from tools/docgen/MetricsGenerator.cs rename to tools/DocGen/MetricsGenerator.cs index 95e7f859851..697b0381772 100644 --- a/tools/docgen/MetricsGenerator.cs +++ b/tools/DocGen/MetricsGenerator.cs @@ -85,13 +85,7 @@ private static void WriteMarkdown(StreamWriter file, Type metricsType) return; file.WriteLine($""" -
- - - #### {GetNamespace(metricsType.FullName)} - - -

+ ### {GetNamespace(metricsType.FullName)} """); @@ -100,7 +94,7 @@ private static void WriteMarkdown(StreamWriter file, Type metricsType) var attr = prop.GetCustomAttribute(); var param = _regex.Replace(prop.Name, m => $"_{m.Value.ToLowerInvariant()}"); - file.WriteLine($"- **`nethermind{param}`**"); + file.WriteLine($"- #### `nethermind{param}` \\{{#{param[1..]}\\}}"); if (!string.IsNullOrWhiteSpace(attr?.Description)) file.WriteLine($""" @@ -110,12 +104,7 @@ private static void WriteMarkdown(StreamWriter file, Type metricsType) """); } - file.WriteLine(""" - -

-
- - """); + file.WriteLine(); } private static string? GetNamespace(string? fullTypeName) diff --git a/tools/docgen/Program.cs b/tools/DocGen/Program.cs similarity index 87% rename from tools/docgen/Program.cs rename to tools/DocGen/Program.cs index 63fc55f1bf0..80a62ef36ca 100644 --- a/tools/docgen/Program.cs +++ b/tools/DocGen/Program.cs @@ -30,7 +30,7 @@ public override int Execute(CommandContext context, AppSettings settings) ConfigGenerator.Generate(settings.DocsPath); if (settings.GenerateDBSize) - DBSizeGenerator.Generate(settings.DocsPath); + DBSizeGenerator.Generate(settings.DocsPath, settings.DBSizeSourcePath); if (settings.GenerateJsonRpc) JsonRpcGenerator.Generate(settings.DocsPath); @@ -44,6 +44,10 @@ public override int Execute(CommandContext context, AppSettings settings) public sealed class AppSettings : CommandSettings { + [Description("Path to the directory with DB size files")] + [CommandOption("--dbsize-src")] + public string? DBSizeSourcePath { get; init; } + [Description("Path to the docs")] [CommandArgument(0, "[docspath]")] public string? DocsPath { get; init; } diff --git a/tools/docgen/Properties/launchSettings.json b/tools/DocGen/Properties/launchSettings.json similarity index 100% rename from tools/docgen/Properties/launchSettings.json rename to tools/DocGen/Properties/launchSettings.json diff --git a/tools/docgen/nuget.config b/tools/DocGen/nuget.config similarity index 100% rename from tools/docgen/nuget.config rename to tools/DocGen/nuget.config diff --git a/tools/SendBlobs/AccountException.cs b/tools/SendBlobs/AccountException.cs index 624e8754a86..00f66e3966f 100644 --- a/tools/SendBlobs/AccountException.cs +++ b/tools/SendBlobs/AccountException.cs @@ -22,8 +22,4 @@ public AccountException(string? message) : base(message) public AccountException(string? message, Exception? innerException) : base(message, innerException) { } - - protected AccountException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } diff --git a/tools/SendBlobs/BlobSender.cs b/tools/SendBlobs/BlobSender.cs index 4b4d0889ced..008523c85e5 100644 --- a/tools/SendBlobs/BlobSender.cs +++ b/tools/SendBlobs/BlobSender.cs @@ -16,7 +16,7 @@ namespace SendBlobs; internal class BlobSender { - private static readonly TxDecoder txDecoder = new(); + private static readonly TxDecoder txDecoder = TxDecoder.Instance; private INodeManager _nodeManager; private readonly ILogger _logger; @@ -85,7 +85,7 @@ public async Task SendRandomBlobs( signers.Add(new(new Signer(chainId, privateKey, _logManager), nonce)); } - TxDecoder txDecoder = new(); + TxDecoder txDecoder = TxDecoder.Instance; Random random = new(); int signerIndex = -1; @@ -283,7 +283,7 @@ public async Task SendData( if (defaultMaxFeePerBlobGas is null) { ulong excessBlobsReserve = 2 * Eip4844Constants.TargetBlobGasPerBlock; - BlobGasCalculator.TryCalculateBlobGasPricePerUnit( + BlobGasCalculator.TryCalculateFeePerBlobGas( (block.ExcessBlobGas ?? 0) + excessBlobs * Eip4844Constants.MaxBlobGasPerBlock + excessBlobsReserve, diff --git a/tools/SendBlobs/FundsDistributor.cs b/tools/SendBlobs/FundsDistributor.cs index 6356d91d345..d794abbfeff 100644 --- a/tools/SendBlobs/FundsDistributor.cs +++ b/tools/SendBlobs/FundsDistributor.cs @@ -83,7 +83,7 @@ public async Task> DitributeFunds(Signer distributeFrom, uin List txHash = new List(); - TxDecoder txDecoder = new(); + TxDecoder txDecoder = TxDecoder.Instance; StreamWriter? keyWriter = null; if (!string.IsNullOrWhiteSpace(_keyFilePath)) @@ -147,7 +147,7 @@ public async Task> ReclaimFunds(Address beneficiary, UInt256 ILogger log = _logManager.GetClassLogger(); List txHashes = new List(); - TxDecoder txDecoder = new(); + TxDecoder txDecoder = TxDecoder.Instance; foreach (var signer in privateSigners) { diff --git a/tools/TxParser/Program.cs b/tools/TxParser/Program.cs index 409c755ef97..cd2baab450c 100644 --- a/tools/TxParser/Program.cs +++ b/tools/TxParser/Program.cs @@ -5,7 +5,6 @@ using Nethermind.Core.Extensions; using Nethermind.Serialization.Rlp; using Nethermind.Crypto; -using Nethermind.Logging; using Nethermind.Consensus.Validators; using Nethermind.Specs.Forks; @@ -21,7 +20,7 @@ TxValidator txValidator = new TxValidator(BlockchainIds.Mainnet); if (txValidator.IsWellFormed(tx, GrayGlacier.Instance)) { - EthereumEcdsa ecdsa = new(BlockchainIds.Mainnet, SimpleConsoleLogManager.Instance); + EthereumEcdsa ecdsa = new(BlockchainIds.Mainnet); Address? sender = ecdsa.RecoverAddress(tx); if (sender is null) {