diff --git a/src/Nethermind/Nethermind.JsonRpc/ErrorType.cs b/src/Nethermind/Nethermind.JsonRpc/ErrorType.cs
index 3b4da6c3c8d4..8ae4af13215c 100644
--- a/src/Nethermind/Nethermind.JsonRpc/ErrorType.cs
+++ b/src/Nethermind/Nethermind.JsonRpc/ErrorType.cs
@@ -87,5 +87,9 @@ public static class ErrorCodes
///
public const int UnknownBlockError = -39001;
+ ///
+ /// Unsupported fork error
+ ///
+ public const int UnsupportedFork = -38005;
}
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs
index 36459b7444e7..8f48a6e3d708 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs
@@ -84,9 +84,15 @@ private ExecutionPayload CreateParentBlockRequestOnHead(IBlockTree blockTree)
};
}
- private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Address miner, IList? withdrawals = null, ulong? dataGasUsed = null, ulong? excessDataGas = null, Transaction[]? transactions = null)
+ private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Address miner, IList? withdrawals = null, Transaction[]? transactions = null)
+ => CreateBlockRequestInternal(parent, miner, withdrawals, transactions: transactions);
+
+ private static ExecutionPayloadV3 CreateBlockRequestV3(ExecutionPayload parent, Address miner, IList? withdrawals = null, ulong? dataGasUsed = null, ulong? excessDataGas = null, Transaction[]? transactions = null)
+ => CreateBlockRequestInternal(parent, miner, withdrawals, dataGasUsed, excessDataGas, transactions: transactions);
+
+ private static T CreateBlockRequestInternal(ExecutionPayload parent, Address miner, IList? withdrawals = null, ulong? dataGasUsed = null, ulong? excessDataGas = null, Transaction[]? transactions = null) where T : ExecutionPayload, new()
{
- ExecutionPayload blockRequest = new()
+ T blockRequest = new()
{
ParentHash = parent.BlockHash,
FeeRecipient = miner,
@@ -98,10 +104,14 @@ private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Addr
LogsBloom = Bloom.Empty,
Timestamp = parent.Timestamp + 1,
Withdrawals = withdrawals,
- DataGasUsed = dataGasUsed,
- ExcessDataGas = excessDataGas,
};
+ if (blockRequest is ExecutionPayloadV3 blockRequestV3)
+ {
+ blockRequestV3.DataGasUsed = dataGasUsed;
+ blockRequestV3.ExcessDataGas = excessDataGas;
+ }
+
blockRequest.SetTransactions(transactions ?? Array.Empty());
TryCalculateHash(blockRequest, out Keccak? hash);
blockRequest.BlockHash = hash;
diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs
index ba012651ea70..f7dc238563b6 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.Setup.cs
@@ -80,12 +80,15 @@ private IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncConf
return new EngineRpcModule(
new GetPayloadV1Handler(
chain.PayloadPreparationService!,
+ chain.SpecProvider!,
chain.LogManager),
new GetPayloadV2Handler(
chain.PayloadPreparationService!,
+ chain.SpecProvider!,
chain.LogManager),
new GetPayloadV3Handler(
chain.PayloadPreparationService!,
+ chain.SpecProvider!,
chain.LogManager),
new NewPayloadHandler(
chain.BlockValidator,
diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs
index 45ac44b43cc4..28e355fa24aa 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs
@@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
+using System.IO.Abstractions;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
@@ -15,27 +17,79 @@
using Nethermind.Crypto;
using Nethermind.Evm;
using Nethermind.JsonRpc;
+using Nethermind.JsonRpc.Modules;
+using Nethermind.JsonRpc.Test;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Merge.Plugin.GC;
using Nethermind.Merge.Plugin.Handlers;
+using Nethermind.Serialization.Json;
using Nethermind.Specs.Forks;
+using Newtonsoft.Json.Linq;
using NSubstitute;
using NUnit.Framework;
-using Transaction = Nethermind.Core.Transaction;
namespace Nethermind.Merge.Plugin.Test;
public partial class EngineModuleTests
{
- [TestCaseSource(nameof(ExcessDataGasInGetPayloadV3ForDifferentSpecTestSource))]
- public async Task ExccessDataGas_should_present_in_cancun_only((IReleaseSpec Spec, bool IsExcessDataGasSet) input)
+ [Test]
+ public async Task NewPayloadV1_should_decline_post_cancun()
+ {
+ MergeTestBlockchain chain = await CreateBlockchain(releaseSpec: Cancun.Instance);
+ IEngineRpcModule rpcModule = CreateEngineModule(chain);
+ ExecutionPayload executionPayload = CreateBlockRequest(
+ CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty());
+
+ ResultWrapper errorCode = (await rpcModule.engine_newPayloadV1(executionPayload));
+
+ Assert.That(errorCode.ErrorCode, Is.EqualTo(ErrorCodes.InvalidParams));
+ }
+
+ [Test]
+ public async Task NewPayloadV2_should_decline_post_cancun()
{
- (IEngineRpcModule rpcModule, string payloadId) = await BuildAndGetPayloadV3Result(input.Spec);
+ MergeTestBlockchain chain = await CreateBlockchain(releaseSpec: Cancun.Instance);
+ IEngineRpcModule rpcModule = CreateEngineModule(chain);
+ ExecutionPayload executionPayload = CreateBlockRequest(
+ CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty());
+
+ ResultWrapper errorCode = (await rpcModule.engine_newPayloadV2(executionPayload));
+
+ Assert.That(errorCode.ErrorCode, Is.EqualTo(ErrorCodes.UnsupportedFork));
+ }
+
+ [Test]
+ public async Task NewPayloadV3_should_decline_pre_cancun_payloads()
+ {
+ MergeTestBlockchain chain = await CreateBlockchain(releaseSpec: Shanghai.Instance);
+ IEngineRpcModule rpcModule = CreateEngineModule(chain);
+ ExecutionPayloadV3 executionPayload = CreateBlockRequestV3(
+ CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty());
+
+ ResultWrapper errorCode = (await rpcModule.engine_newPayloadV3(executionPayload, new byte[0][]));
+
+ Assert.That(errorCode.ErrorCode, Is.EqualTo(ErrorCodes.UnsupportedFork));
+ }
+
+ [Test]
+ public async Task GetPayloadV3_should_decline_pre_cancun_payloads()
+ {
+ (IEngineRpcModule rpcModule, string payloadId) = await BuildAndGetPayloadV3Result(Shanghai.Instance);
ResultWrapper getPayloadResult =
await rpcModule.engine_getPayloadV3(Bytes.FromHexString(payloadId));
- Assert.That(getPayloadResult.Data!.ExecutionPayload.ExcessDataGas.HasValue,
- Is.EqualTo(input.IsExcessDataGasSet));
+ Assert.That(getPayloadResult.ErrorCode,
+ Is.EqualTo(ErrorCodes.UnsupportedFork));
+ }
+
+ [Test]
+ public async Task GetPayloadV2_should_decline_post_cancun_payloads()
+ {
+ (IEngineRpcModule rpcModule, string payloadId) = await BuildAndGetPayloadV3Result(Cancun.Instance);
+ ResultWrapper getPayloadResult =
+ await rpcModule.engine_getPayloadV2(Bytes.FromHexString(payloadId));
+ Assert.That(getPayloadResult.ErrorCode,
+ Is.EqualTo(ErrorCodes.UnsupportedFork));
}
[Test]
@@ -57,13 +111,12 @@ public async Task GetPayloadV3_should_fail_on_unknown_payload()
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
- public async Task PayloadV3_should_return_all_the_blobs(int blobTxCount)
+ public async Task GetPayloadV3_should_return_all_the_blobs(int blobTxCount)
{
(IEngineRpcModule rpcModule, string payloadId) = await BuildAndGetPayloadV3Result(Cancun.Instance, blobTxCount);
var result = await rpcModule.engine_getPayloadV3(Bytes.FromHexString(payloadId));
BlobsBundleV1 getPayloadResultBlobsBundle = result.Data!.BlobsBundle!;
Assert.That(result.Data.ExecutionPayload.DataGasUsed, Is.EqualTo(DataGasCalculator.CalculateDataGas(blobTxCount)));
- Assert.That(result.Data.ExecutionPayload.ExcessDataGas, Is.Not.Null);
Assert.That(getPayloadResultBlobsBundle.Blobs!.Length, Is.EqualTo(blobTxCount));
Assert.That(getPayloadResultBlobsBundle.Commitments!.Length, Is.EqualTo(blobTxCount));
Assert.That(getPayloadResultBlobsBundle.Proofs!.Length, Is.EqualTo(blobTxCount));
@@ -71,15 +124,79 @@ public async Task PayloadV3_should_return_all_the_blobs(int blobTxCount)
[Test]
public async Task NewPayloadV3_should_decline_null_blobversionedhashes()
+ {
+
+ (JsonRpcService jsonRpcService, JsonRpcContext context, EthereumJsonSerializer serializer, ExecutionPayloadV3 executionPayload)
+ = await PreparePayloadRequestEnv();
+
+ string executionPayloadString = serializer.Serialize(executionPayload);
+ string blobsString = serializer.Serialize(Array.Empty());
+
+ JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3),
+ executionPayloadString, null!);
+ JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse;
+ Assert.That(response?.Error, Is.Not.Null);
+ Assert.That(response.Error.Code, Is.EqualTo(ErrorCodes.InvalidParams));
+ }
+
+ private async Task<(JsonRpcService jsonRpcService, JsonRpcContext context, EthereumJsonSerializer serializer, ExecutionPayloadV3 correctExecutionPayload)>
+ PreparePayloadRequestEnv()
{
MergeTestBlockchain chain = await CreateBlockchain(releaseSpec: Cancun.Instance);
IEngineRpcModule rpcModule = CreateEngineModule(chain);
- ExecutionPayload executionPayload = CreateBlockRequest(
- CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty());
+ JsonRpcConfig jsonRpcConfig = new() { EnabledModules = new[] { "Engine" } };
+ RpcModuleProvider moduleProvider = new(new FileSystem(), jsonRpcConfig, LimboLogs.Instance);
+ moduleProvider.Register(new SingletonModulePool(new SingletonFactory(rpcModule), true));
+
+ ExecutionPayloadV3 executionPayload = CreateBlockRequestV3(
+ CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty(), dataGasUsed: 0, excessDataGas: 0);
+
+ return (new(moduleProvider, LimboLogs.Instance, jsonRpcConfig), new(RpcEndpoint.Http), new(), executionPayload);
+ }
+
+ [Test]
+ public async Task NewPayloadV3_should_decline_empty_fields()
+ {
+ (JsonRpcService jsonRpcService, JsonRpcContext context, EthereumJsonSerializer serializer, ExecutionPayloadV3 executionPayload)
+ = await PreparePayloadRequestEnv();
+
+ string executionPayloadString = serializer.Serialize(executionPayload);
+ string blobsString = serializer.Serialize(Array.Empty());
+
+ {
+ JObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString);
+ JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3),
+ serializer.Serialize(executionPayloadAsJObject), blobsString);
+ JsonRpcResponse response = await jsonRpcService.SendRequestAsync(request, context);
+ Assert.That(response is JsonRpcSuccessResponse);
+ }
+
+ string[] props = serializer.Deserialize(serializer.Serialize(new ExecutionPayload()))
+ .Properties().Select(prop => prop.Name).ToArray();
- ResultWrapper errorCode = (await rpcModule.engine_newPayloadV3(executionPayload, null!));
+ foreach (string prop in props)
+ {
+ JObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString);
+ executionPayloadAsJObject[prop] = null;
+
+ JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3),
+ serializer.Serialize(executionPayloadAsJObject), blobsString);
+ JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse;
+ Assert.That(response?.Error, Is.Not.Null);
+ Assert.That(response.Error.Code, Is.EqualTo(ErrorCodes.InvalidParams));
+ }
- Assert.That(errorCode.Data.Status, Is.EqualTo(PayloadStatus.Invalid));
+ foreach (string prop in props)
+ {
+ JObject executionPayloadAsJObject = serializer.Deserialize(executionPayloadString);
+ executionPayloadAsJObject.Remove(prop);
+
+ JsonRpcRequest request = RpcTest.GetJsonRequest(nameof(IEngineRpcModule.engine_newPayloadV3),
+ serializer.Serialize(executionPayloadAsJObject), blobsString);
+ JsonRpcErrorResponse? response = (await jsonRpcService.SendRequestAsync(request, context)) as JsonRpcErrorResponse;
+ Assert.That(response?.Error, Is.Not.Null);
+ Assert.That(response.Error.Code, Is.EqualTo(ErrorCodes.InvalidParams));
+ }
}
private const string FurtherValidationStatus = "FurtherValidation";
@@ -162,8 +279,8 @@ public async Task NewPayloadV3_should_verify_blob_versioned_hashes_again
(MergeTestBlockchain blockchain, IEngineRpcModule engineRpcModule) = await MockRpc();
(byte[][] blobVersionedHashes, Transaction[] transactions) = BuildTransactionsAndBlobVersionedHashesList(hashesFirstBytes, transactionsAndFirstBytesOfTheirHashes, blockchain.SpecProvider.ChainId);
- ExecutionPayload executionPayload = CreateBlockRequest(
- CreateParentBlockRequestOnHead(blockchain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty(), transactions: transactions);
+ ExecutionPayloadV3 executionPayload = CreateBlockRequestV3(
+ CreateParentBlockRequestOnHead(blockchain.BlockTree), TestItem.AddressD, withdrawals: Array.Empty(), 0, 0, transactions: transactions);
ResultWrapper result = await engineRpcModule.engine_newPayloadV3(executionPayload, blobVersionedHashes);
return result.Data.Status;
@@ -225,7 +342,7 @@ public static IEnumerable BlobVersionedHashesDoNotMatchTestSource
private async Task SendNewBlockV3(IEngineRpcModule rpc, MergeTestBlockchain chain, IList? withdrawals)
{
- ExecutionPayload executionPayload = CreateBlockRequest(
+ ExecutionPayloadV3 executionPayload = CreateBlockRequestV3(
CreateParentBlockRequestOnHead(chain.BlockTree), TestItem.AddressD, withdrawals, 0, 0);
ResultWrapper executePayloadResult = await rpc.engine_newPayloadV3(executionPayload, Array.Empty());
@@ -268,10 +385,4 @@ private async Task SendNewBlockV3(IEngineRpcModule rpc, MergeT
.PayloadId!;
return (rpcModule, payloadId);
}
-
- protected static IEnumerable<(IReleaseSpec Spec, bool IsExcessDataGasSet)> ExcessDataGasInGetPayloadV3ForDifferentSpecTestSource()
- {
- yield return (Shanghai.Instance, false);
- yield return (Cancun.Instance, true);
- }
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs
index 2c24119198f7..445b63a1fa74 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs
@@ -9,17 +9,16 @@
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Int256;
-using Nethermind.Serialization.Json;
+using Nethermind.Merge.Plugin.Handlers;
using Nethermind.Serialization.Rlp;
using Nethermind.State.Proofs;
-using Newtonsoft.Json;
namespace Nethermind.Merge.Plugin.Data;
///
/// Represents an object mapping the ExecutionPayload structure of the beacon chain spec.
///
-public class ExecutionPayload
+public class ExecutionPayload : IForkValidator, IExecutionPayloadParams
{
public ExecutionPayload() { } // Needed for tests
@@ -39,8 +38,6 @@ public ExecutionPayload(Block block)
Timestamp = block.Timestamp;
BaseFeePerGas = block.BaseFeePerGas;
Withdrawals = block.Withdrawals;
- DataGasUsed = block.DataGasUsed;
- ExcessDataGas = block.ExcessDataGas;
SetTransactions(block.Transactions);
}
@@ -94,20 +91,6 @@ public byte[][] Transactions
///
public IEnumerable? Withdrawals { get; set; }
- ///
- /// Gets or sets as defined in
- /// EIP-4844.
- ///
- [JsonProperty(ItemConverterType = typeof(NullableUInt256Converter), NullValueHandling = NullValueHandling.Ignore)]
- public ulong? DataGasUsed { get; set; }
-
- ///
- /// Gets or sets as defined in
- /// EIP-4844.
- ///
- [JsonProperty(ItemConverterType = typeof(NullableUInt256Converter), NullValueHandling = NullValueHandling.Ignore)]
- public ulong? ExcessDataGas { get; set; }
-
///
/// Creates the execution block from payload.
@@ -128,9 +111,7 @@ public virtual bool TryGetBlock(out Block? block, UInt256? totalDifficulty = nul
BlockNumber,
GasLimit,
Timestamp,
- ExtraData,
- DataGasUsed,
- ExcessDataGas)
+ ExtraData)
{
Hash = BlockHash,
ReceiptsRoot = ReceiptsRoot,
@@ -159,6 +140,7 @@ public virtual bool TryGetBlock(out Block? block, UInt256? totalDifficulty = nul
}
}
+
private Transaction[]? _transactions = null;
///
@@ -182,20 +164,20 @@ public void SetTransactions(params Transaction[] transactions)
}
public override string ToString() => $"{BlockNumber} ({BlockHash.ToShortString()})";
-}
-public static class ExecutionPayloadExtensions
-{
- public static int GetVersion(this ExecutionPayload executionPayload) =>
- executionPayload.Withdrawals is null ? 1 : 2;
-
- public static bool Validate(
- this ExecutionPayload executionPayload,
- 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 actualVersion = executionPayload.GetVersion();
+ int GetVersion() => Withdrawals is null ? 1 : 2;
+
+ if (spec.IsEip4844Enabled)
+ {
+ error = "ExecutionPayloadV3 expected";
+ return ValidationResult.Fail;
+ }
+
+ int actualVersion = GetVersion();
error = actualVersion switch
{
@@ -204,15 +186,9 @@ public static bool Validate(
_ => actualVersion > version ? $"ExecutionPayloadV{version} expected" : null
};
- return error is null;
+ return error is null ? ValidationResult.Success : ValidationResult.Fail;
}
- public static bool Validate(this ExecutionPayload executionPayload,
- ISpecProvider specProvider,
- int version,
- [NotNullWhen(false)] out string? error) =>
- executionPayload.Validate(
- specProvider.GetSpec(executionPayload.BlockNumber, executionPayload.Timestamp),
- version,
- out error);
+ public virtual bool ValidateFork(ISpecProvider specProvider) =>
+ !specProvider.GetSpec(BlockNumber, Timestamp).IsEip4844Enabled;
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs
new file mode 100644
index 000000000000..94f7170cd02f
--- /dev/null
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayloadV3.cs
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
+// SPDX-License-Identifier: LGPL-3.0-only
+
+using System.Diagnostics.CodeAnalysis;
+using Nethermind.Core;
+using Nethermind.Core.Specs;
+using Nethermind.Int256;
+using Newtonsoft.Json;
+
+namespace Nethermind.Merge.Plugin.Data;
+
+///
+/// Represents an object mapping the ExecutionPayloadV3 structure of the beacon chain spec.
+///
+[JsonObject(ItemRequired = Required.Always)]
+public class ExecutionPayloadV3 : ExecutionPayload
+{
+ public ExecutionPayloadV3() { } // Needed for tests
+
+ public ExecutionPayloadV3(Block block) : base(block)
+ {
+ DataGasUsed = block.DataGasUsed;
+ ExcessDataGas = block.ExcessDataGas;
+ }
+
+ ///
+ /// Gets or sets as defined in
+ /// EIP-4844.
+ ///
+ public ulong? DataGasUsed { get; set; }
+
+ ///
+ /// Gets or sets as defined in
+ /// EIP-4844.
+ ///
+ public ulong? ExcessDataGas { get; set; }
+
+ public override bool TryGetBlock(out Block? block, UInt256? totalDifficulty = null)
+ {
+ if (!base.TryGetBlock(out block, totalDifficulty))
+ {
+ return false;
+ }
+
+ block!.Header.DataGasUsed = DataGasUsed;
+ block.Header.ExcessDataGas = ExcessDataGas;
+ return true;
+ }
+
+ public override bool ValidateFork(ISpecProvider specProvider) =>
+ specProvider.GetSpec(BlockNumber, Timestamp).IsEip4844Enabled;
+}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs
index 1d261ca65d2b..78fffe38a86f 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV2Result.cs
@@ -2,12 +2,13 @@
// SPDX-License-Identifier: LGPL-3.0-only
using Nethermind.Core;
+using Nethermind.Core.Specs;
using Nethermind.Int256;
-using Newtonsoft.Json;
+using Nethermind.Merge.Plugin.Handlers;
namespace Nethermind.Merge.Plugin.Data;
-public class GetPayloadV2Result
+public class GetPayloadV2Result : IForkValidator
{
public GetPayloadV2Result(Block block, UInt256 blockFees)
{
@@ -17,7 +18,9 @@ public GetPayloadV2Result(Block block, UInt256 blockFees)
public UInt256 BlockValue { get; }
- public ExecutionPayload ExecutionPayload { get; }
+ public virtual ExecutionPayload ExecutionPayload { get; }
+
+ public bool ValidateFork(ISpecProvider specProvider) => ExecutionPayload.ValidateFork(specProvider);
public override string ToString() => $"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}}}";
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs
index b3730948fad1..c97b48c06f08 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/GetPayloadV3Result.cs
@@ -10,11 +10,14 @@ public class GetPayloadV3Result : GetPayloadV2Result
{
public GetPayloadV3Result(Block block, UInt256 blockFees, BlobsBundleV1 blobsBundle) : base(block, blockFees)
{
+ ExecutionPayload = new(block);
BlobsBundle = blobsBundle;
}
public BlobsBundleV1 BlobsBundle { get; }
+ public override ExecutionPayloadV3 ExecutionPayload { get; }
+
public override string ToString() =>
$"{{ExecutionPayload: {ExecutionPayload}, Fees: {BlockValue}, BlobsBundle blobs count: {BlobsBundle.Blobs.Length}}}";
}
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 000000000000..51723767251a
--- /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 c92f8b9475d2..8a27a65fb105 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 0e99f729adc2..41aa57d7b61b 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Cancun.cs
@@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
+using System;
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;
@@ -16,32 +16,8 @@ public partial class EngineRpcModule : IEngineRpcModule
{
private readonly IAsyncHandler _getPayloadHandlerV3;
- public Task> engine_newPayloadV3(ExecutionPayload executionPayload, byte[]?[]? blobVersionedHashes = null) =>
- Validate(executionPayload, blobVersionedHashes) ?? NewPayload(executionPayload, 3);
-
- private ResultWrapper? Validate(ExecutionPayload 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(ExecutionPayload payload) =>
- payload.GetTransactions()
- .Where(t => t.BlobVersionedHashes is not null)
- .SelectMany(t => t.BlobVersionedHashes!);
-
- 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 Task> engine_newPayloadV3(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes) =>
+ 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 3bb0ca79dc62..585af416360c 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.Validate(_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/Handlers/GetPayloadHandlerBase.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadHandlerBase.cs
index 501a68167074..5e04823bfe6c 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadHandlerBase.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadHandlerBase.cs
@@ -4,23 +4,26 @@
using System.Threading.Tasks;
using Nethermind.Core;
using Nethermind.Core.Extensions;
+using Nethermind.Core.Specs;
using Nethermind.JsonRpc;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.BlockProduction;
-using Nethermind.Merge.Plugin.Data;
namespace Nethermind.Merge.Plugin.Handlers;
-public abstract class GetPayloadHandlerBase : IAsyncHandler
+public abstract class GetPayloadHandlerBase : IAsyncHandler where TGetPayloadResult : IForkValidator
{
private readonly int _apiVersion;
private readonly IPayloadPreparationService _payloadPreparationService;
+ private readonly ISpecProvider _specProvider;
private readonly ILogger _logger;
- protected GetPayloadHandlerBase(int apiVersion, IPayloadPreparationService payloadPreparationService, ILogManager logManager)
+ protected GetPayloadHandlerBase(int apiVersion, IPayloadPreparationService payloadPreparationService,
+ ISpecProvider specProvider, ILogManager logManager)
{
_apiVersion = apiVersion;
_payloadPreparationService = payloadPreparationService;
+ _specProvider = specProvider;
_logger = logManager.GetClassLogger();
}
@@ -37,11 +40,19 @@ protected GetPayloadHandlerBase(int apiVersion, IPayloadPreparationService paylo
return ResultWrapper.Fail("unknown payload", MergeErrorCodes.UnknownPayload);
}
+ TGetPayloadResult getPayloadResult = GetPayloadResultFromBlock(blockContext);
+
+ if (!getPayloadResult.ValidateFork(_specProvider))
+ {
+ if (_logger.IsWarn) _logger.Warn($"The payload is not supported by the current fork");
+ return ResultWrapper.Fail("unsupported fork", ErrorCodes.UnsupportedFork);
+ }
+
if (_logger.IsInfo) _logger.Info($"GetPayloadV{_apiVersion} result: {block.Header.ToString(BlockHeader.Format.Full)}.");
Metrics.GetPayloadRequests++;
Metrics.NumberOfTransactionsInGetPayload = block.Transactions.Length;
- return ResultWrapper.Success(GetPayloadResultFromBlock(blockContext));
+ return ResultWrapper.Success(getPayloadResult);
}
protected abstract TGetPayloadResult GetPayloadResultFromBlock(IBlockProductionContext blockProductionContext);
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs
index 295859ae19a0..20a27945f9b3 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV1Handler.cs
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
+using Nethermind.Core.Specs;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.BlockProduction;
using Nethermind.Merge.Plugin.Data;
@@ -24,8 +25,8 @@ namespace Nethermind.Merge.Plugin.Handlers;
///
public class GetPayloadV1Handler : GetPayloadHandlerBase
{
- public GetPayloadV1Handler(IPayloadPreparationService payloadPreparationService, ILogManager logManager) : base(
- 1, payloadPreparationService, logManager)
+ public GetPayloadV1Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) : base(
+ 1, payloadPreparationService, specProvider, logManager)
{
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV2Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV2Handler.cs
index 2932fe02cc02..1fd7b21355b4 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV2Handler.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV2Handler.cs
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
+using Nethermind.Core.Specs;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.BlockProduction;
using Nethermind.Merge.Plugin.Data;
@@ -13,8 +14,8 @@ namespace Nethermind.Merge.Plugin.Handlers;
///
public class GetPayloadV2Handler : GetPayloadHandlerBase
{
- public GetPayloadV2Handler(IPayloadPreparationService payloadPreparationService, ILogManager logManager) : base(
- 2, payloadPreparationService, logManager)
+ public GetPayloadV2Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) : base(
+ 2, payloadPreparationService, specProvider, logManager)
{
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV3Handler.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV3Handler.cs
index dd1f0ba962c4..9555b51f1a9b 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV3Handler.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/GetPayloadV3Handler.cs
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
+using Nethermind.Core.Specs;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.BlockProduction;
using Nethermind.Merge.Plugin.Data;
@@ -13,8 +14,8 @@ namespace Nethermind.Merge.Plugin.Handlers;
///
public class GetPayloadV3Handler : GetPayloadHandlerBase
{
- public GetPayloadV3Handler(IPayloadPreparationService payloadPreparationService, ILogManager logManager) : base(
- 3, payloadPreparationService, logManager)
+ public GetPayloadV3Handler(IPayloadPreparationService payloadPreparationService, ISpecProvider specProvider, ILogManager logManager) : base(
+ 3, payloadPreparationService, specProvider, logManager)
{
}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IForkValidator.cs b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IForkValidator.cs
new file mode 100644
index 000000000000..ea0592f9c9ff
--- /dev/null
+++ b/src/Nethermind/Nethermind.Merge.Plugin/Handlers/IForkValidator.cs
@@ -0,0 +1,11 @@
+// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
+// SPDX-License-Identifier: LGPL-3.0-only
+
+using Nethermind.Core.Specs;
+
+namespace Nethermind.Merge.Plugin.Handlers;
+
+public interface IForkValidator
+{
+ bool ValidateFork(ISpecProvider specProvider);
+}
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Cancun.cs b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Cancun.cs
index 43eb090084a8..f22b0bcb5f7e 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Cancun.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.Cancun.cs
@@ -14,7 +14,7 @@ public partial interface IEngineRpcModule : IRpcModule
Description = "Verifies the payload according to the execution environment rules and returns the verification status and hash of the last valid block.",
IsSharable = true,
IsImplemented = true)]
- Task> engine_newPayloadV3(ExecutionPayload executionPayload, byte[][]? blobVersionedHashes = null);
+ Task> engine_newPayloadV3(ExecutionPayloadV3 executionPayload, byte[]?[] blobVersionedHashes);
[JsonRpcMethod(
Description = "Returns the most recent version of an execution payload and fees with respect to the transaction set contained by the mempool.",
diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs
index 5aa639b66625..ecabc6581d8b 100644
--- a/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs
+++ b/src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs
@@ -307,9 +307,9 @@ public Task InitRpcModules()
_api.RpcCapabilitiesProvider = new EngineRpcCapabilitiesProvider(_api.SpecProvider);
IEngineRpcModule engineRpcModule = new EngineRpcModule(
- new GetPayloadV1Handler(payloadPreparationService, _api.LogManager),
- new GetPayloadV2Handler(payloadPreparationService, _api.LogManager),
- new GetPayloadV3Handler(payloadPreparationService, _api.LogManager),
+ new GetPayloadV1Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager),
+ new GetPayloadV2Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager),
+ new GetPayloadV3Handler(payloadPreparationService, _api.SpecProvider, _api.LogManager),
new NewPayloadHandler(
_api.BlockValidator,
_api.BlockTree,