diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs index 7d79e8950df1..f16e472d7e69 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only + using System; using System.Collections.Generic; using System.Linq; @@ -54,12 +55,15 @@ public ExecutionRequestsProcessor(ITransactionProcessor transactionProcessor) _consolidationTransaction.Hash = _consolidationTransaction.CalculateHash(); } - public byte[] ProcessDeposits(TxReceipt[] receipts, IReleaseSpec spec) + public void ProcessDeposits(TxReceipt[] receipts, IReleaseSpec spec, ArrayPoolList requests) { if (!spec.DepositsEnabled) - return []; + return; - using ArrayPoolList depositRequests = new(receipts.Length * 2); + using ArrayPoolList depositRequests = new(receipts.Length * 2 + 1) + { + (byte)ExecutionRequestType.Deposit + }; for (int i = 0; i < receipts.Length; i++) { @@ -79,7 +83,8 @@ public byte[] ProcessDeposits(TxReceipt[] receipts, IReleaseSpec spec) } } - return depositRequests.ToArray(); + if (depositRequests.Count > 1) + requests.Add(depositRequests.ToArray()); } private void DecodeDepositRequest(LogEntry log, Span buffer) @@ -107,15 +112,14 @@ private void DecodeDepositRequest(LogEntry log, Span buffer) } } - - private byte[] ReadRequests(Block block, IWorldState state, IReleaseSpec spec, Address contractAddress) + private void ReadRequests(Block block, IWorldState state, IReleaseSpec spec, Address contractAddress, ArrayPoolList requests) { bool isWithdrawalRequests = contractAddress == spec.Eip7002ContractAddress; int requestsByteSize = isWithdrawalRequests ? ExecutionRequestExtensions.WithdrawalRequestsBytesSize : ExecutionRequestExtensions.ConsolidationRequestsBytesSize; if (!(isWithdrawalRequests ? spec.WithdrawalRequestsEnabled : spec.ConsolidationRequestsEnabled) || !state.AccountExists(contractAddress)) - return []; + return; CallOutputTracer tracer = new(); @@ -123,18 +127,30 @@ private byte[] ReadRequests(Block block, IWorldState state, IReleaseSpec spec, A if (tracer.ReturnValue is null || tracer.ReturnValue.Length == 0) { - return []; + return; } int validLength = tracer.ReturnValue.Length - (tracer.ReturnValue.Length % requestsByteSize); - return tracer.ReturnValue.AsSpan(0, validLength).ToArray(); + + if (validLength == 0) return; + + Span buffer = stackalloc byte[validLength + 1]; + buffer[0] = isWithdrawalRequests ? (byte)ExecutionRequestType.WithdrawalRequest : (byte)ExecutionRequestType.ConsolidationRequest; + tracer.ReturnValue.AsSpan(0, validLength).CopyTo(buffer.Slice(1)); + requests.Add(buffer.ToArray()); } public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) { if (!spec.RequestsEnabled) return; - block.ExecutionRequests = new byte[][] { ProcessDeposits(receipts, spec), ReadRequests(block, state, spec, spec.Eip7002ContractAddress), ReadRequests(block, state, spec, spec.Eip7251ContractAddress) }; + + using ArrayPoolList requests = new(3); + + ProcessDeposits(receipts, spec, requests); + ReadRequests(block, state, spec, spec.Eip7002ContractAddress, requests); + ReadRequests(block, state, spec, spec.Eip7251ContractAddress, requests); + block.ExecutionRequests = requests.ToArray(); block.Header.RequestsHash = ExecutionRequestExtensions.CalculateHashFromFlatEncodedRequests(block.ExecutionRequests); } } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestExecutionRequest.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestExecutionRequest.cs index 532c78d1b7e1..bcbb6d589baa 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestExecutionRequest.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestExecutionRequest.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Linq; using Nethermind.Core.Collections; using Nethermind.Core.ExecutionRequest; @@ -19,7 +18,7 @@ public byte[][]? RequestDataParts set { _requestDataParts = value; - RequestData = value is null ? null : Bytes.Concat(value.AsSpan()); + RequestData = value is null ? null : Bytes.Concat(value); } } } @@ -32,18 +31,30 @@ public static ArrayPoolList GetFlatEncodedRequests( TestExecutionRequest[] consolidationRequests ) { - return new(ExecutionRequestExtensions.RequestPartsCount) + var result = new ArrayPoolList(ExecutionRequestExtensions.MaxRequestsCount); + + if (depositRequests.Length > 0) { - FlatEncodeRequests(depositRequests, depositRequests.Length * ExecutionRequestExtensions.DepositRequestsBytesSize), - FlatEncodeRequests(withdrawalRequests, withdrawalRequests.Length * ExecutionRequestExtensions.WithdrawalRequestsBytesSize), - FlatEncodeRequests(consolidationRequests, consolidationRequests.Length * ExecutionRequestExtensions.ConsolidationRequestsBytesSize) - }; + result.Add(FlatEncodeRequests(depositRequests, depositRequests.Length * ExecutionRequestExtensions.DepositRequestsBytesSize, (byte)ExecutionRequestType.Deposit)); + } + + if (withdrawalRequests.Length > 0) + { + result.Add(FlatEncodeRequests(withdrawalRequests, withdrawalRequests.Length * ExecutionRequestExtensions.WithdrawalRequestsBytesSize, (byte)ExecutionRequestType.WithdrawalRequest)); + } + + if (consolidationRequests.Length > 0) + { + result.Add(FlatEncodeRequests(consolidationRequests, consolidationRequests.Length * ExecutionRequestExtensions.ConsolidationRequestsBytesSize, (byte)ExecutionRequestType.ConsolidationRequest)); + } + + return result; - static byte[] FlatEncodeRequests(TestExecutionRequest[] requests, int bufferSize) + static byte[] FlatEncodeRequests(ExecutionRequest.ExecutionRequest[] requests, int bufferSize, byte type) { - using ArrayPoolList buffer = new(bufferSize); + using ArrayPoolList buffer = new(bufferSize + 1) { type }; - foreach (TestExecutionRequest request in requests) + foreach (ExecutionRequest.ExecutionRequest request in requests) { buffer.AddRange(request.RequestData!); } diff --git a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs index ea5b11dd0575..ad3b9e59741b 100644 --- a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs +++ b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequestExtensions.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only - using System; using System.Buffers; using System.Collections.Generic; @@ -18,11 +17,11 @@ public static class ExecutionRequestExtensions public const int DepositRequestsBytesSize = PublicKeySize /*pubkey: Bytes48 */ + Hash256.Size /*withdrawal_credentials: Bytes32 */ + sizeof(ulong) /*amount: uint64*/ + 96 /*signature: Bytes96*/ + sizeof(ulong) /*index: uint64*/; public const int WithdrawalRequestsBytesSize = Address.Size + PublicKeySize /*validator_pubkey: Bytes48*/ + sizeof(ulong) /*amount: uint64*/; public const int ConsolidationRequestsBytesSize = Address.Size + PublicKeySize /*source_pubkey: Bytes48*/ + PublicKeySize /*target_pubkey: Bytes48*/; - public const int RequestPartsCount = 3; + public const int MaxRequestsCount = 3; private const int PublicKeySize = 48; - public static readonly byte[][] EmptyRequests = [[], [], []]; - public static readonly Hash256 EmptyRequestsHash = CalculateHashFromFlatEncodedRequests(EmptyRequests); + public static byte[][] EmptyRequests = []; + public static Hash256 EmptyRequestsHash = CalculateHashFromFlatEncodedRequests(EmptyRequests); public static int GetRequestsByteSize(this IEnumerable requests) { @@ -37,29 +36,22 @@ public static int GetRequestsByteSize(this IEnumerable request [SkipLocalsInit] public static Hash256 CalculateHashFromFlatEncodedRequests(byte[][]? flatEncodedRequests) { - // make sure that length is exactly 3 - if (flatEncodedRequests is null || flatEncodedRequests.Length != RequestPartsCount) + // make sure that length is 3 or less elements + if (flatEncodedRequests is null || flatEncodedRequests.Length > MaxRequestsCount) { - throw new ArgumentException("Flat encoded requests must be an array of 3 elements"); + throw new ArgumentException("Flat encoded requests must be an array of 3 or less elements"); } - byte[] concatenatedHashes = new byte[Hash256.Size * RequestPartsCount]; - int currentPosition = 0; - byte type = 0; - // Allocate the buffer once outside the loop - Span requestBuffer = stackalloc byte[Math.Max(Math.Max(flatEncodedRequests[0].Length, flatEncodedRequests[1].Length), flatEncodedRequests[2].Length) + 1]; - // Compute sha256 for each request and concatenate them + using SHA256 sha256 = SHA256.Create(); + using ArrayPoolList concatenatedHashes = new(Hash256.Size * MaxRequestsCount); foreach (byte[] requests in flatEncodedRequests) { - requestBuffer[0] = type; - requests.CopyTo(requestBuffer.Slice(1, requests.Length)); - SHA256.HashData(requestBuffer[..(requests.Length + 1)]).CopyTo(concatenatedHashes.AsSpan(currentPosition, Hash256.Size)); - currentPosition += Hash256.Size; - type++; + if (requests.Length <= 1) continue; + concatenatedHashes.AddRange(sha256.ComputeHash(requests)); } // Compute sha256 of the concatenated hashes - return new Hash256(SHA256.HashData(concatenatedHashes)); + return new Hash256(sha256.ComputeHash(concatenatedHashes.ToArray())); } @@ -70,16 +62,28 @@ public static ArrayPoolList GetFlatEncodedRequests( ExecutionRequest[] consolidationRequests ) { - return new(RequestPartsCount) + var result = new ArrayPoolList(MaxRequestsCount); + + if (depositRequests.Length > 0) + { + result.Add(FlatEncodeRequests(depositRequests, depositRequests.Length * DepositRequestsBytesSize, (byte)ExecutionRequestType.Deposit)); + } + + if (withdrawalRequests.Length > 0) { - FlatEncodeRequests(depositRequests, depositRequests.Length * DepositRequestsBytesSize), - FlatEncodeRequests(withdrawalRequests, withdrawalRequests.Length * WithdrawalRequestsBytesSize), - FlatEncodeRequests(consolidationRequests, consolidationRequests.Length * ConsolidationRequestsBytesSize) - }; + result.Add(FlatEncodeRequests(withdrawalRequests, withdrawalRequests.Length * WithdrawalRequestsBytesSize, (byte)ExecutionRequestType.WithdrawalRequest)); + } + + if (consolidationRequests.Length > 0) + { + result.Add(FlatEncodeRequests(consolidationRequests, consolidationRequests.Length * ConsolidationRequestsBytesSize, (byte)ExecutionRequestType.ConsolidationRequest)); + } + + return result; - static byte[] FlatEncodeRequests(ExecutionRequest[] requests, int bufferSize) + static byte[] FlatEncodeRequests(ExecutionRequest[] requests, int bufferSize, byte type) { - using ArrayPoolList buffer = new(bufferSize); + using ArrayPoolList buffer = new(bufferSize + 1) { type }; foreach (ExecutionRequest request in requests) { diff --git a/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs b/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs index 3064491f8968..de6cd3e26fa0 100644 --- a/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs +++ b/src/Nethermind/Nethermind.Merge.AuRa.Test/AuRaMergeEngineModuleTests.cs @@ -62,10 +62,10 @@ int ErrorCode => base.forkchoiceUpdatedV2_should_validate_withdrawals(input); [TestCase( - "0xd25128557a58fe7bb6346d779cfc86a1f5bd9bff4786a118097aebdf3128f46d", - "0xcbf0d15de352e744aba609aca74846ede5fc3ffd00ca506914b498b00470cbf8", + "0x1270af16dfea9b40aa9381529cb2629008fea35386041f52c07034ea8c038a05", + "0xea3bdca86662fa8b5399f2c3ff494ced747f07834740ead723ebe023852e9ea1", "0xd75d320c3a98a02ec7fe2abdcb1769bd063fec04d73f1735810f365ac12bc4ba", - "0xc9763e9904d3fe5b")] + "0x7389011914b1ca84")] public override Task Should_process_block_as_expected_V4(string latestValidHash, string blockHash, string stateRoot, string payloadId) => base.Should_process_block_as_expected_V4(latestValidHash, blockHash, stateRoot, payloadId); diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs index a7e11bef29f1..8d4b5d5ad768 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V4.cs @@ -24,10 +24,10 @@ namespace Nethermind.Merge.Plugin.Test; public partial class EngineModuleTests { [TestCase( - "0xd7e58364f16b4a329b959b166f9c32323cb135669335db5dadd0344568f8dc9a", - "0xfafb92e8ece12d5fcfa867df9ae6865c5bd8aaf0b277c244552bfe869f61fb26", + "0x9233c931ff3c17ae124b9aa2ca8db1c641a2dd87fa2d7e00030b274bcc33f928", + "0xe97fdbfa2fcf60073d9579d87b127cdbeffbe6c7387b9e1e836eb7f8fb2d9548", "0xa272b2f949e4a0e411c9b45542bd5d0ef3c311b5f26c4ed6b7a8d4f605a91154", - "0x774c6aff527bbc68")] + "0x2fc07c25edadc149")] public virtual async Task Should_process_block_as_expected_V4(string latestValidHash, string blockHash, string stateRoot, string payloadId) { @@ -108,7 +108,7 @@ public virtual async Task Should_process_block_as_expected_V4(string latestValid Array.Empty(), Array.Empty(), withdrawals); - GetPayloadV4Result expectedPayload = new(block, UInt256.Zero, new BlobsBundleV1(block), executionRequests: new byte[][] { [], [], [] }); + GetPayloadV4Result expectedPayload = new(block, UInt256.Zero, new BlobsBundleV1(block), executionRequests: []); response = await RpcTest.TestSerializedRequest(rpc, "engine_getPayloadV4", expectedPayloadId); successResponse = chain.JsonSerializer.Deserialize(response); @@ -121,7 +121,7 @@ public virtual async Task Should_process_block_as_expected_V4(string latestValid })); response = await RpcTest.TestSerializedRequest(rpc, "engine_newPayloadV4", - chain.JsonSerializer.Serialize(ExecutionPayloadV3.Create(block)), "[]", Keccak.Zero.ToString(true), "[\"0x\",\"0x\",\"0x\"]"); + chain.JsonSerializer.Serialize(ExecutionPayloadV3.Create(block)), "[]", Keccak.Zero.ToString(true), "[]"); successResponse = chain.JsonSerializer.Deserialize(response); successResponse.Should().NotBeNull(); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs index ca52c480c0e8..87860936d860 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs @@ -48,9 +48,9 @@ public ValidationResult ValidateParams(IReleaseSpec spec, int version, out strin return ValidationResult.Fail; } - if (ExecutionRequests.Length != ExecutionRequestExtensions.RequestPartsCount) + if (ExecutionRequests.Length > ExecutionRequestExtensions.MaxRequestsCount) { - error = "Execution requests must have exactly three items"; + error = $"Execution requests must have less than {ExecutionRequestExtensions.MaxRequestsCount} items"; return ValidationResult.Invalid; }