Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make chainspec extendable by plugins #7540

Merged
merged 53 commits into from
Nov 4, 2024
Merged

Make chainspec extendable by plugins #7540

merged 53 commits into from
Nov 4, 2024

Conversation

deffrian
Copy link
Contributor

@deffrian deffrian commented Oct 2, 2024

Fixes Closes Resolves #
#6222
Right now our ChainSpec is a mess. This pr tries to fix some issues.

Changes

Adds new interface IChainSpecEngineParameters. This works similar to IConfig.

  1. In runtime we search for all inheritors of this interface and create instances. We set corresponding fields from chainspec file.
  2. During ReleaseSpec creation we call IChainSpecEngineParameters.AddTransitions. Plugin devs write implementation of this function if they want to add transitions.
  3. After all ReleaseSpec created we call IChainSpecEngineParameters.AdjustReleaseSpec. Plugin devs write implementation of this function if they want to change any parameters in ReleaseSpec.

Types of changes

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Documentation

Requires documentation update

  • Yes
  • No

Requires explanation in Release Notes

  • Yes
  • No

@deffrian deffrian changed the title Make chainspec extendable by plugins [WIP]Make chainspec extendable by plugins Oct 2, 2024
@jmederosalvarado jmederosalvarado self-requested a review October 2, 2024 16:42
Copy link
Member

@LukaszRozmej LukaszRozmej left a comment

Choose a reason for hiding this comment

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

I think the direction is good, but there is some details to iron out.

Comment on lines 39 to 44
// hex encoded byte array
string hex = valueString.Trim().RemoveStart('0').RemoveStart('x').TrimEnd();
value = Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
Copy link
Member

Choose a reason for hiding this comment

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

Bytes.FromHexString
AsSpan().Trim() if needed

src/Nethermind/Nethermind.Config/ConfigSourceHelper.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Config/ConfigSourceHelper.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Config/ConfigSourceHelper.cs Outdated Show resolved Hide resolved
Comment on lines +11 to +12
private readonly long? _bedrockBlockNumber = parameters.BedrockBlockNumber;
private readonly ulong? _regolithTimestamp = parameters.RegolithTimestamp;
Copy link
Member

Choose a reason for hiding this comment

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

Why do we have to make stuff nullable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There might be no value in config, so better to make it nullable imo

Copy link
Member

Choose a reason for hiding this comment

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

@deffrian shouldn't we always have a value for these hardforks?

Copy link
Contributor

Choose a reason for hiding this comment

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

On op-sepolia which actually does not have a bedrock block we're using 0x0, for example.

I'm in favor of letting it default to 0 rather than making it null

deffrian and others added 2 commits October 3, 2024 12:19
Co-authored-by: Lukasz Rozmej <lukasz.rozmej@gmail.com>
Co-authored-by: Lukasz Rozmej <lukasz.rozmej@gmail.com>
Copy link
Contributor

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

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

Some questions regarding nullability and discovery, but overall LGTM.

