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
35 changes: 35 additions & 0 deletions src/Nethermind/Nethermind.Consensus/Producers/PayloadAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;

namespace Nethermind.Consensus.Producers;

Expand Down Expand Up @@ -45,3 +47,36 @@ public string ToString(string indentation)
return sb.ToString();
}
}

public static class PayloadAttributesVersioningExtensions
{
public static int GetVersion(this PayloadAttributes executionPayload) =>
executionPayload.Withdrawals is null ? 1 : 2;

public static bool Validate(
this PayloadAttributes payloadAttributes,
IReleaseSpec spec,
int version,
[NotNullWhen(false)] out string? error)
{
int actualVersion = payloadAttributes.GetVersion();

error = actualVersion switch
{
1 when spec.WithdrawalsEnabled => "PayloadAttributesV2 expected",
> 1 when !spec.WithdrawalsEnabled => "PayloadAttributesV1 expected",
_ => actualVersion > version ? $"PayloadAttributesV{version} expected" : null
};

return error is null;
}

public static bool Validate(this PayloadAttributes payloadAttributes,
ISpecProvider specProvider,
int version,
[NotNullWhen(false)] out string? error) =>
payloadAttributes.Validate(
specProvider.GetSpec(ForkActivation.TimestampOnly(payloadAttributes.Timestamp)),
version,
out error);
}
67 changes: 23 additions & 44 deletions src/Nethermind/Nethermind.Core/Specs/ForkActivation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@ public ForkActivation(long blockNumber, ulong? timestamp = null)
BlockNumber = blockNumber;
Timestamp = timestamp;
}

/// <summary>
/// Fork activation for forks past The Merge/Paris
/// </summary>
/// <param name="timestamp">Timestamp of the fork or check</param>
/// <returns>Post merge fork activation</returns>
/// <remarks>
/// Post Merge are based only on timestamp and we can ignore block number.
/// </remarks>
public static ForkActivation TimestampOnly(ulong timestamp) => new(long.MaxValue, timestamp);

public void Deconstruct(out long blockNumber, out ulong? timestamp)
{
blockNumber = BlockNumber;
Expand All @@ -30,60 +41,28 @@ public static implicit operator ForkActivation((long blocknumber, ulong? timesta
public static implicit operator (long blocknumber, ulong? timestamp)(ForkActivation forkActivation)
=> (forkActivation.BlockNumber, forkActivation.Timestamp);

public bool Equals(ForkActivation other)
{
return BlockNumber == other.BlockNumber && Timestamp == other.Timestamp;
}
public bool Equals(ForkActivation other) => BlockNumber == other.BlockNumber && Timestamp == other.Timestamp;

public override bool Equals(object? obj)
{
return obj is ForkActivation other && Equals(other);
}
public override bool Equals(object? obj) => obj is ForkActivation other && Equals(other);

public override int GetHashCode()
{
return HashCode.Combine(BlockNumber, Timestamp);
}
public override int GetHashCode() => HashCode.Combine(BlockNumber, Timestamp);

public override string ToString()
{
return $"{BlockNumber} {Timestamp}";
}
public override string ToString() => $"{BlockNumber} {Timestamp}";

public int CompareTo(ForkActivation other)
{
return Timestamp is null || other.Timestamp is null
public int CompareTo(ForkActivation other) =>
Timestamp is null || other.Timestamp is null
? BlockNumber.CompareTo(other.BlockNumber)
: Timestamp.Value.CompareTo(other.Timestamp.Value);
}

public static bool operator ==(ForkActivation first, ForkActivation second)
{
return first.Equals(second);
}
public static bool operator ==(ForkActivation first, ForkActivation second) => first.Equals(second);

public static bool operator !=(ForkActivation first, ForkActivation second)
{
return !first.Equals(second);
}
public static bool operator !=(ForkActivation first, ForkActivation second) => !first.Equals(second);

public static bool operator <(ForkActivation first, ForkActivation second)
{
return first.CompareTo(second) < 0;
}
public static bool operator <(ForkActivation first, ForkActivation second) => first.CompareTo(second) < 0;

public static bool operator >(ForkActivation first, ForkActivation second)
{
return first.CompareTo(second) > 0;
}
public static bool operator >(ForkActivation first, ForkActivation second) => first.CompareTo(second) > 0;

public static bool operator <=(ForkActivation first, ForkActivation second)
{
return first.CompareTo(second) <= 0;
}
public static bool operator <=(ForkActivation first, ForkActivation second) => first.CompareTo(second) <= 0;

public static bool operator >=(ForkActivation first, ForkActivation second)
{
return first.CompareTo(second) >= 0;
}
public static bool operator >=(ForkActivation first, ForkActivation second) => first.CompareTo(second) >= 0;
}
35 changes: 35 additions & 0 deletions src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Int256;
using Nethermind.Serialization.Rlp;
using Nethermind.State.Proofs;
Expand Down Expand Up @@ -144,3 +146,36 @@ public void SetTransactions(params Transaction[] transactions) => Transactions =

public override string ToString() => $"{BlockNumber} ({BlockHash})";
}

public static class ExecutionPayloadVersioningExtensions
{
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)
{
int actualVersion = executionPayload.GetVersion();

error = actualVersion switch
{
1 when spec.WithdrawalsEnabled => "ExecutionPayloadV2 expected",
> 1 when !spec.WithdrawalsEnabled => "ExecutionPayloadV1 expected",
_ => actualVersion > version ? $"ExecutionPayloadV{version} expected" : null
};

return error is null;
}

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);
}
102 changes: 102 additions & 0 deletions src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Paris.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Nethermind.Consensus.Producers;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.JsonRpc;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Merge.Plugin.Handlers;

