Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Evm.Precompiles/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static OrderedDictionary<string, Address> ListPrecompiles(this IReleaseSp
AddPrecompile<MapFp2ToG2Precompile>();
}

if (spec.IsEip7951Enabled)
if (spec.IsRip7212Enabled || spec.IsEip7951Enabled)
{
AddPrecompile<Secp256r1Precompile>();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,27 @@ public async Task Eth_get_transaction_by_block_number_and_index_out_of_bounds()
Assert.That(serialized, Is.EqualTo("{\"jsonrpc\":\"2.0\",\"result\":null,\"id\":67}"));
}

[Test]
public async Task Eth_config_does_not_include_all_by_default()
{
using Context ctx = await Context.Create();
string serialized = await ctx.Test.TestEthRpc("eth_config");
JToken result = JToken.Parse(serialized)["result"]!;
result.Should().NotBeNull();
result["all"].Should().BeNull();
}

[Test]
public async Task Eth_config_returns_all_forks_when_requested()
{
using Context ctx = await Context.Create();
string serialized = await ctx.Test.TestEthRpc("eth_config", true);
JToken result = JToken.Parse(serialized)["result"]!;
result.Should().NotBeNull();
JArray allForks = (JArray)result["all"]!;
allForks.Count.Should().BeGreaterThan(0);
}

[TestCase(false, "{\"jsonrpc\":\"2.0\",\"result\":{\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")]
[TestCase(true, "{\"jsonrpc\":\"2.0\",\"result\":{\"difficulty\":\"0xf4240\",\"extraData\":\"0x010203\",\"gasLimit\":\"0x3d0900\",\"gasUsed\":\"0x0\",\"hash\":\"0xa2a9f03b9493046696099d27b2612b99497aa1f392ec966716ab393c715a5bb6\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x2ba5557a4c62a513c7e56d1bf13373e0da6bec016755483e91589fe1c6d212e2\",\"nonce\":\"0x00000000000003e8\",\"number\":\"0x0\",\"parentHash\":\"0xff483e972a04a9a62bb4b7d04ae403c615604e4090521ecc5bb7af67f71be09c\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x201\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"totalDifficulty\":\"0x0\",\"timestamp\":\"0xf4240\",\"baseFeePerGas\":\"0x0\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]},\"id\":67}")]
public async Task Eth_get_uncle_by_block_number_and_index(bool eip1559, string expectedJson)
Expand Down
2 changes: 2 additions & 0 deletions src/Nethermind/Nethermind.JsonRpc/Data/ForkConfigSummary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class ForkConfigSummary

[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public required ForkConfig? Last { get; init; }

public IReadOnlyList<ForkConfig>? All { get; init; }
}

public class ForkConfig
Expand Down
53 changes: 39 additions & 14 deletions src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Security;
Expand Down Expand Up @@ -67,6 +68,8 @@ public partial class EthRpcModule(
IForkInfo forkInfo,
ulong? secondsPerSlot) : IEthRpcModule
{
private static FrozenDictionary<ForkId, ForkConfig>? forkConfigCache = null;

protected readonly Encoding _messageEncoding = Encoding.UTF8;
protected readonly IJsonRpcConfig _rpcConfig = rpcConfig ?? throw new ArgumentNullException(nameof(rpcConfig));
protected readonly IBlockchainBridge _blockchainBridge = blockchainBridge ?? throw new ArgumentNullException(nameof(blockchainBridge));
Expand Down Expand Up @@ -802,38 +805,60 @@ private ResultWrapper<TResult> GetStateFailureResult<TResult>(BlockHeader header
};
}

public ResultWrapper<JsonNode> eth_config()
public ResultWrapper<JsonNode> eth_config(bool showAllForks = false)
{
ForkActivationsSummary forks = forkInfo.GetForkActivationsSummary(_blockFinder.Head?.Header);

return ResultWrapper<JsonNode>.Success(JsonNode.Parse(JsonSerializer.Serialize((new ForkConfigSummary
if (forkConfigCache is null)
{
Current = GetForkConfig(forks.Current, _specProvider)!,
Next = GetForkConfig(forks.Next, _specProvider),
Last = GetForkConfig(forks.Last, _specProvider)
}), UnchangedDictionaryKeyOptions)));
ReadOnlySpan<Fork> forkSchedule = forkInfo.GetAllForks();
Dictionary<ForkId, ForkConfig> allForks = new(forkSchedule.Length);

foreach (Fork scheduledFork in forkSchedule)
{
allForks.Add(scheduledFork.Id, BuildForkConfig(scheduledFork, _specProvider));
}

forkConfigCache = allForks.ToFrozenDictionary();
}

