diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 5638afebb00..445b63a1fa7 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -18,7 +18,7 @@ namespace Nethermind.Merge.Plugin.Data; /// /// Represents an object mapping the ExecutionPayload structure of the beacon chain spec. /// -public class ExecutionPayload : IForkValidator +public class ExecutionPayload : IForkValidator, IExecutionPayloadParams { public ExecutionPayload() { } // Needed for tests @@ -165,14 +165,16 @@ public void SetTransactions(params Transaction[] transactions) public override string ToString() => $"{BlockNumber} ({BlockHash.ToShortString()})"; - public virtual bool ValidateParams(IReleaseSpec spec, int version, [NotNullWhen(false)] out string? error) + ExecutionPayload IExecutionPayloadParams.ExecutionPayload => this; + + public virtual ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error) { int GetVersion() => Withdrawals is null ? 1 : 2; if (spec.IsEip4844Enabled) { error = "ExecutionPayloadV3 expected"; - return false; + return ValidationResult.Fail; } int actualVersion = GetVersion(); @@ -184,12 +186,9 @@ public virtual bool ValidateParams(IReleaseSpec spec, int version, [NotNullWhen( _ => actualVersion > version ? $"ExecutionPayloadV{version} expected" : null }; - return error is null; + return error is null ? ValidationResult.Success : ValidationResult.Fail; } public virtual bool ValidateFork(ISpecProvider specProvider) => !specProvider.GetSpec(BlockNumber, Timestamp).IsEip4844Enabled; - - public bool ValidateParams(ISpecProvider specProvider, int version, [NotNullWhen(false)] out string? error) => - ValidateParams(specProvider.GetSpec(BlockNumber, Timestamp), version, out error); } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs index 1c6dbc4b1dc..94f7170cd02 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs @@ -47,13 +47,6 @@ public override bool TryGetBlock(out Block? block, UInt256? totalDifficulty = nu return true; } - public override bool ValidateParams(IReleaseSpec spec, int version, [NotNullWhen(false)] out string? error) - { - // handled by `JsonObject` attribute of the class - error = null; - return true; - } - public override bool ValidateFork(ISpecProvider specProvider) => specProvider.GetSpec(BlockNumber, Timestamp).IsEip4844Enabled; } diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs new file mode 100644 index 00000000000..51723767251 --- /dev/null +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/IExecutionPayloadParams.cs @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; + +namespace Nethermind.Merge.Plugin.Data; + +public interface IExecutionPayloadParams +{ + ExecutionPayload ExecutionPayload { get; } + ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error); +} + +public enum ValidationResult : byte { Success, Fail, Invalid }; + +public class ExecutionPayloadV3Params : IExecutionPayloadParams +{ + private readonly ExecutionPayloadV3 _executionPayload; + private readonly byte[]?[] _blobVersionedHashes; + + public ExecutionPayloadV3Params(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes) + { + _executionPayload = executionPayload; + _blobVersionedHashes = blobVersionedHashes; + } + + public ExecutionPayload ExecutionPayload => _executionPayload; + + public ValidationResult ValidateParams(IReleaseSpec spec, int version, out string? error) + { + static IEnumerable FlattenHashesFromTransactions(ExecutionPayloadV3 payload) => + payload.GetTransactions() + .Where(t => t.BlobVersionedHashes is not null) + .SelectMany(t => t.BlobVersionedHashes!); + + if (FlattenHashesFromTransactions(_executionPayload).SequenceEqual(_blobVersionedHashes, Bytes.NullableEqualityComparer)) + { + error = null; + return ValidationResult.Success; + } + + error = "Blob versioned hashes do not match"; + return ValidationResult.Invalid; + } +} diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadStatusV1.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadStatusV1.cs index c92f8b9475d..8a27a65fb10 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadStatusV1.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/PayloadStatusV1.cs @@ -17,10 +17,11 @@ public class PayloadStatusV1 public static readonly PayloadStatusV1 Accepted = new() { Status = PayloadStatus.Accepted }; - public static PayloadStatusV1 Invalid(Keccak? latestValidHash) => new() + public static PayloadStatusV1 Invalid(Keccak? latestValidHash, string? validationError = null) => new() { Status = PayloadStatus.Invalid, - LatestValidHash = latestValidHash + LatestValidHash = latestValidHash, + ValidationError = validationError }; /// diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs index 470d191e67d..41aa57d7b61 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs @@ -17,44 +17,7 @@ public partial class EngineRpcModule : IEngineRpcModule private readonly IAsyncHandler _getPayloadHandlerV3; public Task> engine_newPayloadV3(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes) => - ValidateFork(executionPayload) ?? PreValidatePayload(executionPayload, blobVersionedHashes) ?? NewPayload(executionPayload, 3); - - private ResultWrapper? PreValidatePayload(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes) - { - ResultWrapper ErrorResult(string error) - { - if (_logger.IsWarn) _logger.Warn(error); - return ResultWrapper.Success( - new PayloadStatusV1 - { - Status = PayloadStatus.Invalid, - LatestValidHash = null, - ValidationError = error - }); - } - - static IEnumerable FlattenHashesFromTransactions(ExecutionPayloadV3 payload) => - payload.GetTransactions() - .Where(t => t.BlobVersionedHashes is not null) - .SelectMany(t => t.BlobVersionedHashes!); - - return !FlattenHashesFromTransactions(executionPayload).SequenceEqual(blobVersionedHashes, Bytes.NullableEqualityComparer) - ? ErrorResult("Blob versioned hashes do not match") - : null; - } - - private ResultWrapper? ValidateFork(ExecutionPayload executionPayload) - { - if (executionPayload.ValidateFork(_specProvider)) - { - return null; - } - else - { - if (_logger.IsWarn) _logger.Warn($"The payload is not supported by the current fork"); - return ResultWrapper.Fail("unsupported fork", ErrorCodes.UnsupportedFork); - } - } + NewPayload(new ExecutionPayloadV3Params(executionPayload, blobVersionedHashes), 3); public async Task> engine_getPayloadV3(byte[] payloadId) => await _getPayloadHandlerV3.HandleAsync(payloadId); diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs index 8a09b6399ce..585af416360 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Nethermind.Consensus.Producers; +using Nethermind.Core.Specs; using Nethermind.JsonRpc; using Nethermind.Merge.Plugin.Data; using Nethermind.Merge.Plugin.GC; @@ -64,12 +65,24 @@ private async Task> ForkchoiceUpdated(F } } - private async Task> NewPayload(ExecutionPayload executionPayload, int version) + private async Task> NewPayload(IExecutionPayloadParams executionPayloadParams, int version) { - if (!executionPayload.ValidateParams(_specProvider, version, out string? error)) + ExecutionPayload executionPayload = executionPayloadParams.ExecutionPayload; + + if (!executionPayload.ValidateFork(_specProvider)) + { + if (_logger.IsWarn) _logger.Warn($"The payload is not supported by the current fork"); + return ResultWrapper.Fail("unsupported fork", version < 2 ? ErrorCodes.InvalidParams : ErrorCodes.UnsupportedFork); + } + + IReleaseSpec releaseSpec = _specProvider.GetSpec(executionPayload.BlockNumber, executionPayload.Timestamp); + ValidationResult validationResult = executionPayloadParams.ValidateParams(releaseSpec, version, out string? error); + if (validationResult != ValidationResult.Success) { if (_logger.IsWarn) _logger.Warn(error); - return ResultWrapper.Fail(error, ErrorCodes.InvalidParams); + return validationResult == ValidationResult.Fail + ? ResultWrapper.Fail(error!, ErrorCodes.InvalidParams) + : ResultWrapper.Success(PayloadStatusV1.Invalid(null, error)); } if (await _locker.WaitAsync(_timeout)) diff --git a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs index ec1e72e540b..99792015ae3 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs @@ -30,5 +30,5 @@ public Task> engine_forkchoiceUpdatedV2 => _executionGetPayloadBodiesByRangeV1Handler.Handle(start, count); public Task> engine_newPayloadV2(ExecutionPayload executionPayload) - => ValidateFork(executionPayload) ?? NewPayload(executionPayload, 2); + => NewPayload(executionPayload, 2); }