namespace Nethermind.Merge.Plugin
{
public partial class EngineRpcModule : IEngineRpcModule
{
private readonly IAsyncHandler<byte[], ExecutionPayload?> _getPayloadHandlerV1;
private readonly IAsyncHandler<ExecutionPayload, PayloadStatusV1> _newPayloadV1Handler;
private readonly IForkchoiceUpdatedHandler _forkchoiceUpdatedV1Handler;
Comment on lines +21 to +22
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be here or in EngineRpcModule.cs?

private readonly IHandler<TransitionConfigurationV1, TransitionConfigurationV1> _transitionConfigurationHandler;
private readonly SemaphoreSlim _locker = new(1, 1);
private readonly TimeSpan _timeout = TimeSpan.FromSeconds(8);
Comment on lines +24 to +25
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be here or in EngineRpcModule.cs?


public Task<ResultWrapper<ExecutionPayload?>> engine_getPayloadV1(byte[] payloadId) =>
_getPayloadHandlerV1.HandleAsync(payloadId);

public async Task<ResultWrapper<PayloadStatusV1>> engine_newPayloadV1(ExecutionPayload executionPayload)
=> await NewPayload(executionPayload, 1);

public async Task<ResultWrapper<ForkchoiceUpdatedV1Result>> engine_forkchoiceUpdatedV1(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes = null)
=> await ForkchoiceUpdated(forkchoiceState, payloadAttributes, 1);

public ResultWrapper<TransitionConfigurationV1> engine_exchangeTransitionConfigurationV1(
TransitionConfigurationV1 beaconTransitionConfiguration) => _transitionConfigurationHandler.Handle(beaconTransitionConfiguration);

private async Task<ResultWrapper<ForkchoiceUpdatedV1Result>> ForkchoiceUpdated(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes, int version)
{
if (payloadAttributes?.Validate(_specProvider, version, out string? error) == false)
{
if (_logger.IsWarn) _logger.Warn(error);
return ResultWrapper<ForkchoiceUpdatedV1Result>.Fail(error, ErrorCodes.InvalidParams);
}

if (await _locker.WaitAsync(_timeout))
{
Stopwatch watch = Stopwatch.StartNew();
try
{
return await _forkchoiceUpdatedV1Handler.Handle(forkchoiceState, payloadAttributes);
}
finally
{
watch.Stop();
Metrics.ForkchoiceUpdedExecutionTime = watch.ElapsedMilliseconds;
_locker.Release();
}
}
else
{
if (_logger.IsWarn) _logger.Warn($"engine_forkchoiceUpdated{version} timed out");
return ResultWrapper<ForkchoiceUpdatedV1Result>.Fail("Timed out", ErrorCodes.Timeout);
}
}

private async Task<ResultWrapper<PayloadStatusV1>> NewPayload(ExecutionPayload executionPayload, int version)
{
if (!executionPayload.Validate(_specProvider, version, out string? error))
{
if (_logger.IsWarn) _logger.Warn(error);
return ResultWrapper<PayloadStatusV1>.Fail(error, ErrorCodes.InvalidParams);
}

if (await _locker.WaitAsync(_timeout))
{
Stopwatch watch = Stopwatch.StartNew();
try
{
return await _newPayloadV1Handler.HandleAsync(executionPayload);
}
catch (Exception exception)
{
if (_logger.IsError) _logger.Error($"engine_newPayloadV{version} failed: {exception}");
return ResultWrapper<PayloadStatusV1>.Fail(exception.Message);
}
finally
{
watch.Stop();
Metrics.NewPayloadExecutionTime = watch.ElapsedMilliseconds;
_locker.Release();
}
}
else
{
if (_logger.IsWarn) _logger.Warn($"engine_newPayloadV{version} timed out");
return ResultWrapper<PayloadStatusV1>.Fail("Timed out", ErrorCodes.Timeout);
}
}
}
Comment on lines +39 to +101
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be here or in EngineRpcModule.cs?

}
31 changes: 31 additions & 0 deletions src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.Shanghai.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Nethermind.Consensus.Producers;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.JsonRpc;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Merge.Plugin.Handlers;

namespace Nethermind.Merge.Plugin
{
public partial class EngineRpcModule : IEngineRpcModule
{
private readonly IAsyncHandler<byte[], GetPayloadV2Result?> _getPayloadHandlerV2;

public async Task<ResultWrapper<GetPayloadV2Result?>> engine_getPayloadV2(byte[] payloadId) =>
await _getPayloadHandlerV2.HandleAsync(payloadId);

public Task<ResultWrapper<PayloadStatusV1>> engine_newPayloadV2(ExecutionPayload executionPayload)
=> NewPayload(executionPayload, 2);

public Task<ResultWrapper<ForkchoiceUpdatedV1Result>> engine_forkchoiceUpdatedV2(ForkchoiceStateV1 forkchoiceState, PayloadAttributes? payloadAttributes = null)
=> ForkchoiceUpdated(forkchoiceState, payloadAttributes, 2);
}
}
Loading