static ForkConfig? GetForkConfig(Fork? fork, ISpecProvider specProvider)
List<ForkConfig>? allForkConfigs = null;

if (showAllForks)
{
if (fork is null)
ReadOnlySpan<Fork> forkSchedule = forkInfo.GetAllForks();
allForkConfigs = new(forkSchedule.Length);

foreach (Fork scheduledFork in forkSchedule)
{
return null;
allForkConfigs.Add(forkConfigCache[scheduledFork.Id]);
}
}

IReleaseSpec? spec = specProvider.GetSpec(fork.Value.Activation.BlockNumber, fork.Value.Activation.Timestamp);
return ResultWrapper<JsonNode>.Success(JsonNode.Parse(JsonSerializer.Serialize(new ForkConfigSummary
{
Current = forkConfigCache[forks.Current.Id],
Next = forks.Next is null ? null : forkConfigCache[forks.Next.Value.Id],
Last = forks.Last is null ? null : forkConfigCache[forks.Last.Value.Id],
All = allForkConfigs
}, UnchangedDictionaryKeyOptions)));

static ForkConfig BuildForkConfig(Fork fork, ISpecProvider specProvider)
{
IReleaseSpec spec = specProvider.GetSpec(fork.Activation.BlockNumber, fork.Activation.Timestamp);

return new ForkConfig
{
ActivationTime = fork.Value.Activation.Timestamp is not null ? (int)fork.Value.Activation.Timestamp : null,
ActivationBlock = fork.Value.Activation.Timestamp is null ? (int)fork.Value.Activation.BlockNumber : null,
ActivationTime = fork.Activation.Timestamp is not null ? (int)fork.Activation.Timestamp : null,
ActivationBlock = fork.Activation.Timestamp is null ? (int)fork.Activation.BlockNumber : null,
BlobSchedule = spec.IsEip4844Enabled ? new BlobScheduleSettingsForRpc
{
BaseFeeUpdateFraction = (int)spec.BlobBaseFeeUpdateFraction,
Max = (int)spec.MaxBlobCount,
Target = (int)spec.TargetBlobCount,
} : null,
ChainId = specProvider.ChainId,
ForkId = fork.Value.Id.HashBytes,
ForkId = fork.Id.HashBytes,
Precompiles = spec.ListPrecompiles(),
SystemContracts = spec.ListSystemContracts(),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
Expand Down Expand Up @@ -289,7 +288,7 @@ ResultWrapper<TransactionForRpc> eth_getTransactionByBlockNumberAndIndex(
[JsonRpcMethod(IsImplemented = true, Description = "Retrieves Account with code and no storageRoot via Address and Blocknumber", IsSharable = true)]
ResultWrapper<AccountInfoForRpc?> eth_getAccountInfo([JsonRpcParameter(ExampleValue = "[\"0xaa00000000000000000000000000000000000000\", \"latest\"]")] Address accountAddress, BlockParameter? blockParameter = null);

[JsonRpcMethod(IsImplemented = true, Description = "Provides configuration data for the current and next fork", IsSharable = true)]
ResultWrapper<JsonNode> eth_config();
[JsonRpcMethod(IsImplemented = true, Description = "Provides configuration data for the current and next fork or the full fork schedule", IsSharable = true)]
ResultWrapper<JsonNode> eth_config([JsonRpcParameter(Description = "[Nethermind only] Additionally returns every known forks when true", ExampleValue = "[true]")] bool showAllForks = false);
}
}
8 changes: 7 additions & 1 deletion src/Nethermind/Nethermind.Network/ForkInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Force.Crc32;
Expand Down Expand Up @@ -176,5 +176,11 @@ public ForkActivationsSummary GetForkActivationsSummary(BlockHeader? head)
Last = isNextPresent ? new Fork(Forks[^1].Activation, Forks[^1].Id) : null,
};
}

public ReadOnlySpan<Fork> GetAllForks()
{
EnsureInitialized();
return Forks;
}
}
}
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Network/IForkInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Nethermind.Core;
using Nethermind.Core.Specs;
using System;

namespace Nethermind.Network;

Expand All @@ -19,6 +20,8 @@ public interface IForkInfo
ValidationResult ValidateForkId(ForkId peerId, BlockHeader? head);

ForkActivationsSummary GetForkActivationsSummary(BlockHeader? head);

ReadOnlySpan<Fork> GetAllForks();
}

public readonly record struct Fork(ForkActivation Activation, ForkId Id);
Expand Down