Skip to content

Commit

Permalink
Implement engine_exchangeCapabilities method (#5212)
Browse files Browse the repository at this point in the history
  • Loading branch information
rubo committed Jan 27, 2023
1 parent 3e87458 commit a9cb737
Show file tree
Hide file tree
Showing 7 changed files with 483 additions and 343 deletions.
663 changes: 329 additions & 334 deletions src/Nethermind/Nethermind.Core.Test/Blockchain/TestBlockchain.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ private IEngineRpcModule CreateEngineModule(MergeTestBlockchain chain, ISyncConf
new GetPayloadBodiesByHashV1Handler(chain.BlockTree, chain.LogManager),
new GetPayloadBodiesByRangeV1Handler(chain.BlockTree, chain.LogManager),
new ExchangeTransitionConfigurationV1Handler(chain.PoSSwitcher, chain.LogManager),
new ExchangeCapabilitiesHandler(chain.SpecProvider, chain.LogManager),
chain.SpecProvider,
chain.LogManager);
}
Expand Down Expand Up @@ -139,7 +140,7 @@ public MergeTestBlockchain(IMergeConfig? mergeConfig = null, IPayloadPreparation

protected override Task AddBlocksOnStart() => Task.CompletedTask;

public sealed override ILogManager LogManager { get; } = LimboLogs.Instance;
public sealed override ILogManager LogManager { get; set; } = LimboLogs.Instance;

public IEthSyncingInfo? EthSyncingInfo { get; protected set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
using Nethermind.JsonRpc.Modules.Eth;
using Nethermind.JsonRpc.Test;
using Nethermind.JsonRpc.Test.Modules;
using Nethermind.Logging;
using Nethermind.Merge.Plugin.Data;
using Nethermind.Specs;
using Nethermind.Specs.Forks;
using Nethermind.State;
using Nethermind.Trie;
using Newtonsoft.Json;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.Merge.Plugin.Test;
Expand Down Expand Up @@ -1661,6 +1663,43 @@ await rpc.engine_forkchoiceUpdatedV1(forkChoiceState1,
}
}

[Test]
public async Task Should_return_capabilities()
{
using var chain = await CreateBlockChain();
var rpcModule = CreateEngineModule(chain);
var expected = typeof(IEngineRpcModule).GetMethods()
.Select(m => m.Name)
.Where(m => !m.Equals(nameof(IEngineRpcModule.engine_exchangeCapabilities), StringComparison.Ordinal))
.Order();

var result = await rpcModule.engine_exchangeCapabilities(expected);

result.Data.Should().BeEquivalentTo(expected);
}

[Test]
public async Task Should_warn_for_missing_capabilities()
{
using var chain = await CreateBlockChain();
chain.LogManager = Substitute.For<ILogManager>();
chain.LogManager.GetClassLogger().IsWarn.Returns(true);

var rpcModule = CreateEngineModule(chain);
var list = new[]
{
nameof(IEngineRpcModule.engine_forkchoiceUpdatedV1),
nameof(IEngineRpcModule.engine_forkchoiceUpdatedV2)
};

var result = await rpcModule.engine_exchangeCapabilities(list);

chain.LogManager.GetClassLogger().Received().Warn(
Arg.Is<string>(a =>
a.Contains(nameof(IEngineRpcModule.engine_getPayloadV1), StringComparison.Ordinal) &&
!a.Contains(nameof(IEngineRpcModule.engine_getPayloadV2), StringComparison.Ordinal)));
}

private async Task<ExecutionPayload> BuildAndGetPayloadResult(
IEngineRpcModule rpc, MergeTestBlockchain chain, Keccak headBlockHash, Keccak finalizedBlockHash,
Keccak safeBlockHash,
Expand Down
20 changes: 12 additions & 8 deletions src/Nethermind/Nethermind.Merge.Plugin/EngineRpcModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
Expand All @@ -14,6 +15,8 @@ namespace Nethermind.Merge.Plugin;

public partial class EngineRpcModule : IEngineRpcModule
{

private readonly IAsyncHandler<IEnumerable<string>, IEnumerable<string>> _capabilitiesHandler;
private readonly IHandler<ExecutionStatusResult> _executionStatusHandler;
private readonly IAsyncHandler<Keccak[], ExecutionPayloadBodyV1Result?[]> _executionGetPayloadBodiesByHashV1Handler;
private readonly IGetPayloadBodiesByRangeV1Handler _executionGetPayloadBodiesByRangeV1Handler;
Expand All @@ -29,9 +32,11 @@ public EngineRpcModule(
IAsyncHandler<Keccak[], ExecutionPayloadBodyV1Result?[]> executionGetPayloadBodiesByHashV1Handler,
IGetPayloadBodiesByRangeV1Handler executionGetPayloadBodiesByRangeV1Handler,
IHandler<TransitionConfigurationV1, TransitionConfigurationV1> transitionConfigurationHandler,
IAsyncHandler<IEnumerable<string>, IEnumerable<string>> capabilitiesHandler,
ISpecProvider specProvider,
ILogManager logManager)
{
_capabilitiesHandler = capabilitiesHandler ?? throw new ArgumentNullException(nameof(capabilitiesHandler));
_getPayloadHandlerV1 = getPayloadHandlerV1;
_getPayloadHandlerV2 = getPayloadHandlerV2;
_newPayloadV1Handler = newPayloadV1Handler;
Expand All @@ -44,15 +49,14 @@ public EngineRpcModule(
_logger = logManager.GetClassLogger();
}

public Task<ResultWrapper<IEnumerable<string>>> engine_exchangeCapabilities(IEnumerable<string> methods)
=> _capabilitiesHandler.HandleAsync(methods);

public ResultWrapper<ExecutionStatusResult> engine_executionStatus() => _executionStatusHandler.Handle();

public async Task<ResultWrapper<ExecutionPayloadBodyV1Result?[]>> engine_getPayloadBodiesByHashV1(Keccak[] blockHashes)
{
return await _executionGetPayloadBodiesByHashV1Handler.HandleAsync(blockHashes);
}
public Task<ResultWrapper<ExecutionPayloadBodyV1Result?[]>> engine_getPayloadBodiesByHashV1(Keccak[] blockHashes)
=> _executionGetPayloadBodiesByHashV1Handler.HandleAsync(blockHashes);

public async Task<ResultWrapper<ExecutionPayloadBodyV1Result?[]>> engine_getPayloadBodiesByRangeV1(long start, long count)
{
return await _executionGetPayloadBodiesByRangeV1Handler.Handle(start, count);
}
public Task<ResultWrapper<ExecutionPayloadBodyV1Result?[]>> engine_getPayloadBodiesByRangeV1(long start, long count)
=> _executionGetPayloadBodiesByRangeV1Handler.Handle(start, count);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: 2023 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.Specs;
using Nethermind.JsonRpc;
using Nethermind.Logging;

namespace Nethermind.Merge.Plugin.Handlers;

public class ExchangeCapabilitiesHandler : IAsyncHandler<IEnumerable<string>, IEnumerable<string>>
{
private static IDictionary<string, bool> _capabilities = new Dictionary<string, bool>();
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(1);

private readonly ILogger _logger;

public ExchangeCapabilitiesHandler(ISpecProvider specProvider, ILogManager logManager)
{
ArgumentNullException.ThrowIfNull(specProvider);
ArgumentNullException.ThrowIfNull(logManager);

_logger = logManager.GetClassLogger();

if (_capabilities.Count == 0)
{
var spec = specProvider.GetSpec((long.MaxValue, ulong.MaxValue));

#region The Merge
_capabilities[nameof(IEngineRpcModule.engine_exchangeTransitionConfigurationV1)] = true;
_capabilities[nameof(IEngineRpcModule.engine_executionStatus)] = true;
_capabilities[nameof(IEngineRpcModule.engine_forkchoiceUpdatedV1)] = true;
_capabilities[nameof(IEngineRpcModule.engine_getPayloadV1)] = true;
_capabilities[nameof(IEngineRpcModule.engine_newPayloadV1)] = true;
#endregion

#region Shanghai
_capabilities[nameof(IEngineRpcModule.engine_forkchoiceUpdatedV2)] = spec.WithdrawalsEnabled;
_capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByHashV1)] = spec.WithdrawalsEnabled;
_capabilities[nameof(IEngineRpcModule.engine_getPayloadBodiesByRangeV1)] = spec.WithdrawalsEnabled;
_capabilities[nameof(IEngineRpcModule.engine_getPayloadV2)] = spec.WithdrawalsEnabled;
_capabilities[nameof(IEngineRpcModule.engine_newPayloadV2)] = spec.WithdrawalsEnabled;
#endregion
}
}

public async Task<ResultWrapper<IEnumerable<string>>> HandleAsync(IEnumerable<string> methods)
{
var task = Task.Run(() => CheckCapabilities(methods));

try
{
await task.WaitAsync(_timeout);

return ResultWrapper<IEnumerable<string>>.Success(_capabilities.Keys);
}
catch (TimeoutException)
{
if (_logger.IsWarn) _logger.Warn($"{nameof(IEngineRpcModule.engine_exchangeCapabilities)} timed out");

return ResultWrapper<IEnumerable<string>>.Fail("Timed out", ErrorCodes.Timeout);
}
}

private void CheckCapabilities(IEnumerable<string> methods)
{
var missing = new List<string>();

foreach (var capability in _capabilities)
{
var found = false;

foreach (var method in methods)
if (method.Equals(capability.Key, StringComparison.Ordinal))
{
found = true;
break;
}

// Warn if not found and capability activated
if (!found && capability.Value)
missing.Add(capability.Key);
}

if (missing.Any())
{
if (_logger.IsWarn) _logger.Warn($"Consensus client missing capabilities: {string.Join(", ", missing)}");
}
}
}
7 changes: 7 additions & 0 deletions src/Nethermind/Nethermind.Merge.Plugin/IEngineRpcModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Threading.Tasks;
using Nethermind.Core.Crypto;
using Nethermind.JsonRpc;
Expand All @@ -12,6 +13,12 @@ namespace Nethermind.Merge.Plugin;
[RpcModule(ModuleType.Engine)]
public partial interface IEngineRpcModule : IRpcModule
{
[JsonRpcMethod(
Description = "Returns the currently supported list of Engine API methods.",
IsSharable = true,
IsImplemented = true)]
Task<ResultWrapper<IEnumerable<string>>> engine_exchangeCapabilities(IEnumerable<string> methods);

[JsonRpcMethod(
Description =
"Responds with information on the state of the execution client to either engine_consensusStatus or any other call if consistency failure has occurred.",
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Merge.Plugin/MergePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ public Task InitRpcModules()
new GetPayloadBodiesByHashV1Handler(_api.BlockTree, _api.LogManager),
new GetPayloadBodiesByRangeV1Handler(_api.BlockTree, _api.LogManager),
new ExchangeTransitionConfigurationV1Handler(_poSSwitcher, _api.LogManager),
new ExchangeCapabilitiesHandler(_api.SpecProvider, _api.LogManager),
_api.SpecProvider,
_api.LogManager);

Expand Down

0 comments on commit a9cb737

Please sign in to comment.