Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/BytesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,5 +359,11 @@ public void Or(byte[] first, byte[] second, byte[] expected)
first.AsSpan().Or(second);
first.Should().Equal(expected);
}

[Test]
public void NullableComparision()
{
Bytes.NullableEqualityComparer.Equals(null, null).Should().BeTrue();
}
}
}
14 changes: 14 additions & 0 deletions src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ namespace Nethermind.Core.Extensions
public static unsafe partial class Bytes
{
public static readonly IEqualityComparer<byte[]> EqualityComparer = new BytesEqualityComparer();
public static readonly IEqualityComparer<byte[]?> NullableEqualityComparer = new NullableBytesEqualityComparer();
public static readonly ISpanEqualityComparer<byte> SpanEqualityComparer = new SpanBytesEqualityComparer();
public static readonly BytesComparer Comparer = new();

Expand All @@ -40,6 +41,19 @@ public override int GetHashCode(byte[] obj)
}
}

private class NullableBytesEqualityComparer : EqualityComparer<byte[]?>
{
public override bool Equals(byte[]? x, byte[]? y)
{
return AreEqual(x, y);
}

public override int GetHashCode(byte[]? obj)
{
return obj?.GetSimplifiedHashCode() ?? 0;
}
}

private class SpanBytesEqualityComparer : ISpanEqualityComparer<byte>
{
public bool Equals(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y) => AreEqual(x, y);
Expand Down
26 changes: 21 additions & 5 deletions src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,22 @@ public ExecutionPayload(Block block)

public ulong Timestamp { get; set; }

private byte[][] _encodedTransactions = Array.Empty<byte[]>();

/// <summary>
/// Gets or sets an array of RLP-encoded transaction where each item is a byte list (data)
/// representing <c>TransactionType || TransactionPayload</c> or <c>LegacyTransaction</c> as defined in
/// <see href="https://eips.ethereum.org/EIPS/eip-2718">EIP-2718</see>.
/// </summary>
public byte[][] Transactions { get; set; } = Array.Empty<byte[]>();
Comment thread
flcl42 marked this conversation as resolved.
public byte[][] Transactions
{
get { return _encodedTransactions; }
set
{
_encodedTransactions = value;
_transactions = null;
}
}

/// <summary>
/// Gets or sets a collection of <see cref="Withdrawal"/> as defined in
Expand Down Expand Up @@ -139,21 +149,27 @@ public virtual bool TryGetBlock(out Block? block, UInt256? totalDifficulty = nul
}
}

private Transaction[]? _transactions = null;

/// <summary>
/// Decodes and returns an array of <see cref="Transaction"/> from <see cref="Transactions"/>.
/// </summary>
/// <returns>An RLP-decoded array of <see cref="Transaction"/>.</returns>
public Transaction[] GetTransactions() => Transactions
public Transaction[] GetTransactions() => _transactions ??= Transactions
.Select(t => Rlp.Decode<Transaction>(t, RlpBehaviors.SkipTypedWrapping))
.ToArray();

/// <summary>
/// RLP-encodes and sets the transactions specified to <see cref="Transactions"/>.
/// </summary>
/// <param name="transactions">An array of transactions to encode.</param>
public void SetTransactions(params Transaction[] transactions) => Transactions = transactions
.Select(t => Rlp.Encode(t, RlpBehaviors.SkipTypedWrapping).Bytes)
.ToArray();
public void SetTransactions(params Transaction[] transactions)
{
Transactions = transactions
.Select(t => Rlp.Encode(t, RlpBehaviors.SkipTypedWrapping).Bytes)
.ToArray();
_transactions = transactions;
}

public override string ToString() => $"{BlockNumber} ({BlockHash})";
}
Expand Down
60 changes: 14 additions & 46 deletions src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Nethermind.Core;
using Nethermind.Core.Extensions;
using Nethermind.JsonRpc;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Merge.Plugin.Handlers;
Expand All @@ -14,11 +16,13 @@ public partial class EngineRpcModule : IEngineRpcModule
{
private readonly IAsyncHandler<byte[], GetPayloadV3Result?> _getPayloadHandlerV3;

public Task<ResultWrapper<PayloadStatusV1>> engine_newPayloadV3(ExecutionPayload executionPayload, byte[][]? blobVersionedHashes = null)
public Task<ResultWrapper<PayloadStatusV1>> engine_newPayloadV3(ExecutionPayload executionPayload, byte[]?[]? blobVersionedHashes = null) =>
Validate(executionPayload, blobVersionedHashes) ?? NewPayload(executionPayload, 3);

private ResultWrapper<PayloadStatusV1>? Validate(ExecutionPayload executionPayload, byte[]?[]? blobVersionedHashes)
{
if (blobVersionedHashes is null)
ResultWrapper<PayloadStatusV1> ErrorResult(string error)
{
string error = "Blob versioned hashes must be set";
if (_logger.IsWarn) _logger.Warn(error);
return ResultWrapper<PayloadStatusV1>.Success(
new PayloadStatusV1
Expand All @@ -29,50 +33,14 @@ public Task<ResultWrapper<PayloadStatusV1>> engine_newPayloadV3(ExecutionPayload
});
}

int index = 0;

foreach (Transaction tx in executionPayload.GetTransactions())
{
if (!tx.SupportsBlobs || tx.BlobVersionedHashes is null)
{
continue;
}

foreach (byte[]? blobVersionedHash in tx.BlobVersionedHashes)
{
if (index == blobVersionedHashes.Length
|| blobVersionedHash is null
|| blobVersionedHashes[index] is null
|| !blobVersionedHash.SequenceEqual(blobVersionedHashes[index]))
{
string error = "Blob versioned hashes do not match";
if (_logger.IsWarn) _logger.Warn(error);
return ResultWrapper<PayloadStatusV1>.Success(
new PayloadStatusV1
{
Status = PayloadStatus.Invalid,
LatestValidHash = null,
ValidationError = error
});
}
index++;
}
}

if (index != blobVersionedHashes.Length)
{
string error = "Blob versioned hashes do not match";
if (_logger.IsWarn) _logger.Warn(error);
return ResultWrapper<PayloadStatusV1>.Success(
new PayloadStatusV1
{
Status = PayloadStatus.Invalid,
LatestValidHash = null,
ValidationError = error
});
}
static IEnumerable<byte[]?> FlattenHashesFromTransactions(ExecutionPayload payload) =>
payload.GetTransactions()
.Where(t => t.BlobVersionedHashes is not null)
.SelectMany(t => t.BlobVersionedHashes!);

return NewPayload(executionPayload, 3);
return blobVersionedHashes is null ? ErrorResult("Blob versioned hashes must be set")
: !FlattenHashesFromTransactions(executionPayload).SequenceEqual(blobVersionedHashes, Bytes.NullableEqualityComparer) ? ErrorResult("Blob versioned hashes do not match")
: null;
}

public async Task<ResultWrapper<GetPayloadV3Result?>> engine_getPayloadV3(byte[] payloadId) =>
Expand Down