diff --git a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs index 2d679176068..2b03370e2c2 100644 --- a/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs +++ b/src/Nethermind/Nethermind.Specs.Test/ChainSpecStyle/ChainSpecBasedSpecProviderTests.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -98,15 +99,16 @@ public void Logs_warning_when_timestampActivation_happens_before_blockActivation expectedSpec.IsEip3855Enabled = isEip3855Enabled; expectedSpec.Eip1559TransitionBlock = 0; expectedSpec.DifficultyBombDelay = 0; - TestSpecProvider testProvider = TestSpecProvider.Instance; - testProvider.SpecToReturn = expectedSpec; - testProvider.TerminalTotalDifficulty = 0; - testProvider.GenesisSpec = expectedSpec; List forkActivationsToTest = new() { (blockNumber, timestamp), }; - CompareSpecProviders(testProvider, provider, forkActivationsToTest); + + foreach (ForkActivation activation in forkActivationsToTest) + { + provider.GetSpec(activation); + } + if (receivesWarning) { logger.Received(1).Warn(Arg.Is("Chainspec file is misconfigured! Timestamp transition is configured to happen before the last block transition.")); @@ -290,8 +292,8 @@ public void Gnosis_loads_properly() (ForkActivation)(GnosisSpecProvider.BerlinBlockNumber), (ForkActivation)(GnosisSpecProvider.LondonBlockNumber -1), (ForkActivation)(GnosisSpecProvider.LondonBlockNumber), - (1, GnosisSpecProvider.ShanghaiTimestamp - 1), - (1, GnosisSpecProvider.ShanghaiTimestamp), + (GnosisSpecProvider.LondonBlockNumber, GnosisSpecProvider.ShanghaiTimestamp - 1), + (GnosisSpecProvider.LondonBlockNumber, GnosisSpecProvider.ShanghaiTimestamp), (999_999_999, 999_999_999) // far in the future }; @@ -831,6 +833,43 @@ void TestTransitions(ForkActivation activation, Action changes) TestTransitions((40001L, 1000000024), r => { r.IsEip1153Enabled = r.IsEip2537Enabled = true; }); } + [TestCaseSource(nameof(BlockNumbersAndTimestampsNearForkActivations))] + public void Forks_should_be_selected_properly_for_exact_matches(ForkActivation forkActivation, bool isEip3651Enabled, bool isEip3198Enabled, bool isEip3855Enabled) + { + ISpecProvider provider = new CustomSpecProvider( + (new ForkActivation(0), new ReleaseSpec() { IsEip3651Enabled = true }), + (new ForkActivation(2, 10), new ReleaseSpec() { IsEip3651Enabled = true, IsEip3198Enabled = true, }), + (new ForkActivation(2, 20), new ReleaseSpec() { IsEip3651Enabled = true, IsEip3198Enabled = true, IsEip3855Enabled = true }) + ); + + IReleaseSpec spec = provider.GetSpec(forkActivation); + Assert.Multiple(() => + { + Assert.That(spec.IsEip3651Enabled, Is.EqualTo(isEip3651Enabled)); + Assert.That(spec.IsEip3198Enabled, Is.EqualTo(isEip3198Enabled)); + Assert.That(spec.IsEip3855Enabled, Is.EqualTo(isEip3855Enabled)); + }); + } + + public static IEnumerable BlockNumbersAndTimestampsNearForkActivations + { + get + { + yield return new TestCaseData(new ForkActivation(1, 9), true, false, false); + yield return new TestCaseData(new ForkActivation(2, 9), true, false, false); + yield return new TestCaseData(new ForkActivation(2, 10), true, true, false); + yield return new TestCaseData(new ForkActivation(2, 11), true, true, false); + yield return new TestCaseData(new ForkActivation(2, 19), true, true, false); + yield return new TestCaseData(new ForkActivation(2, 20), true, true, true); + yield return new TestCaseData(new ForkActivation(2, 21), true, true, true); + yield return new TestCaseData(new ForkActivation(3, 10), true, true, false); + yield return new TestCaseData(new ForkActivation(3, 11), true, true, false); + yield return new TestCaseData(new ForkActivation(3, 19), true, true, false); + yield return new TestCaseData(new ForkActivation(3, 20), true, true, true); + yield return new TestCaseData(new ForkActivation(3, 21), true, true, true); + } + } + private static IEnumerable GetTransitionTimestamps(ChainParameters parameters) => parameters.GetType() .Properties() .Where(p => p.Name.EndsWith("TransitionTimestamp", StringComparison.Ordinal)) diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 45a85affb03..64d5257070b 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -16,6 +16,7 @@ namespace Nethermind.Specs.ChainSpecStyle public class ChainSpecBasedSpecProvider : ISpecProvider { private (ForkActivation Activation, ReleaseSpec Spec)[] _transitions; + private (ForkActivation Activation, ReleaseSpec Spec)[] _timestampOnlyTransitions; private ForkActivation? _firstTimestampActivation; private readonly ChainSpec _chainSpec; @@ -98,6 +99,7 @@ static void Add(SortedSet transitions, T value, T? minValueExclusive) TransitionActivations = CreateTransitionActivations(transitionBlockNumbers, transitionTimestamps); _transitions = CreateTransitions(_chainSpec, transitionBlockNumbers, transitionTimestamps); _firstTimestampActivation = TransitionActivations.FirstOrDefault(t => t.Timestamp is not null); + _timestampOnlyTransitions = _transitions.SkipWhile(t => t.Activation.Timestamp is null).ToArray(); if (_chainSpec.Parameters.TerminalPoWBlockNumber is not null) { @@ -268,6 +270,8 @@ public void UpdateMergeTransitionInfo(long? blockNumber, UInt256? terminalTotalD public IReleaseSpec GetSpec(ForkActivation activation) { + (ForkActivation Activation, ReleaseSpec Spec)[] consideredTransitions = _transitions; + // TODO: Is this actually needed? Can this be tricked with invalid activation check if someone would fake timestamp from the future? if (_firstTimestampActivation is not null && activation.Timestamp is not null) { @@ -276,9 +280,14 @@ public IReleaseSpec GetSpec(ForkActivation activation) { if (_logger.IsWarn) _logger.Warn($"Chainspec file is misconfigured! Timestamp transition is configured to happen before the last block transition."); } + + if (_firstTimestampActivation.Value.Timestamp <= activation.Timestamp) + { + consideredTransitions = _timestampOnlyTransitions; + } } - return _transitions.TryGetSearchedItem(activation, + return consideredTransitions.TryGetSearchedItem(activation, CompareTransitionOnActivation, out (ForkActivation Activation, ReleaseSpec Spec) transition) ? transition.Spec