diff --git a/src/Nethermind/Nethermind.Consensus.Test/ExecutionRequestProcessorTests.cs b/src/Nethermind/Nethermind.Consensus.Test/ExecutionRequestProcessorTests.cs index e7731326ea9..4e4bd95e4e9 100644 --- a/src/Nethermind/Nethermind.Consensus.Test/ExecutionRequestProcessorTests.cs +++ b/src/Nethermind/Nethermind.Consensus.Test/ExecutionRequestProcessorTests.cs @@ -76,11 +76,15 @@ public void Setup() CallOutputTracer tracer = ci.Arg(); if (transaction.To == eip7002Account) { - tracer.ReturnValue = executionWithdrawalRequests.FlatEncodeWithoutType(); + Span buffer = new byte[executionWithdrawalRequests.GetRequestsByteSize()]; + executionWithdrawalRequests.FlatEncodeWithoutType(buffer); + tracer.ReturnValue = buffer.ToArray(); } else if (transaction.To == eip7251Account) { - tracer.ReturnValue = executionConsolidationRequests.FlatEncodeWithoutType(); + Span buffer = new byte[executionConsolidationRequests.GetRequestsByteSize()]; + executionConsolidationRequests.FlatEncodeWithoutType(buffer); + tracer.ReturnValue = buffer.ToArray(); } else { @@ -120,7 +124,7 @@ .. executionConsolidationRequests ])) { Assert.That(processedRequest.RequestType, Is.EqualTo(expectedRequest.RequestType)); - Assert.That(processedRequest.RequestData, Is.EqualTo(expectedRequest.RequestData)); + Assert.That(processedRequest.RequestData.ToArray(), Is.EqualTo(expectedRequest.RequestData.ToArray())); } Assert.That(block.Header.RequestsHash, Is.EqualTo(block.ExecutionRequests.ToArray().CalculateHash())); diff --git a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs index f02f6f70ed0..0d46820272e 100644 --- a/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs +++ b/src/Nethermind/Nethermind.Consensus/ExecutionRequests/ExecutionRequestProcessor.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography; using Nethermind.Abi; using Nethermind.Core; using Nethermind.Core.Collections; -using Nethermind.Core.Crypto; using Nethermind.Core.ExecutionRequest; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -130,50 +128,14 @@ private IEnumerable ReadRequests(Block block, IWorldState stat } - public Hash256 CalculateRequestsHash(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, out ArrayPoolList requests) - { - ArrayPoolList requestsList = new ArrayPoolList(receipts.Length * 2); - using (SHA256 sha256 = SHA256.Create()) - { - using (SHA256 sha256Inner = SHA256.Create()) - { - foreach (ExecutionRequest request in ProcessDeposits(receipts, spec)) - { - requestsList.AddRange(request); - byte[] requestHash = sha256Inner.ComputeHash(request.FlatEncode()); - - // Update the outer hash with the result of each inner hash - sha256.TransformBlock(requestHash, 0, requestHash.Length, null, 0); - } - foreach (ExecutionRequest request in ReadRequests(block, state, spec, spec.Eip7002ContractAddress)) - { - requestsList.AddRange(request); - byte[] requestHash = sha256Inner.ComputeHash(request.FlatEncode()); - - sha256.TransformBlock(requestHash, 0, requestHash.Length, null, 0); - } - - foreach (ExecutionRequest request in ReadRequests(block, state, spec, spec.Eip7251ContractAddress)) - { - requestsList.AddRange(request); - byte[] requestHash = sha256Inner.ComputeHash(request.FlatEncode()); - - sha256.TransformBlock(requestHash, 0, requestHash.Length, null, 0); - } - - // Complete the final hash computation - sha256.TransformFinalBlock(new byte[0], 0, 0); - requests = requestsList; - return new Hash256(sha256.Hash!); - } - } - } - public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec) { if (!spec.RequestsEnabled) return; - block.Header.RequestsHash = CalculateRequestsHash(block, state, receipts, spec, out ArrayPoolList requests); - block.ExecutionRequests = requests; + IEnumerable requests = ProcessDeposits(receipts, spec) + .Concat(ReadRequests(block, state, spec, spec.Eip7002ContractAddress)) + .Concat(ReadRequests(block, state, spec, spec.Eip7251ContractAddress)); + block.Header.RequestsHash = requests.CalculateHash(); + block.ExecutionRequests = requests.ToArray(); } } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs index ee80f1bb81a..e66e47f283c 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs @@ -252,7 +252,7 @@ public BlockBuilder WithReceiptsRoot(Hash256 keccak) public BlockBuilder WithEmptyRequestsHash() { TestObjectInternal.Header.RequestsHash = Array.Empty().CalculateHash(); - TestObjectInternal.ExecutionRequests = new ArrayPoolList(0); + TestObjectInternal.ExecutionRequests = Array.Empty(); return this; } diff --git a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs index e7345b1bcf3..9865dcb0089 100644 --- a/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs +++ b/src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs @@ -102,15 +102,15 @@ public static Hash256 KeccakFromNumber(int i) public static byte[] SignatureBytes = [.. new Signature("0x9242685bf161793cc25603c231bc2f568eb630ea16aa137d2664ac80388256084f8ae3bd7535248d0bd448298cc2e2071e56992d0774dc340c368ae950852ada1c").Bytes, .. KeccakA.Bytes]; - public static ExecutionRequest.ExecutionRequest ExecutionRequestA = new() { RequestType = 0, RequestData = ([.. PublicKeyA.Bytes.Slice(0, 48), .. KeccakA.Bytes, .. (BitConverter.GetBytes((ulong)1_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)1)]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestB = new() { RequestType = 0, RequestData = ([.. PublicKeyB.Bytes.Slice(0, 48), .. KeccakB.Bytes, .. (BitConverter.GetBytes((ulong)2_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)2)]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestC = new() { RequestType = 0, RequestData = ([.. PublicKeyC.Bytes.Slice(0, 48), .. KeccakC.Bytes, .. (BitConverter.GetBytes((ulong)3_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)3)]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestD = new() { RequestType = 1, RequestData = ([.. AddressA.Bytes, .. PublicKeyA.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)1_000_000_000))]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestE = new() { RequestType = 1, RequestData = ([.. AddressB.Bytes, .. PublicKeyB.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)2_000_000_000))]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestF = new() { RequestType = 1, RequestData = ([.. AddressC.Bytes, .. PublicKeyC.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)3_000_000_000))]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestG = new() { RequestType = 2, RequestData = ([.. AddressA.Bytes, .. PublicKeyA.Bytes.Slice(0, 48), .. PublicKeyB.Bytes.Slice(0, 48)]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestH = new() { RequestType = 2, RequestData = ([.. AddressB.Bytes, .. PublicKeyB.Bytes.Slice(0, 48), .. PublicKeyC.Bytes.Slice(0, 48)]) }; - public static ExecutionRequest.ExecutionRequest ExecutionRequestI = new() { RequestType = 2, RequestData = ([.. AddressC.Bytes, .. PublicKeyC.Bytes.Slice(0, 48), .. PublicKeyA.Bytes.Slice(0, 48)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestA = new() { RequestType = 0, RequestData = (byte[])([.. PublicKeyA.Bytes.Slice(0, 48), .. KeccakA.Bytes, .. (BitConverter.GetBytes((ulong)1_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)1)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestB = new() { RequestType = 0, RequestData = (byte[])([.. PublicKeyB.Bytes.Slice(0, 48), .. KeccakB.Bytes, .. (BitConverter.GetBytes((ulong)2_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)2)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestC = new() { RequestType = 0, RequestData = (byte[])([.. PublicKeyC.Bytes.Slice(0, 48), .. KeccakC.Bytes, .. (BitConverter.GetBytes((ulong)3_000_000_000)), .. SignatureBytes, .. BitConverter.GetBytes((ulong)3)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestD = new() { RequestType = 1, RequestData = (byte[])([.. AddressA.Bytes, .. PublicKeyA.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)1_000_000_000))]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestE = new() { RequestType = 1, RequestData = (byte[])([.. AddressB.Bytes, .. PublicKeyB.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)2_000_000_000))]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestF = new() { RequestType = 1, RequestData = (byte[])([.. AddressC.Bytes, .. PublicKeyC.Bytes.Slice(0, 48), .. (BitConverter.GetBytes((ulong)3_000_000_000))]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestG = new() { RequestType = 2, RequestData = (byte[])([.. AddressA.Bytes, .. PublicKeyA.Bytes.Slice(0, 48), .. PublicKeyB.Bytes.Slice(0, 48)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestH = new() { RequestType = 2, RequestData = (byte[])([.. AddressB.Bytes, .. PublicKeyB.Bytes.Slice(0, 48), .. PublicKeyC.Bytes.Slice(0, 48)]) }; + public static ExecutionRequest.ExecutionRequest ExecutionRequestI = new() { RequestType = 2, RequestData = (byte[])([.. AddressC.Bytes, .. PublicKeyC.Bytes.Slice(0, 48), .. PublicKeyA.Bytes.Slice(0, 48)]) }; public static IPEndPoint IPEndPointA = IPEndPoint.Parse("10.0.0.1"); public static IPEndPoint IPEndPointB = IPEndPoint.Parse("10.0.0.2"); diff --git a/src/Nethermind/Nethermind.Core/Block.cs b/src/Nethermind/Nethermind.Core/Block.cs index 935a1b0b900..57ebf3b5726 100644 --- a/src/Nethermind/Nethermind.Core/Block.cs +++ b/src/Nethermind/Nethermind.Core/Block.cs @@ -118,7 +118,7 @@ public Transaction[] Transactions public Hash256? RequestsHash => Header.RequestsHash; // do not add setter here [JsonIgnore] - public ArrayPoolList? ExecutionRequests { get; set; } + public ExecutionRequest.ExecutionRequest[]? ExecutionRequests { get; set; } [JsonIgnore] public ArrayPoolList? AccountChanges { get; set; } diff --git a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequest.cs b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequest.cs index 29711de9dd4..7e81a8abb35 100644 --- a/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequest.cs +++ b/src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequest.cs @@ -4,9 +4,8 @@ using System; using System.Collections.Generic; -using System.Data.SqlTypes; -using System.Security.Cryptography; using System.Linq; +using System.Security.Cryptography; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; @@ -24,60 +23,90 @@ public class ExecutionRequest public byte RequestType { get; set; } public byte[]? RequestData { get; set; } - public byte[] FlatEncode() + public void FlatEncode(Span buffer) { - byte[] encoded = new byte[RequestData!.Length + 1]; - encoded[0] = RequestType; - RequestData.CopyTo(encoded, 1); - return encoded; + if (buffer.Length < RequestData!.Length + 1) + throw new ArgumentException("Buffer too small"); + + buffer[0] = RequestType; + RequestData.CopyTo(buffer.Slice(1)); } public override string ToString() => ToString(string.Empty); public string ToString(string indentation) => @$"{indentation}{nameof(ExecutionRequest)} {{{nameof(RequestType)}: {RequestType}, - {nameof(RequestData)}: {RequestData?.ToHexString()}}}"; + {nameof(RequestData)}: {RequestData!.ToHexString()}}}"; } public static class ExecutionRequestExtensions { - public static byte[] FlatEncode(this ExecutionRequest[] requests) + public static int GetRequestsByteSize(this IEnumerable requests) { - List encoded = new(); + int size = 0; foreach (ExecutionRequest request in requests) { - encoded.AddRange(request.FlatEncode()); + size += request.RequestData!.Length + 1; } - return encoded.ToArray(); + return size; } - public static byte[] FlatEncodeWithoutType(this ExecutionRequest[] requests) + public static void FlatEncode(this ExecutionRequest[] requests, Span buffer) { - List encoded = new(); + int currentPosition = 0; + foreach (ExecutionRequest request in requests) { - encoded.AddRange(request.RequestData!); + Span internalBuffer = new byte[request.RequestData!.Length + 1]; + request.FlatEncode(internalBuffer); + + // Ensure the buffer has enough space to accommodate the new data + if (currentPosition + internalBuffer.Length > buffer.Length) + { + throw new InvalidOperationException("Buffer is not large enough to hold all data of requests"); + } + + // Copy the internalBuffer to the buffer at the current position + internalBuffer.CopyTo(buffer.Slice(currentPosition, internalBuffer.Length)); + currentPosition += internalBuffer.Length; } - return encoded.ToArray(); } - public static Hash256 CalculateHash(this ExecutionRequest[] requests) + public static void FlatEncodeWithoutType(this ExecutionRequest[] requests, Span buffer) + { + int currentPosition = 0; + + foreach (ExecutionRequest request in requests) + { + // Ensure the buffer has enough space to accommodate the new data + if (currentPosition + request.RequestData!.Length > buffer.Length) + { + throw new InvalidOperationException("Buffer is not large enough to hold all data of requests"); + } + + // Copy the RequestData to the buffer at the current position + request.RequestData.CopyTo(buffer.Slice(currentPosition, request.RequestData.Length)); + currentPosition += request.RequestData.Length; + } + } + + public static Hash256 CalculateHash(this IEnumerable requests) { using (SHA256 sha256 = SHA256.Create()) { - using (SHA256 sha256Inner = SHA256.Create()) + Span concatenatedHashes = new byte[32 * requests.Count()]; + int currentPosition = 0; + // Compute sha256 for each request and concatenate them + foreach (ExecutionRequest request in requests) { - foreach (ExecutionRequest request in requests) - { - byte[] requestHash = sha256Inner.ComputeHash(request.FlatEncode()); - - // Update the outer hash with the result of each inner hash - sha256.TransformBlock(requestHash, 0, requestHash.Length, null, 0); - } - // Complete the final hash computation - sha256.TransformFinalBlock(new byte[0], 0, 0); - return new Hash256(sha256.Hash!); + Span requestBuffer = new byte[request.RequestData!.Length + 1]; + request.FlatEncode(requestBuffer); + sha256.ComputeHash(requestBuffer.ToArray()).CopyTo(concatenatedHashes.Slice(currentPosition, 32)); + currentPosition += 32; } + + // Compute sha256 of the concatenated hashes + return new Hash256(sha256.ComputeHash(concatenatedHashes.ToArray())); } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs index 4217bec0535..3f2f22bfcef 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/ExecutionRequestsProcessorMock.cs @@ -31,11 +31,7 @@ public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[] if (block.IsGenesis) return; - block.ExecutionRequests = new ArrayPoolList(Requests.Length); - foreach (var request in Requests) - { - block.ExecutionRequests.Add(request); - } + block.ExecutionRequests = Requests; block.Header.RequestsHash = Requests.CalculateHash(); } } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs index 5db3bf94e95..c3b7bef351a 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV4Result.cs @@ -12,5 +12,5 @@ public class GetPayloadV4Result(Block block, UInt256 blockFees, BlobsBundleV1 bl public ExecutionRequest[]? ExecutionRequests { get; } = ExecutionRequests; public override string ToString() => - $"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}, BlobsBundle blobs count: {BlobsBundle.Blobs.Length}, ShouldOverrideBuilder {ShouldOverrideBuilder}, ExecutionRequests : {ExecutionRequests?.FlatEncode()}}}"; + $"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}, BlobsBundle blobs count: {BlobsBundle.Blobs.Length}, ShouldOverrideBuilder {ShouldOverrideBuilder}, ExecutionRequests count : {ExecutionRequests?.Length}}}"; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs index 6a9b458fa31..d403c8c43bb 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV4Handler.cs @@ -24,7 +24,7 @@ public class GetPayloadV4Handler( { protected override GetPayloadV4Result GetPayloadResultFromBlock(IBlockProductionContext context) { - return new(context.CurrentBestBlock!, context.BlockFees, new BlobsBundleV1(context.CurrentBestBlock!), context.CurrentBestBlock!.ExecutionRequests!.ToArray()) + return new(context.CurrentBestBlock!, context.BlockFees, new BlobsBundleV1(context.CurrentBestBlock!), context.CurrentBestBlock!.ExecutionRequests!) { ShouldOverrideBuilder = censorshipDetector?.GetCensoredBlocks().Contains(new BlockNumberHash(context.CurrentBestBlock!)) ?? false };