IEnumerable<Type> types = TypeDiscovery.FindNethermindBasedTypes(type).Where(x => x.IsClass);
foreach (Type @class in types)
{
string engineName = @class.Name.Remove(@class.Name.Length - EngineParamsSuffix.Length);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we do the discovery based on implementing IChainSpecEngineParameters rather than class name? Even if we go with the manual registration route we could perform this check.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what you mean. It already looks for all inheritors of IChainSpecEngineParameters. I didn't make manual registration because ChainSpec is more similar to IConfig. So I thought we should behave the same way here

Copy link
Contributor

Choose a reason for hiding this comment

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

My question was more in line with replacing the class name logic with something more "typesafe": right now it seems like I need to follow a specific naming convention but it's not enforced statically, and to figure it out I need to take a look into this implementation.

If I were to add another IChainSpecEngineParameters class, what would the requirements be and how would I figure them out?

Copy link
Member

Choose a reason for hiding this comment

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

I think what Lautaro means is that we should have a property IChainSpecEngineParamerters.Name or something similar, instead of using the name of the class. I understand @deffrian's point that we're being consistent with how IConfig works. But maybe we should start improving both.

Copy link
Contributor

@emlautarom1 emlautarom1 Oct 9, 2024

Choose a reason for hiding this comment

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

I think what Lautaro means is that we should have a property IChainSpecEngineParamerters.Name or something similar, instead of using the name of the class.

Precisely, I just didn't want to push for any particular approach.

we're being consistent with how IConfig works

Didn't know that we were using the class name approach there. If it is consistent with the existing style then let's move forward, but we might want to add a TODO to eventually improve (that is, move to the type system) these kind of requirements.

private readonly Dictionary<string, JsonElement> _chainSpecParameters =
new(StringComparer.InvariantCultureIgnoreCase);

private readonly ConcurrentDictionary<Type, IChainSpecEngineParameters> _instances = new();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this have to be concurrent? We cannot add elements through AllChainSpecParameters and there is no other accessor.

public interface IChainSpecParametersProvider
{
string SealEngineType { get; }
ICollection<IChainSpecEngineParameters> AllChainSpecParameters { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

If we don't intend to allow modification (which I think we don't) prefer IEnumerable to ICollection

{
ArgumentNullException.ThrowIfNull(RegolithTimestamp);
ArgumentNullException.ThrowIfNull(BedrockBlockNumber);
ArgumentNullException.ThrowIfNull(CanyonTimestamp);
Copy link
Contributor

Choose a reason for hiding this comment

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

Some of these fields (like CanyonTimestamp) used to be nullable and still are here, but others have changed (like RegolithTimestamp).
Which fields are actually required and which are not?

src/Nethermind/Nethermind.Config/ConfigSourceHelper.cs Outdated Show resolved Hide resolved
deffrian and others added 10 commits October 5, 2024 13:00
…tersProvider.cs

Co-authored-by: Lautaro Emanuel <31224949+emlautarom1@users.noreply.github.com>
Co-authored-by: Lukasz Rozmej <lukasz.rozmej@gmail.com>
Co-authored-by: Lukasz Rozmej <lukasz.rozmej@gmail.com>
…tersProvider.cs

Co-authored-by: Lautaro Emanuel <31224949+emlautarom1@users.noreply.github.com>
@deffrian deffrian force-pushed the refactor/chainspec-v2 branch from 32cbfa4 to b6f1a53 Compare October 6, 2024 12:22
# Conflicts:
#	src/Nethermind/Nethermind.Core/SealEngineType.cs
#	src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs
#	src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecLoader.cs
#	src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecJson.cs
@deffrian deffrian changed the title [WIP]Make chainspec extendable by plugins Make chainspec extendable by plugins Oct 27, 2024
@deffrian deffrian marked this pull request as ready for review October 27, 2024 15:29
Copy link
Contributor

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

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

Some comments before approving

{
if (CanyonTimestamp <= startTimestamp)
{
spec.BaseFeeMaxChangeDenominator = CanyonBaseFeeChangeDenominator!.Value;
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible risk of null dereference?

Comment on lines +11 to +12
private readonly long? _bedrockBlockNumber = parameters.BedrockBlockNumber;
private readonly ulong? _regolithTimestamp = parameters.RegolithTimestamp;
Copy link
Contributor

Choose a reason for hiding this comment

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

On op-sepolia which actually does not have a bedrock block we're using 0x0, for example.

I'm in favor of letting it default to 0 rather than making it null

using System;
using System.Buffers;
using System.Collections.Generic;
using System.Globalization;
Copy link
Contributor

Choose a reason for hiding this comment

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

Unused import

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For comment on top: This is chainSpec, so better to force people to set 0 explicitly so they don't missconfigure it

[Test]
public void Can_load_posdao_with_rewriteBytecode()
{
// TODO: modexp 2565
Copy link
Contributor

Choose a reason for hiding this comment

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

Context on this TODO?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't know. This is from the original test

# Conflicts:
#	src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs
#	src/Nethermind/Nethermind.Optimism/InitializeBlockchainOptimism.cs
Copy link
Contributor

@emlautarom1 emlautarom1 left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member

@LukaszRozmej LukaszRozmej left a comment

Choose a reason for hiding this comment

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

Almost done, can you check if we need those custom dictionary converters?

Comment on lines 11 to 14
public const string NethDev = nameof(NethDev);
public const string Ethash = nameof(Ethash);
public const string BeaconChain = nameof(BeaconChain);
public const string Optimism = nameof(Optimism);
Copy link
Member

Choose a reason for hiding this comment

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

I think we could keep them there for simplicity.

Comment on lines 51 to 54
// we need this to discover ChainSpecEngineParameters
new CliqueConfig();
new OptimismConfig();
new TaikoPlugin();
Copy link
Member

Choose a reason for hiding this comment

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

Can we do it in some more elegant way?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the only way I know

Copy link
Member

Choose a reason for hiding this comment

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

what are we trying to achieve here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My assumption is that .net is not loading plugins if we don't use them. If you remove this it will not discover ChainSpecEngineParameters

Copy link
Member

Choose a reason for hiding this comment

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

Can we use TypeDiscovery class for that?

@deffrian deffrian merged commit c91e0b2 into master Nov 4, 2024
77 checks passed
@deffrian deffrian deleted the refactor/chainspec-v2 branch November 4, 2024 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants