Skip to content

Commit

Permalink
Add flat encoding and requests as a side car (#7608)
Browse files Browse the repository at this point in the history
  • Loading branch information
rjnrohit authored Oct 17, 2024
1 parent bb41884 commit 76b3ff5
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ public void Setup()
CallOutputTracer tracer = ci.Arg<CallOutputTracer>();
if (transaction.To == eip7002Account)
{
tracer.ReturnValue = executionWithdrawalRequests.FlatEncodeWithoutType();
Span<byte> buffer = new byte[executionWithdrawalRequests.GetRequestsByteSize()];
executionWithdrawalRequests.FlatEncodeWithoutType(buffer);
tracer.ReturnValue = buffer.ToArray();
}
else if (transaction.To == eip7251Account)
{
tracer.ReturnValue = executionConsolidationRequests.FlatEncodeWithoutType();
Span<byte> buffer = new byte[executionConsolidationRequests.GetRequestsByteSize()];
executionConsolidationRequests.FlatEncodeWithoutType(buffer);
tracer.ReturnValue = buffer.ToArray();
}
else
{
Expand Down Expand Up @@ -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()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -130,50 +128,14 @@ private IEnumerable<ExecutionRequest> ReadRequests(Block block, IWorldState stat

}

public Hash256 CalculateRequestsHash(Block block, IWorldState state, TxReceipt[] receipts, IReleaseSpec spec, out ArrayPoolList<ExecutionRequest> requests)
{
ArrayPoolList<ExecutionRequest> requestsList = new ArrayPoolList<ExecutionRequest>(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<ExecutionRequest> requests);
block.ExecutionRequests = requests;
IEnumerable<ExecutionRequest> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public BlockBuilder WithReceiptsRoot(Hash256 keccak)
public BlockBuilder WithEmptyRequestsHash()
{
TestObjectInternal.Header.RequestsHash = Array.Empty<ExecutionRequest.ExecutionRequest>().CalculateHash();
TestObjectInternal.ExecutionRequests = new ArrayPoolList<ExecutionRequest.ExecutionRequest>(0);
TestObjectInternal.ExecutionRequests = Array.Empty<ExecutionRequest.ExecutionRequest>();
return this;
}

Expand Down
18 changes: 9 additions & 9 deletions src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public Transaction[] Transactions
public Hash256? RequestsHash => Header.RequestsHash; // do not add setter here

[JsonIgnore]
public ArrayPoolList<ExecutionRequest.ExecutionRequest>? ExecutionRequests { get; set; }
public ExecutionRequest.ExecutionRequest[]? ExecutionRequests { get; set; }

[JsonIgnore]
public ArrayPoolList<AddressAsKey>? AccountChanges { get; set; }
Expand Down
85 changes: 57 additions & 28 deletions src/Nethermind/Nethermind.Core/ExecutionRequest/ExecutionRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -24,60 +23,90 @@ public class ExecutionRequest
public byte RequestType { get; set; }
public byte[]? RequestData { get; set; }

public byte[] FlatEncode()
public void FlatEncode(Span<byte> 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<ExecutionRequest> requests)
{
List<byte> 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<byte> buffer)
{
List<byte> encoded = new();
int currentPosition = 0;

foreach (ExecutionRequest request in requests)
{
encoded.AddRange(request.RequestData!);
Span<byte> 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<byte> 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<ExecutionRequest> requests)
{
using (SHA256 sha256 = SHA256.Create())
{
using (SHA256 sha256Inner = SHA256.Create())
Span<byte> 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<byte> 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()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ public void ProcessExecutionRequests(Block block, IWorldState state, TxReceipt[]
if (block.IsGenesis)
return;

block.ExecutionRequests = new ArrayPoolList<ExecutionRequest>(Requests.Length);
foreach (var request in Requests)
{
block.ExecutionRequests.Add(request);
}
block.ExecutionRequests = Requests;
block.Header.RequestsHash = Requests.CalculateHash();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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}}}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down

0 comments on commit 76b3ff5

Please sign in to comment.