From 6f0395eca6e0ac7b83e9f72a14a1f86a9376b69f Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:02:27 -0300 Subject: [PATCH 01/41] Perform single dependency capture --- .../Nethermind.Consensus/Validators/HeaderValidator.cs | 2 +- src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 2605b291631..399e7f205a6 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -20,7 +20,7 @@ public class HeaderValidator : IHeaderValidator private static readonly byte[] DaoExtraData = Bytes.FromHexString("0x64616f2d686172642d666f726b"); private readonly ISealValidator _sealValidator; - private readonly ISpecProvider _specProvider; + protected readonly ISpecProvider _specProvider; private readonly long? _daoBlockNumber; protected readonly ILogger _logger; private readonly IBlockTree _blockTree; diff --git a/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs index 903ff9dad59..83f6cc8ba40 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs @@ -22,7 +22,6 @@ public class MergeHeaderValidator : HeaderValidator private readonly IPoSSwitcher _poSSwitcher; private readonly IHeaderValidator _preMergeHeaderValidator; private readonly IBlockTree _blockTree; - private readonly ISpecProvider _specProvider; public MergeHeaderValidator( IPoSSwitcher poSSwitcher, @@ -36,7 +35,6 @@ public MergeHeaderValidator( _poSSwitcher = poSSwitcher; _preMergeHeaderValidator = preMergeHeaderValidator; _blockTree = blockTree; - _specProvider = specProvider; } public override bool Validate(BlockHeader header, BlockHeader? parent, bool isUncle = false) From fda85b9d3c32ce14297fb13123fd3877c4a6ec87 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:08:36 -0300 Subject: [PATCH 02/41] Add Holocene to ReleaseSpec --- src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs | 3 +++ src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs | 1 + src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs | 1 + .../Nethermind.Specs/ChainSpecStyle/ChainParameters.cs | 1 + .../ChainSpecStyle/ChainSpecBasedSpecProvider.cs | 1 + .../ChainSpecStyle/Json/ChainSpecParamsJson.cs | 1 + src/Nethermind/Nethermind.Specs/ReleaseSpec.cs | 1 + 7 files changed, 9 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs index 6c4f7c550a4..0241fa1912a 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs @@ -326,6 +326,9 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec /// OP Granite bool IsOpGraniteEnabled { get; } + /// OP Holocene + bool IsOpHoloceneEnabled { get; } + /// Taiko Ontake bool IsOntakeEnabled { get; } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index ac41ca6978b..4e214117a67 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -80,6 +80,7 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public bool IsEip7702Enabled => spec.IsEip7702Enabled; public virtual bool IsRip7212Enabled => spec.IsRip7212Enabled; public virtual bool IsOpGraniteEnabled => spec.IsOpGraniteEnabled; + public virtual bool IsOpHoloceneEnabled => spec.IsOpHoloceneEnabled; public virtual bool IsOntakeEnabled => spec.IsOntakeEnabled; public virtual ulong WithdrawalTimestamp => spec.WithdrawalTimestamp; public virtual ulong Eip4844TransitionTimestamp => spec.Eip4844TransitionTimestamp; diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 49f39bbaf0d..8aa58b89faa 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -114,6 +114,7 @@ public OverridableReleaseSpec(IReleaseSpec spec) public bool IsEip4844Enabled => _spec.IsEip4844Enabled; public bool IsRip7212Enabled => _spec.IsRip7212Enabled; public bool IsOpGraniteEnabled => _spec.IsOpGraniteEnabled; + public bool IsOpHoloceneEnabled => _spec.IsOpHoloceneEnabled; private bool? _isOntakeEnabled; public bool IsOntakeEnabled diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs index e5cb4cd37a5..72badcbc77a 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainParameters.cs @@ -130,6 +130,7 @@ public class ChainParameters public ulong? Rip7212TransitionTimestamp { get; set; } public ulong? Eip7702TransitionTimestamp { get; set; } public ulong? OpGraniteTransitionTimestamp { get; set; } + public ulong? OpHoloceneTransitionTimestamp { get; set; } #region EIP-4844 parameters /// diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs index 7b44d28dc29..25e00504aa7 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/ChainSpecBasedSpecProvider.cs @@ -214,6 +214,7 @@ private ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releaseStartBloc releaseSpec.IsEip4844Enabled = (chainSpec.Parameters.Eip4844TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsRip7212Enabled = (chainSpec.Parameters.Rip7212TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsOpGraniteEnabled = (chainSpec.Parameters.OpGraniteTransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; + releaseSpec.IsOpHoloceneEnabled = (chainSpec.Parameters.OpHoloceneTransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.Eip4844TransitionTimestamp = chainSpec.Parameters.Eip4844TransitionTimestamp ?? ulong.MaxValue; releaseSpec.IsEip5656Enabled = (chainSpec.Parameters.Eip5656TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; releaseSpec.IsEip6780Enabled = (chainSpec.Parameters.Eip6780TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp; diff --git a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs index b4749be1ab5..ff9e33274c6 100644 --- a/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs +++ b/src/Nethermind/Nethermind.Specs/ChainSpecStyle/Json/ChainSpecParamsJson.cs @@ -160,4 +160,5 @@ internal class ChainSpecParamsJson public ulong? Rip7212TransitionTimestamp { get; set; } public ulong? Eip7702TransitionTimestamp { get; set; } public ulong? OpGraniteTransitionTimestamp { get; set; } + public ulong? OpHoloceneTransitionTimestamp { get; set; } } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index dfb34cf9faf..0f18787b5f6 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -83,6 +83,7 @@ public bool IsEip1559Enabled public bool IsEip4844Enabled { get; set; } public bool IsRip7212Enabled { get; set; } public bool IsOpGraniteEnabled { get; set; } + public bool IsOpHoloceneEnabled { get; set; } public bool IsEip5656Enabled { get; set; } public bool IsEip6780Enabled { get; set; } public bool IsEip4788Enabled { get; set; } From 32ee4f7942dff49072940fb432fb9c7f37e40680 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:09:51 -0300 Subject: [PATCH 03/41] Decode EIP1559 parameters from BlockHeader --- .../EIP1559ParametersTests.cs | 34 +++++++++++++++ .../BlockHeaderExtensions.cs | 43 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs create mode 100644 src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs new file mode 100644 index 00000000000..5f52f13ffac --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; +using NUnit.Framework; +using FluentAssertions; +using System; + +namespace Nethermind.Optimism.Test; + +public class EIP1559ParametersTests +{ + private static IEnumerable<(string hexString, EIP1559Parameters expected)> DecodeBlockHeaderParametersCases() + { + yield return ("0x000000000100000000", new(0, 1, 0)); + yield return ("0x0000000001000001bc", new(0, 1, 444)); + yield return ("0x0000000001ffffffff", new(0, 1, UInt32.MaxValue)); + yield return ("0x00ffffffff00000000", new(0, UInt32.MaxValue, 0)); + yield return ("0x00ffffffff000001bc", new(0, UInt32.MaxValue, 444)); + yield return ("0x00ffffffffffffffff", new(0, UInt32.MaxValue, UInt32.MaxValue)); + } + [TestCaseSource(nameof(DecodeBlockHeaderParametersCases))] + public void DecodeBlockHeaderParameters((string HexString, EIP1559Parameters Expected) testCase) + { + var bytes = Bytes.FromHexString(testCase.HexString); + var blockHeader = Build.A.BlockHeader.WithExtraData(bytes).TestObject; + + EIP1559Parameters? decoded = blockHeader.DecodeEIP1559Parameters(); + + decoded.Should().Be(testCase.Expected); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs new file mode 100644 index 00000000000..9449f15c6c2 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Buffers.Binary; +using Nethermind.Core; +using Nethermind.Core.Extensions; + +namespace Nethermind.Optimism; + +public readonly struct EIP1559Parameters +{ + public byte Version { get; } + public UInt32 Denominator { get; } + public UInt32 Elasticity { get; } + + public EIP1559Parameters(byte version, UInt32 denominator, UInt32 elasticity) + { + if (version != 0) throw new ArgumentException($"{nameof(version)} must be 0", nameof(version)); + if (denominator == 0) throw new ArgumentException($"{nameof(denominator)} cannot be 0", nameof(denominator)); + // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 + // TODO: Revisit this check: https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L104 + + Version = version; + Denominator = denominator; + Elasticity = elasticity; + } +} + +public static class BlockHeaderExtensions +{ + public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) + { + if (header.ExtraData.Length < 9) throw new ArgumentException($"{header.ExtraData} data must be at least 9 bytes long"); + + ReadOnlySpan extraData = header.ExtraData.AsSpan(); + var version = extraData.TakeAndMove(1)[0]; + var denominator = BinaryPrimitives.ReadUInt32BigEndian(extraData.TakeAndMove(4)); + var elasticity = BinaryPrimitives.ReadUInt32BigEndian(extraData.TakeAndMove(4)); + + return new EIP1559Parameters(version, denominator, elasticity); + } +} From 5a8e70cc79e73d953fff1382104d857379670543 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:11:30 -0300 Subject: [PATCH 04/41] Validate ExtraData fields post-Holocene --- .../OptimismHeaderValidatorTests.cs | 104 ++++++++++++++++++ .../OptimismHeaderValidator.cs | 24 +++- 2 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs new file mode 100644 index 00000000000..2491e1b534e --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections.Generic; +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Consensus; +using Nethermind.Consensus.Validators; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Logging; +using Nethermind.Specs; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Optimism.Test; + +public class OptimismHeaderValidatorTests +{ + private static IEnumerable ValidEIP1559ParametersExtraData() + { + yield return "0x000000000100000000"; + yield return "0x0000000001000001bc"; + yield return "0x0000000001ffffffff"; + yield return "0x00ffffffff00000000"; + yield return "0x00ffffffff000001bc"; + yield return "0x00ffffffffffffffff"; + } + [TestCaseSource(nameof(ValidEIP1559ParametersExtraData))] + public void Validates_EIP1559Parameters_InExtraData_AfterHolocene(string hexString) + { + var genesis = Build.A.BlockHeader + .WithNumber(0) + .WithTimestamp(1_000) + .TestObject; + var header = Build.A.BlockHeader + .WithNumber(1) + .WithTimestamp(2_000) + .WithDifficulty(0) + .WithNonce(0) + .WithUnclesHash(Keccak.OfAnEmptySequenceRlp) + .WithExtraData(Bytes.FromHexString(hexString)).TestObject; + + var holoceneEnabledSpec = Substitute.For(); + holoceneEnabledSpec.IsOpHoloceneEnabled = true; + + var specProvider = Substitute.For(); + specProvider.GetSpec(header).Returns(holoceneEnabledSpec); + + var validator = new OptimismHeaderValidator( + AlwaysPoS.Instance, + Substitute.For(), + Always.Valid, + specProvider, + TestLogManager.Instance); + + var valid = validator.Validate(header, genesis); + + valid.Should().BeTrue(); + } + + private static IEnumerable InvalidEIP1559ParametersExtraData() + { + yield return "0x0"; + yield return "0xffffaaaa"; + yield return "0x01ffffffff00000000"; + yield return "0xff0000000100000001"; + yield return "0x000000000000000001"; + } + [TestCaseSource(nameof(InvalidEIP1559ParametersExtraData))] + public void Rejects_EIP1559Parameters_InExtraData_AfterHolocene(string hexString) + { + var genesis = Build.A.BlockHeader + .WithNumber(0) + .WithTimestamp(1_000) + .TestObject; + var header = Build.A.BlockHeader + .WithNumber(1) + .WithTimestamp(2_000) + .WithDifficulty(0) + .WithNonce(0) + .WithUnclesHash(Keccak.OfAnEmptySequenceRlp) + .WithExtraData(Bytes.FromHexString(hexString)).TestObject; + + var holoceneEnabledSpec = Substitute.For(); + holoceneEnabledSpec.IsOpHoloceneEnabled = true; + + var specProvider = Substitute.For(); + specProvider.GetSpec(header).Returns(holoceneEnabledSpec); + + var validator = new OptimismHeaderValidator( + AlwaysPoS.Instance, + Substitute.For(), + Always.Valid, + specProvider, + TestLogManager.Instance); + + var valid = validator.Validate(header, genesis); + + valid.Should().BeFalse(); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs index 0851b0a29fd..11812da9079 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs @@ -1,15 +1,13 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Diagnostics; +using System; using System.Diagnostics.CodeAnalysis; using Nethermind.Blockchain; using Nethermind.Consensus; -using Nethermind.Consensus.Messages; using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Merge.Plugin; @@ -39,5 +37,25 @@ public class OptimismHeaderValidator( new PreBedrockHeaderValidator(blockTree, sealValidator, specProvider, logManager), blockTree, specProvider, sealValidator, logManager) { + public override bool Validate(BlockHeader header, BlockHeader? parent, bool isUncle, out string? error) + { + IReleaseSpec spec = _specProvider.GetSpec(header); + if (spec.IsOpHoloceneEnabled) + { + try + { + // TODO: We might want to avoid using exceptions here + _ = header.DecodeEIP1559Parameters(); + } + catch (ArgumentException e) + { + error = e.Message; + return false; + } + } + + return base.Validate(header, parent, isUncle, out error); + } + protected override bool ValidateGasLimitRange(BlockHeader header, BlockHeader parent, IReleaseSpec spec, ref string? error) => true; } From c8aa8916e6a210b57baa01c599554a61b6c8f2c3 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:16:23 -0300 Subject: [PATCH 05/41] Collapse test cases --- .../OptimismHeaderValidatorTests.cs | 69 +++++-------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs index 2491e1b534e..e4382b926cf 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -19,58 +19,25 @@ namespace Nethermind.Optimism.Test; public class OptimismHeaderValidatorTests { - private static IEnumerable ValidEIP1559ParametersExtraData() + private static IEnumerable<(string, bool)> EIP1559ParametersExtraData() { - yield return "0x000000000100000000"; - yield return "0x0000000001000001bc"; - yield return "0x0000000001ffffffff"; - yield return "0x00ffffffff00000000"; - yield return "0x00ffffffff000001bc"; - yield return "0x00ffffffffffffffff"; + // Valid + yield return ("0x000000000100000000", true); + yield return ("0x0000000001000001bc", true); + yield return ("0x0000000001ffffffff", true); + yield return ("0x00ffffffff00000000", true); + yield return ("0x00ffffffff000001bc", true); + yield return ("0x00ffffffffffffffff", true); + // Invalid + yield return ("0x0", false); + yield return ("0xffffaaaa", false); + yield return ("0x01ffffffff00000000", false); + yield return ("0xff0000000100000001", false); + yield return ("0x000000000000000001", false); } - [TestCaseSource(nameof(ValidEIP1559ParametersExtraData))] - public void Validates_EIP1559Parameters_InExtraData_AfterHolocene(string hexString) - { - var genesis = Build.A.BlockHeader - .WithNumber(0) - .WithTimestamp(1_000) - .TestObject; - var header = Build.A.BlockHeader - .WithNumber(1) - .WithTimestamp(2_000) - .WithDifficulty(0) - .WithNonce(0) - .WithUnclesHash(Keccak.OfAnEmptySequenceRlp) - .WithExtraData(Bytes.FromHexString(hexString)).TestObject; - - var holoceneEnabledSpec = Substitute.For(); - holoceneEnabledSpec.IsOpHoloceneEnabled = true; - - var specProvider = Substitute.For(); - specProvider.GetSpec(header).Returns(holoceneEnabledSpec); - var validator = new OptimismHeaderValidator( - AlwaysPoS.Instance, - Substitute.For(), - Always.Valid, - specProvider, - TestLogManager.Instance); - - var valid = validator.Validate(header, genesis); - - valid.Should().BeTrue(); - } - - private static IEnumerable InvalidEIP1559ParametersExtraData() - { - yield return "0x0"; - yield return "0xffffaaaa"; - yield return "0x01ffffffff00000000"; - yield return "0xff0000000100000001"; - yield return "0x000000000000000001"; - } - [TestCaseSource(nameof(InvalidEIP1559ParametersExtraData))] - public void Rejects_EIP1559Parameters_InExtraData_AfterHolocene(string hexString) + [TestCaseSource(nameof(EIP1559ParametersExtraData))] + public void Validates_EIP1559Parameters_InExtraData_AfterHolocene((string HexString, bool Expected) testCase) { var genesis = Build.A.BlockHeader .WithNumber(0) @@ -82,7 +49,7 @@ public void Rejects_EIP1559Parameters_InExtraData_AfterHolocene(string hexString .WithDifficulty(0) .WithNonce(0) .WithUnclesHash(Keccak.OfAnEmptySequenceRlp) - .WithExtraData(Bytes.FromHexString(hexString)).TestObject; + .WithExtraData(Bytes.FromHexString(testCase.HexString)).TestObject; var holoceneEnabledSpec = Substitute.For(); holoceneEnabledSpec.IsOpHoloceneEnabled = true; @@ -99,6 +66,6 @@ public void Rejects_EIP1559Parameters_InExtraData_AfterHolocene(string hexString var valid = validator.Validate(header, genesis); - valid.Should().BeFalse(); + valid.Should().Be(testCase.Expected); } } From e0f5d5e4e1ebafbd4823f0b08f30942b2eb1c428 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 7 Nov 2024 17:18:49 -0300 Subject: [PATCH 06/41] Test for pre-Holocene ExtraData validation --- .../OptimismHeaderValidatorTests.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs index e4382b926cf..4eb93f76db3 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -37,7 +37,7 @@ public class OptimismHeaderValidatorTests } [TestCaseSource(nameof(EIP1559ParametersExtraData))] - public void Validates_EIP1559Parameters_InExtraData_AfterHolocene((string HexString, bool Expected) testCase) + public void Validates_EIP1559Parameters_InExtraData_AfterHolocene((string HexString, bool IsValid) testCase) { var genesis = Build.A.BlockHeader .WithNumber(0) @@ -66,6 +66,39 @@ public void Validates_EIP1559Parameters_InExtraData_AfterHolocene((string HexStr var valid = validator.Validate(header, genesis); - valid.Should().Be(testCase.Expected); + valid.Should().Be(testCase.IsValid); + } + + [TestCaseSource(nameof(EIP1559ParametersExtraData))] + public void Ignores_ExtraData_BeforeHolocene((string HexString, bool _) testCase) + { + var genesis = Build.A.BlockHeader + .WithNumber(0) + .WithTimestamp(1_000) + .TestObject; + var header = Build.A.BlockHeader + .WithNumber(1) + .WithTimestamp(2_000) + .WithDifficulty(0) + .WithNonce(0) + .WithUnclesHash(Keccak.OfAnEmptySequenceRlp) + .WithExtraData(Bytes.FromHexString(testCase.HexString)).TestObject; + + var holoceneDisabledSpec = Substitute.For(); + holoceneDisabledSpec.IsOpHoloceneEnabled = false; + + var specProvider = Substitute.For(); + specProvider.GetSpec(header).Returns(holoceneDisabledSpec); + + var validator = new OptimismHeaderValidator( + AlwaysPoS.Instance, + Substitute.For(), + Always.Valid, + specProvider, + TestLogManager.Instance); + + var valid = validator.Validate(header, genesis); + + valid.Should().BeTrue(); } } From d9a5c125e276f935932d73e231efee3f9e07100c Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 8 Nov 2024 13:30:04 -0300 Subject: [PATCH 07/41] Extract to interface --- .../Validators/HeaderValidatorTests.cs | 2 +- .../CliqueBlockProducer.cs | 2 +- .../Producers/BlockProducerBase.cs | 2 +- .../Producers/TxPoolTxSource.cs | 2 +- .../Transactions/BaseFeeTxFilter.cs | 2 +- .../Transactions/MinGasPriceTxFilter.cs | 2 +- .../Validators/HeaderValidator.cs | 2 +- .../Nethermind.Core.Test/BlockHeaderTests.cs | 6 +- .../Nethermind.Core/BaseFeeCalculator.cs | 97 +++++++++++-------- .../Nethermind.Facade/BlockchainBridge.cs | 2 +- .../Proxy/Models/Simulate/BlockOverride.cs | 2 +- .../Simulate/SimulateBridgeHelper.cs | 2 +- .../Eth/FeeHistory/FeeHistoryOracle.cs | 2 +- 13 files changed, 68 insertions(+), 57 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs index eb28f0160bc..e9dba694fe6 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs @@ -214,7 +214,7 @@ public void When_gaslimit_is_on_london_fork(long parentGasLimit, long blockNumbe .WithMixHash(new Hash256("0xd7db5fdd332d3a65d6ac9c4c530929369905734d3ef7a91e373e81d0f010b8e8")) .WithGasLimit(gasLimit) .WithNumber(_parentBlock.Number + 1) - .WithBaseFeePerGas(BaseFeeCalculator.Calculate(_parentBlock.Header, specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)))) + .WithBaseFeePerGas(new BaseFeeCalculator().Calculate(_parentBlock.Header, specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)))) .WithNonce(0).TestObject; _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); diff --git a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs index f85842a83ca..e36debfbaa6 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs @@ -462,7 +462,7 @@ ILogManager logManager var spec = _specProvider.GetSpec(header); - header.BaseFeePerGas = BaseFeeCalculator.Calculate(parentHeader, spec); + header.BaseFeePerGas = new BaseFeeCalculator().Calculate(parentHeader, spec); // Set the correct difficulty header.Difficulty = CalculateDifficulty(snapshot, _sealer.Address); header.TotalDifficulty = parentHeader.TotalDifficulty + header.Difficulty; diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs index d11d0190576..4df3218840a 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs @@ -241,7 +241,7 @@ protected virtual BlockHeader PrepareBlockHeader(BlockHeader parent, if (Logger.IsDebug) Logger.Debug($"Setting total difficulty to {parent.TotalDifficulty} + {difficulty}."); - header.BaseFeePerGas = BaseFeeCalculator.Calculate(parent, _specProvider.GetSpec(header)); + header.BaseFeePerGas = new BaseFeeCalculator().Calculate(parent, _specProvider.GetSpec(header)); return header; } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs index 70793885fd3..5845a434947 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs @@ -50,7 +50,7 @@ public IEnumerable GetTransactions(BlockHeader parent, long gasLimi { long blockNumber = parent.Number + 1; IReleaseSpec spec = _specProvider.GetSpec(parent); - UInt256 baseFee = BaseFeeCalculator.Calculate(parent, spec); + UInt256 baseFee = new BaseFeeCalculator().Calculate(parent, spec); IDictionary pendingTransactions = _transactionPool.GetPendingTransactionsBySender(); IDictionary pendingBlobTransactionsEquivalences = _transactionPool.GetPendingLightBlobTransactionsBySender(); IComparer comparer = GetComparer(parent, new BlockPreparationContext(baseFee, blockNumber)) diff --git a/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs b/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs index 39e67b95dc2..c8c9baf3044 100644 --- a/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs +++ b/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs @@ -24,7 +24,7 @@ public AcceptTxResult IsAllowed(Transaction tx, BlockHeader parentHeader) { long blockNumber = parentHeader.Number + 1; IEip1559Spec specFor1559 = _specProvider.GetSpecFor1559(blockNumber); - UInt256 baseFee = BaseFeeCalculator.Calculate(parentHeader, specFor1559); + UInt256 baseFee = new BaseFeeCalculator().Calculate(parentHeader, specFor1559); bool isEip1559Enabled = specFor1559.IsEip1559Enabled; bool skipCheck = tx.IsServiceTransaction || !isEip1559Enabled; diff --git a/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs b/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs index 8959fd30fc4..fa7480c43f9 100644 --- a/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs +++ b/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs @@ -40,7 +40,7 @@ public AcceptTxResult IsAllowed(Transaction tx, BlockHeader? parentHeader, in UI IReleaseSpec spec = _specProvider.GetSpec(blockNumber, blockTimestamp); if (spec.IsEip1559Enabled) { - baseFeePerGas = BaseFeeCalculator.Calculate(parentHeader, spec); + baseFeePerGas = new BaseFeeCalculator().Calculate(parentHeader, spec); tx.TryCalculatePremiumPerGas(baseFeePerGas, out premiumPerGas); } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 399e7f205a6..855b96ec694 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -98,7 +98,7 @@ protected virtual bool Validate1559(BlockHeader header, BlockHeader parent, IRel { if (spec.IsEip1559Enabled) { - UInt256? expectedBaseFee = BaseFeeCalculator.Calculate(parent, spec); + UInt256? expectedBaseFee = new BaseFeeCalculator().Calculate(parent, spec); if (expectedBaseFee != header.BaseFeePerGas) { diff --git a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs index 849c25d3364..6167259185b 100644 --- a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs @@ -96,7 +96,7 @@ public void Eip_1559_CalculateBaseFee_should_returns_zero_when_eip1559_not_enabl BlockHeader blockHeader = Build.A.BlockHeader.TestObject; blockHeader.Number = 2001; blockHeader.GasLimit = 100; - UInt256 baseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); + UInt256 baseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); Assert.That(baseFee, Is.EqualTo(UInt256.Zero)); } @@ -123,7 +123,7 @@ public void Eip_1559_CalculateBaseFee(long gasTarget, long baseFee, long expecte blockHeader.GasLimit = gasTarget * Eip1559Constants.DefaultElasticityMultiplier; blockHeader.BaseFeePerGas = (UInt256)baseFee; blockHeader.GasUsed = gasUsed; - UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); + UInt256 actualBaseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); Assert.That(actualBaseFee, Is.EqualTo((UInt256)expectedBaseFee)); } @@ -153,7 +153,7 @@ public void Eip_1559_CalculateBaseFee_shared_test_cases((BaseFeeTestCases Info, blockHeader.GasLimit = testCase.Info.ParentTargetGasUsed * Eip1559Constants.DefaultElasticityMultiplier; blockHeader.BaseFeePerGas = (UInt256)testCase.Info.ParentBaseFee; blockHeader.GasUsed = testCase.Info.ParentGasUsed; - UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); + UInt256 actualBaseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); Assert.That(actualBaseFee, Is.EqualTo((UInt256)testCase.Info.ExpectedBaseFee), testCase.Description); } diff --git a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs index 268997f6df9..9311eefb28d 100644 --- a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs @@ -1,58 +1,69 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Threading; using Nethermind.Core.Specs; using Nethermind.Int256; -namespace Nethermind.Core +namespace Nethermind.Core; + +public interface IBaseFeeCalculator +{ + UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559); +} + +/// +/// Calculate BaseFee based on parent and . +/// +public sealed class BaseFeeCalculator : IBaseFeeCalculator { - /// Calculate BaseFee based on block parent and release spec. - public static class BaseFeeCalculator + public static IBaseFeeCalculator Instance => + LazyInitializer.EnsureInitialized(ref _instance, () => new BaseFeeCalculator()); + private static BaseFeeCalculator? _instance; + + public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) { - public static UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) + UInt256 expectedBaseFee = parent.BaseFeePerGas; + if (specFor1559.IsEip1559Enabled) { - UInt256 expectedBaseFee = parent.BaseFeePerGas; - if (specFor1559.IsEip1559Enabled) + UInt256 parentBaseFee = parent.BaseFeePerGas; + long gasDelta; + UInt256 feeDelta; + bool isForkBlockNumber = specFor1559.Eip1559TransitionBlock == parent.Number + 1; + long parentGasTarget = parent.GasLimit / specFor1559.ElasticityMultiplier; + if (isForkBlockNumber) + parentGasTarget = parent.GasLimit; + + if (parent.GasUsed == parentGasTarget) + { + expectedBaseFee = parent.BaseFeePerGas; + } + else if (parent.GasUsed > parentGasTarget) { - UInt256 parentBaseFee = parent.BaseFeePerGas; - long gasDelta; - UInt256 feeDelta; - bool isForkBlockNumber = specFor1559.Eip1559TransitionBlock == parent.Number + 1; - long parentGasTarget = parent.GasLimit / specFor1559.ElasticityMultiplier; - if (isForkBlockNumber) - parentGasTarget = parent.GasLimit; - - if (parent.GasUsed == parentGasTarget) - { - expectedBaseFee = parent.BaseFeePerGas; - } - else if (parent.GasUsed > parentGasTarget) - { - gasDelta = parent.GasUsed - parentGasTarget; - feeDelta = UInt256.Max( - parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator, - UInt256.One); - expectedBaseFee = parentBaseFee + feeDelta; - } - else - { - gasDelta = parentGasTarget - parent.GasUsed; - feeDelta = parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator; - expectedBaseFee = UInt256.Max(parentBaseFee - feeDelta, 0); - } - - if (isForkBlockNumber) - { - expectedBaseFee = specFor1559.ForkBaseFee; - } - - if (specFor1559.Eip1559BaseFeeMinValue.HasValue) - { - expectedBaseFee = UInt256.Max(expectedBaseFee, specFor1559.Eip1559BaseFeeMinValue.Value); - } + gasDelta = parent.GasUsed - parentGasTarget; + feeDelta = UInt256.Max( + parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator, + UInt256.One); + expectedBaseFee = parentBaseFee + feeDelta; + } + else + { + gasDelta = parentGasTarget - parent.GasUsed; + feeDelta = parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator; + expectedBaseFee = UInt256.Max(parentBaseFee - feeDelta, 0); + } + + if (isForkBlockNumber) + { + expectedBaseFee = specFor1559.ForkBaseFee; } - return expectedBaseFee; + if (specFor1559.Eip1559BaseFeeMinValue.HasValue) + { + expectedBaseFee = UInt256.Max(expectedBaseFee, specFor1559.Eip1559BaseFeeMinValue.Value); + } } + + return expectedBaseFee; } } diff --git a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs index 42e3c884e2f..e7455cd400c 100644 --- a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs +++ b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs @@ -285,7 +285,7 @@ private TransactionResult CallAndRestore( IReleaseSpec releaseSpec = _specProvider.GetSpec(callHeader); callHeader.BaseFeePerGas = treatBlockHeaderAsParentBlock - ? BaseFeeCalculator.Calculate(blockHeader, releaseSpec) + ? new BaseFeeCalculator().Calculate(blockHeader, releaseSpec) : blockHeader.BaseFeePerGas; if (releaseSpec.IsEip4844Enabled) diff --git a/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs b/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs index 2db5e30b88d..8439b7ecb92 100644 --- a/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs +++ b/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs @@ -50,7 +50,7 @@ public BlockHeader GetBlockHeader(BlockHeader parent, IBlocksConfig cfg, IReleas newTime, Array.Empty()) { - BaseFeePerGas = BaseFeePerGas ?? BaseFeeCalculator.Calculate(parent, spec), + BaseFeePerGas = BaseFeePerGas ?? new BaseFeeCalculator().Calculate(parent, spec), MixHash = PrevRandao, IsPostMerge = parent.Difficulty == 0, TotalDifficulty = parent.TotalDifficulty + newDifficulty, diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs index 59c97ea3c5c..08851f33f28 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs @@ -293,7 +293,7 @@ private BlockHeader GetCallHeader(BlockStateCall b ? block.BlockOverrides.BaseFeePerGas.Value : !payloadValidation ? 0 - : BaseFeeCalculator.Calculate(parent, spec); + : new BaseFeeCalculator().Calculate(parent, spec); result.ExcessBlobGas = spec.IsEip4844Enabled ? BlobGasCalculator.CalculateExcessBlobGas(parent, spec) : (ulong?)0; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs index 28fe3a15d93..82a2c5407e5 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs @@ -112,7 +112,7 @@ BlockFeeHistorySearchInfo BlockFeeHistorySearchInfoFromBlock(Block b) return new( b.Number, b.BaseFeePerGas, - BaseFeeCalculator.Calculate(b.Header, _specProvider.GetSpecFor1559(b.Number + 1)), + new BaseFeeCalculator().Calculate(b.Header, _specProvider.GetSpecFor1559(b.Number + 1)), feePerBlobGas == UInt256.MaxValue ? UInt256.Zero : feePerBlobGas, b.GasUsed / (double)b.GasLimit, (b.BlobGasUsed ?? 0) / (double)Eip4844Constants.MaxBlobGasPerBlock, From 35d6ef073ff967ebdb5f7e4cf28cfed9a3841a09 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 8 Nov 2024 16:28:27 -0300 Subject: [PATCH 08/41] Allow for "zero" EIP1559 parameters --- .../Nethermind.Optimism.Test/EIP1559ParametersTests.cs | 1 + .../Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs | 1 + src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs | 5 ++--- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs index 5f52f13ffac..08db50d8b50 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -14,6 +14,7 @@ public class EIP1559ParametersTests { private static IEnumerable<(string hexString, EIP1559Parameters expected)> DecodeBlockHeaderParametersCases() { + yield return ("0x000000000000000000", new(0, 0, 0)); yield return ("0x000000000100000000", new(0, 1, 0)); yield return ("0x0000000001000001bc", new(0, 1, 444)); yield return ("0x0000000001ffffffff", new(0, 1, UInt32.MaxValue)); diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs index 4eb93f76db3..a13f024b446 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -22,6 +22,7 @@ public class OptimismHeaderValidatorTests private static IEnumerable<(string, bool)> EIP1559ParametersExtraData() { // Valid + yield return ("0x000000000000000000", true); yield return ("0x000000000100000000", true); yield return ("0x0000000001000001bc", true); yield return ("0x0000000001ffffffff", true); diff --git a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs index 9449f15c6c2..ea684ee4eca 100644 --- a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs +++ b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs @@ -17,9 +17,7 @@ public readonly struct EIP1559Parameters public EIP1559Parameters(byte version, UInt32 denominator, UInt32 elasticity) { if (version != 0) throw new ArgumentException($"{nameof(version)} must be 0", nameof(version)); - if (denominator == 0) throw new ArgumentException($"{nameof(denominator)} cannot be 0", nameof(denominator)); - // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 - // TODO: Revisit this check: https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L104 + if (denominator == 0 && elasticity != 0) throw new ArgumentException($"{nameof(denominator)} cannot be 0 unless {nameof(elasticity)} is also 0", nameof(denominator)); Version = version; Denominator = denominator; @@ -32,6 +30,7 @@ public static class BlockHeaderExtensions public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) { if (header.ExtraData.Length < 9) throw new ArgumentException($"{header.ExtraData} data must be at least 9 bytes long"); + // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 ReadOnlySpan extraData = header.ExtraData.AsSpan(); var version = extraData.TakeAndMove(1)[0]; From 412fdbcdd0555974e31a9f20c5131dbe7bb06807 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Mon, 11 Nov 2024 14:08:30 -0300 Subject: [PATCH 09/41] Add concrete `Eip1559Spec : IEip1559Spec` --- .../Nethermind.Core/Specs/IEip1559Spec.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index 811a4510a63..d9db7f70b27 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -22,4 +22,28 @@ public interface IEip1559Spec public UInt256 BaseFeeMaxChangeDenominator { get; } public long ElasticityMultiplier { get; } } + + public sealed class Eip1559Spec : IEip1559Spec + { + public bool IsEip1559Enabled { get; set; } + public long Eip1559TransitionBlock { get; set; } + public Address? FeeCollector { get; set; } + public UInt256? Eip1559BaseFeeMinValue { get; set; } + public UInt256 ForkBaseFee { get; set; } + public UInt256 BaseFeeMaxChangeDenominator { get; set; } + public long ElasticityMultiplier { get; set; } + + public Eip1559Spec() { } + + public Eip1559Spec(IEip1559Spec spec) + { + IsEip1559Enabled = spec.IsEip1559Enabled; + Eip1559TransitionBlock = spec.Eip1559TransitionBlock; + FeeCollector = spec.FeeCollector; + Eip1559BaseFeeMinValue = spec.Eip1559BaseFeeMinValue; + ForkBaseFee = spec.ForkBaseFee; + BaseFeeMaxChangeDenominator = spec.BaseFeeMaxChangeDenominator; + ElasticityMultiplier = spec.ElasticityMultiplier; + } + } } From 315cc23d22ce7bc497968e42cbeef5f0e83da3d2 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Mon, 11 Nov 2024 14:09:09 -0300 Subject: [PATCH 10/41] Add helper methods - WriteTo - IsZero --- .../Nethermind.Optimism/BlockHeaderExtensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs index ea684ee4eca..41ab211fd2c 100644 --- a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs +++ b/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs @@ -23,6 +23,15 @@ public EIP1559Parameters(byte version, UInt32 denominator, UInt32 elasticity) Denominator = denominator; Elasticity = elasticity; } + + public bool IsZero() => Denominator == 0 && Elasticity == 0; + + public void WriteTo(Span span) + { + span[0] = Version; + BinaryPrimitives.WriteUInt32BigEndian(span.Slice(1, 4), Denominator); + BinaryPrimitives.WriteUInt32BigEndian(span.Slice(5, 4), Elasticity); + } } public static class BlockHeaderExtensions From 0ce590800550ab89cb4a028b92367080d9f70a10 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Mon, 11 Nov 2024 14:09:40 -0300 Subject: [PATCH 11/41] Add OptimismBaseFeeCalculator - Source tests from op-geth --- .../OptimismBaseFeeCalculatorTests.cs | 55 +++++++++++++++++++ .../OptimismBaseFeeCalculator.cs | 35 ++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs create mode 100644 src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs new file mode 100644 index 00000000000..42081908218 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using Nethermind.Specs; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Optimism.Test; + +public class OptimismBaseFeeCalculatorTests +{ + /// + /// Tests sourced from + /// + [TestCase(15_000_000, 10_000_000, 10u, 2u)] // Target + [TestCase(10_000_000, 9_666_667, 10u, 2u)] // Below + [TestCase(20_000_000, 10_333_333, 10u, 2u)] // Above + [TestCase(3_000_000, 10_000_000, 2u, 10u)] // Target + [TestCase(1_000_000, 6_666_667, 2u, 10u)] // Below + [TestCase(30_000_000, 55_000_000, 2u, 10u)] // Above + public void CalculatesBaseFee_AfterHolocene_UsingExtraDataParameters(long gasUsed, long expectedBaseFee, UInt32 denominator, UInt32 elasticity) + { + IReleaseSpec releaseSpec = new ReleaseSpec + { + IsEip1559Enabled = true, + IsOpHoloceneEnabled = true + }; + + var extraData = new byte[32]; + var parameters = new EIP1559Parameters(0, denominator, elasticity); + parameters.WriteTo(extraData); + + BlockHeader blockHeader = Build.A.BlockHeader + .WithGasLimit(30_000_000) + .WithBaseFee(10_000_000) + .WithGasUsed(gasUsed) + .WithExtraData(extraData) + .TestObject; + + var specProvider = Substitute.For(); + specProvider.GetSpec(blockHeader).Returns(releaseSpec); + var calculator = new OptimismBaseFeeCalculator(new BaseFeeCalculator(), specProvider); + + UInt256 actualBaseFee = calculator.Calculate(blockHeader, releaseSpec); + + actualBaseFee.Should().Be((UInt256)expectedBaseFee); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs new file mode 100644 index 00000000000..1d080e4e565 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Int256; + +namespace Nethermind.Optimism; + +/// +/// See +/// +public sealed class OptimismBaseFeeCalculator( + IBaseFeeCalculator baseFeeCalculator, + ISpecProvider specProvider) : IBaseFeeCalculator +{ + public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) + { + var spec = specFor1559; + + var releaseSpec = specProvider.GetSpec(parent); + if (releaseSpec.IsOpHoloceneEnabled) + { + // NOTE: This operation should never fail since headers should be valid at this point + EIP1559Parameters eip1559Params = parent.DecodeEIP1559Parameters(); + spec = new Eip1559Spec(specFor1559) + { + ElasticityMultiplier = eip1559Params.IsZero() ? Eip1559Constants.DefaultElasticityMultiplier : eip1559Params.Elasticity, + BaseFeeMaxChangeDenominator = eip1559Params.IsZero() ? Eip1559Constants.DefaultBaseFeeMaxChangeDenominator : eip1559Params.Denominator + }; + } + + return baseFeeCalculator.Calculate(parent, spec); + } +} From 22fc7ad8d52b5b1caaacdd40aa951d72997d4e23 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Mon, 11 Nov 2024 14:10:05 -0300 Subject: [PATCH 12/41] Remove unused using --- .../Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs index 42081908218..c54c6b5e84a 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; -using System.Collections; using FluentAssertions; using Nethermind.Core; using Nethermind.Core.Specs; From 3d14ce3de536df3176233320632f618e1b40bf95 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Mon, 11 Nov 2024 14:24:52 -0300 Subject: [PATCH 13/41] Add `EIP1559Params` to `OptimismPayloadAttributes` --- .../OptimismPayloadAttributesTests.cs | 74 +++++++++++++++++++ .../Rpc/OptimismPayloadAttributes.cs | 32 +++++++- 2 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs new file mode 100644 index 00000000000..af318a3e5e7 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Collections; +using System.Collections.Generic; +using FluentAssertions; +using Nethermind.Consensus.Producers; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Optimism.Rpc; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Optimism.Test; + +public class OptimismEngineModuleTests +{ + private static IEnumerable<(string, string)> PayloadIdTestCases() + { + yield return ("0x000000000100000000", "0x00dea77451f10b20"); + yield return ("0x0000000001000001bc", "0xf2975f6725d5f2e5"); + yield return ("0x0000000001ffffffff", "0x6b09fc2a90d6c067"); + yield return ("0x00ffffffff00000000", "0x9787e23f29594f18"); + yield return ("0x00ffffffff000001bc", "0x2cb414f72aac7824"); + yield return ("0x00ffffffffffffffff", "0xe411646692277df5"); + } + [TestCaseSource(nameof(PayloadIdTestCases))] + public void Compute_PayloadID_with_EIP1559Params((string HexStringEIP1559Params, string PayloadId) testCase) + { + var blockHeader = Build.A.BlockHeader.TestObject; + var payloadAttributes = new OptimismPayloadAttributes + { + GasLimit = 1, + Transactions = [], + PrevRandao = Hash256.Zero, + SuggestedFeeRecipient = TestItem.AddressA, + EIP1559Params = Bytes.FromHexString(testCase.HexStringEIP1559Params) + }; + + var payloadId = payloadAttributes.GetPayloadId(blockHeader); + + payloadId.Should().Be(testCase.PayloadId); + } + + private static IEnumerable<(byte[]?, PayloadAttributesValidationResult)> Validate_EIP1559Params_AfterHolocene_TestCases() + { + yield return (null, PayloadAttributesValidationResult.InvalidPayloadAttributes); + yield return (new byte[7], PayloadAttributesValidationResult.InvalidPayloadAttributes); + yield return (new byte[8], PayloadAttributesValidationResult.Success); + yield return (new byte[9], PayloadAttributesValidationResult.InvalidPayloadAttributes); + } + [TestCaseSource(nameof(Validate_EIP1559Params_AfterHolocene_TestCases))] + public void Validate_EIP1559Params_AfterHolocene((byte[]? Eip1559Params, PayloadAttributesValidationResult ExpectedResult) testCase) + { + var payloadAttributes = new OptimismPayloadAttributes + { + GasLimit = 1, + Transactions = [], + PrevRandao = Hash256.Zero, + SuggestedFeeRecipient = TestItem.AddressA, + EIP1559Params = testCase.Eip1559Params + }; + + var releaseSpec = Substitute.For(); + releaseSpec.IsOpHoloceneEnabled.Returns(true); + var specProvider = Substitute.For(); + specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); + + var result = payloadAttributes.Validate(specProvider, 0, out var _); + result.Should().Be(testCase.ExpectedResult); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs index 8db3156fcae..d8b5fdb54d4 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs @@ -30,6 +30,12 @@ public byte[][]? Transactions public bool NoTxPool { get; set; } public long GasLimit { get; set; } public override long? GetGasLimit() => GasLimit; + + /// + /// See + /// + public byte[]? EIP1559Params { get; set; } + private int TransactionsLength => Transactions?.Length ?? 0; private Transaction[]? _transactions; @@ -64,8 +70,11 @@ public void SetTransactions(params Transaction[] transactions) } protected override int ComputePayloadIdMembersSize() => - // Add NoTxPool + Txs + GasLimit - base.ComputePayloadIdMembersSize() + sizeof(bool) + Keccak.Size * TransactionsLength + sizeof(long); + base.ComputePayloadIdMembersSize() + + sizeof(bool) // noTxPool + + (Keccak.Size * TransactionsLength) // Txs + + sizeof(long) // gasLimit + + ((EIP1559Params?.Length * sizeof(byte)) ?? 0); // eip1559Params protected override int WritePayloadIdMembers(BlockHeader parentHeader, Span inputSpan) { @@ -87,6 +96,12 @@ protected override int WritePayloadIdMembers(BlockHeader parentHeader, Span Date: Tue, 12 Nov 2024 12:49:21 -0300 Subject: [PATCH 14/41] Rename file --- ...BlockHeaderExtensions.cs => OptimismEIP1559Parameters.cs} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename src/Nethermind/Nethermind.Optimism/{BlockHeaderExtensions.cs => OptimismEIP1559Parameters.cs} (92%) diff --git a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs similarity index 92% rename from src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs rename to src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs index 41ab211fd2c..4b3768a37f7 100644 --- a/src/Nethermind/Nethermind.Optimism/BlockHeaderExtensions.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -5,6 +5,7 @@ using System.Buffers.Binary; using Nethermind.Core; using Nethermind.Core.Extensions; +using Nethermind.Optimism.Rpc; namespace Nethermind.Optimism; @@ -34,11 +35,11 @@ public void WriteTo(Span span) } } -public static class BlockHeaderExtensions +public static class EIP1559ParametersExtensions { public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) { - if (header.ExtraData.Length < 9) throw new ArgumentException($"{header.ExtraData} data must be at least 9 bytes long"); + if (header.ExtraData.Length < 9) throw new ArgumentException($"{nameof(header.ExtraData)} data must be at least 9 bytes long"); // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 ReadOnlySpan extraData = header.ExtraData.AsSpan(); From defe8f7e0209e0072cbbb470d6df25c324618429 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 11:44:32 -0300 Subject: [PATCH 15/41] Use correct version - Would always result in validation errors --- .../Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs index af318a3e5e7..9bb82680403 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -68,7 +68,7 @@ public void Validate_EIP1559Params_AfterHolocene((byte[]? Eip1559Params, Payload var specProvider = Substitute.For(); specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); - var result = payloadAttributes.Validate(specProvider, 0, out var _); + var result = payloadAttributes.Validate(specProvider, 1, out var _); result.Should().Be(testCase.ExpectedResult); } } From ecabab64b758d64595315b96477bb663ca6c54bb Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 12:49:53 -0300 Subject: [PATCH 16/41] Decode EIP1559 parameters from `OptimismPayloadAttributes` --- .../Nethermind.Optimism/OptimismEIP1559Parameters.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs index 4b3768a37f7..e9a44ab1563 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -49,4 +49,15 @@ public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) return new EIP1559Parameters(version, denominator, elasticity); } + + public static EIP1559Parameters DecodeEIP1559Parameters(this OptimismPayloadAttributes attributes) + { + if (attributes.EIP1559Params?.Length != 8) throw new ArgumentException($"{nameof(attributes.EIP1559Params)} must be 8 bytes long"); + + ReadOnlySpan span = attributes.EIP1559Params.AsSpan(); + var denominator = BinaryPrimitives.ReadUInt32BigEndian(span.TakeAndMove(4)); + var elasticity = BinaryPrimitives.ReadUInt32BigEndian(span.TakeAndMove(4)); + + return new EIP1559Parameters(0, denominator, elasticity); + } } From b7db888f79b52fc51cecca9a086987366c396134 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 12:50:40 -0300 Subject: [PATCH 17/41] Include EIP1559 params from attributes into block header --- .../OptimismPayloadPreparationServiceTests.cs | 89 +++++++++++++++++++ .../OptimismPayloadPreparationService.cs | 21 ++++- .../Nethermind.Optimism/OptimismPlugin.cs | 1 + 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs new file mode 100644 index 00000000000..6b23e2e4442 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core.Test.Builders; +using NUnit.Framework; +using FluentAssertions; +using System; +using NSubstitute; +using Nethermind.Core.Specs; +using Nethermind.Merge.Plugin.BlockProduction; +using Nethermind.Core.Timers; +using Nethermind.Logging; +using Nethermind.Optimism.Rpc; +using Nethermind.Consensus.Transactions; +using Nethermind.Consensus.Processing; +using Nethermind.Blockchain; +using Nethermind.State; +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Config; +using Nethermind.Core.Crypto; +using Nethermind.Evm.Tracing; +using System.Buffers.Binary; +using System.Threading.Tasks; + +namespace Nethermind.Optimism.Test; + +public class OptimismPayloadPreparationServiceTests +{ + [TestCase(8u, 2u)] + [TestCase(2u, 2u)] + [TestCase(2u, 10u)] + public async Task Writes_EIP1559Params_Into_HeaderExtraData(UInt32 denominator, UInt32 elasticity) + { + var parent = Build.A.BlockHeader.TestObject; + + var releaseSpec = Substitute.For(); + releaseSpec.IsOpHoloceneEnabled.Returns(true); + var specProvider = Substitute.For(); + specProvider.GetSpec(parent).Returns(releaseSpec); + + var stateProvider = Substitute.For(); + stateProvider.HasStateForRoot(Arg.Any()).Returns(true); + + var block = Build.A.Block + .WithExtraData([]) + .TestObject; + IBlockchainProcessor processor = Substitute.For(); + processor.Process(Arg.Any(), ProcessingOptions.ProducingBlock, Arg.Any()).Returns(block); + + var service = new OptimismPayloadPreparationService( + blockProducer: new PostMergeBlockProducer( + processor: processor, + specProvider: specProvider, + stateProvider: stateProvider, + txSource: Substitute.For(), + blockTree: Substitute.For(), + gasLimitCalculator: Substitute.For(), + sealEngine: Substitute.For(), + timestamper: Substitute.For(), + miningConfig: Substitute.For(), + logManager: TestLogManager.Instance + ), + specProvider: specProvider, + blockImprovementContextFactory: NoBlockImprovementContextFactory.Instance, + timePerSlot: TimeSpan.FromSeconds(1), + timerFactory: Substitute.For(), + logManager: TestLogManager.Instance + ); + + var eip1559Params = new byte[8]; + BinaryPrimitives.WriteUInt32BigEndian(eip1559Params.AsSpan(0, 4), denominator); + BinaryPrimitives.WriteUInt32BigEndian(eip1559Params.AsSpan(4, 4), elasticity); + + var attributes = new OptimismPayloadAttributes() + { + PrevRandao = Hash256.Zero, + SuggestedFeeRecipient = TestItem.AddressA, + EIP1559Params = eip1559Params, + }; + + var payloadId = service.StartPreparingPayload(parent, attributes); + var context = await service.GetPayload(payloadId); + var currentBestBlock = context?.CurrentBestBlock!; + + currentBestBlock.Should().Be(block); + currentBestBlock.Header.DecodeEIP1559Parameters().Should().BeEquivalentTo(new EIP1559Parameters(0, denominator, elasticity)); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs index 35fd2e3abb2..539c26fa85b 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs @@ -4,6 +4,7 @@ using System; using Nethermind.Consensus.Producers; using Nethermind.Core; +using Nethermind.Core.Specs; using Nethermind.Core.Timers; using Nethermind.Int256; using Nethermind.Logging; @@ -14,9 +15,11 @@ namespace Nethermind.Optimism; public class OptimismPayloadPreparationService : PayloadPreparationService { + private readonly ISpecProvider _specProvider; private readonly ILogger _logger; public OptimismPayloadPreparationService( + ISpecProvider specProvider, PostMergeBlockProducer blockProducer, IBlockImprovementContextFactory blockImprovementContextFactory, ITimerFactory timerFactory, @@ -35,6 +38,7 @@ public OptimismPayloadPreparationService( improvementDelay, minTimeForProduction) { + _specProvider = specProvider; _logger = logManager.GetClassLogger(); } @@ -50,6 +54,21 @@ protected override void ImproveBlock(string payloadId, BlockHeader parentHeader, _payloadStorage.TryAdd(payloadId, new NoBlockImprovementContext(currentBestBlock, UInt256.Zero, startDateTime)); } - else base.ImproveBlock(payloadId, parentHeader, payloadAttributes, currentBestBlock, startDateTime); + else + { + if (payloadAttributes is OptimismPayloadAttributes optimismPayload) + { + var spec = _specProvider.GetSpec(currentBestBlock.Header); + if (spec.IsOpHoloceneEnabled) + { + var eip1559Parameters = optimismPayload.DecodeEIP1559Parameters(); + + currentBestBlock.Header.ExtraData = new byte[32]; + eip1559Parameters.WriteTo(currentBestBlock.Header.ExtraData); + } + } + + base.ImproveBlock(payloadId, parentHeader, payloadAttributes, currentBestBlock, startDateTime); + } } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 0afaefd7d78..f61f0b8595f 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -217,6 +217,7 @@ public async Task InitRpcModules() TimeSpan.FromSeconds(_blocksConfig.SecondsPerSlot)); OptimismPayloadPreparationService payloadPreparationService = new( + _api.SpecProvider, (PostMergeBlockProducer)_api.BlockProducer, improvementContextFactory, _api.TimerFactory, From aef8a7c3fd9d84113b484279d0022181de8a52ad Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 12:59:37 -0300 Subject: [PATCH 18/41] Extract required length to constant --- .../OptimismBaseFeeCalculatorTests.cs | 2 +- .../Nethermind.Optimism/OptimismEIP1559Parameters.cs | 4 +++- .../Nethermind.Optimism/OptimismPayloadPreparationService.cs | 5 ++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs index c54c6b5e84a..cb13ad47fdd 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -32,7 +32,7 @@ public void CalculatesBaseFee_AfterHolocene_UsingExtraDataParameters(long gasUse IsOpHoloceneEnabled = true }; - var extraData = new byte[32]; + var extraData = new byte[EIP1559Parameters.ByteLength]; var parameters = new EIP1559Parameters(0, denominator, elasticity); parameters.WriteTo(extraData); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs index e9a44ab1563..ee1d6a27576 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -11,6 +11,8 @@ namespace Nethermind.Optimism; public readonly struct EIP1559Parameters { + public const int ByteLength = 9; + public byte Version { get; } public UInt32 Denominator { get; } public UInt32 Elasticity { get; } @@ -39,7 +41,7 @@ public static class EIP1559ParametersExtensions { public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) { - if (header.ExtraData.Length < 9) throw new ArgumentException($"{nameof(header.ExtraData)} data must be at least 9 bytes long"); + if (header.ExtraData.Length < EIP1559Parameters.ByteLength) throw new ArgumentException($"{nameof(header.ExtraData)} data must be at least 9 bytes long"); // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 ReadOnlySpan extraData = header.ExtraData.AsSpan(); diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs index 539c26fa85b..967410636dd 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs @@ -61,9 +61,8 @@ protected override void ImproveBlock(string payloadId, BlockHeader parentHeader, var spec = _specProvider.GetSpec(currentBestBlock.Header); if (spec.IsOpHoloceneEnabled) { - var eip1559Parameters = optimismPayload.DecodeEIP1559Parameters(); - - currentBestBlock.Header.ExtraData = new byte[32]; + EIP1559Parameters eip1559Parameters = optimismPayload.DecodeEIP1559Parameters(); + currentBestBlock.Header.ExtraData = new byte[EIP1559Parameters.ByteLength]; eip1559Parameters.WriteTo(currentBestBlock.Header.ExtraData); } } From 67c6577df7433e02e552847e5722690c124ffe69 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 13:40:31 -0300 Subject: [PATCH 19/41] Remove unused constructor --- src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index d9db7f70b27..cb7a4e3ff3d 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -33,8 +33,6 @@ public sealed class Eip1559Spec : IEip1559Spec public UInt256 BaseFeeMaxChangeDenominator { get; set; } public long ElasticityMultiplier { get; set; } - public Eip1559Spec() { } - public Eip1559Spec(IEip1559Spec spec) { IsEip1559Enabled = spec.IsEip1559Enabled; From 551976c05e1f19bd45b29e753d66991593b9df77 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 14:02:30 -0300 Subject: [PATCH 20/41] Test payload validation before Holocene --- .../OptimismPayloadAttributesTests.cs | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs index 9bb82680403..c2477a32a3d 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Collections; using System.Collections.Generic; using FluentAssertions; using Nethermind.Consensus.Producers; @@ -44,15 +43,15 @@ public void Compute_PayloadID_with_EIP1559Params((string HexStringEIP1559Params, payloadId.Should().Be(testCase.PayloadId); } - private static IEnumerable<(byte[]?, PayloadAttributesValidationResult)> Validate_EIP1559Params_AfterHolocene_TestCases() + private static IEnumerable<(byte[]?, PayloadAttributesValidationResult, PayloadAttributesValidationResult)> Validate_EIP1559Params_Holocene_TestCases() { - yield return (null, PayloadAttributesValidationResult.InvalidPayloadAttributes); - yield return (new byte[7], PayloadAttributesValidationResult.InvalidPayloadAttributes); - yield return (new byte[8], PayloadAttributesValidationResult.Success); - yield return (new byte[9], PayloadAttributesValidationResult.InvalidPayloadAttributes); + yield return (null, PayloadAttributesValidationResult.Success, PayloadAttributesValidationResult.InvalidPayloadAttributes); + yield return (new byte[7], PayloadAttributesValidationResult.InvalidPayloadAttributes, PayloadAttributesValidationResult.InvalidPayloadAttributes); + yield return (new byte[8], PayloadAttributesValidationResult.InvalidPayloadAttributes, PayloadAttributesValidationResult.Success); + yield return (new byte[9], PayloadAttributesValidationResult.InvalidPayloadAttributes, PayloadAttributesValidationResult.InvalidPayloadAttributes); } - [TestCaseSource(nameof(Validate_EIP1559Params_AfterHolocene_TestCases))] - public void Validate_EIP1559Params_AfterHolocene((byte[]? Eip1559Params, PayloadAttributesValidationResult ExpectedResult) testCase) + [TestCaseSource(nameof(Validate_EIP1559Params_Holocene_TestCases))] + public void Validate_EIP1559Params_Holocene((byte[]? Eip1559Params, PayloadAttributesValidationResult BeforeHolocene, PayloadAttributesValidationResult AfterHolocene) testCase) { var payloadAttributes = new OptimismPayloadAttributes { @@ -63,12 +62,19 @@ public void Validate_EIP1559Params_AfterHolocene((byte[]? Eip1559Params, Payload EIP1559Params = testCase.Eip1559Params }; - var releaseSpec = Substitute.For(); - releaseSpec.IsOpHoloceneEnabled.Returns(true); - var specProvider = Substitute.For(); - specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); + static ISpecProvider BuildSpecProvider(bool isHolocene) + { + var releaseSpec = Substitute.For(); + releaseSpec.IsOpHoloceneEnabled.Returns(isHolocene); + var specProvider = Substitute.For(); + specProvider.GetSpec(Arg.Any()).Returns(releaseSpec); + return specProvider; + } + + var beforeHolocene = payloadAttributes.Validate(BuildSpecProvider(isHolocene: false), 1, out var _); + beforeHolocene.Should().Be(testCase.BeforeHolocene); - var result = payloadAttributes.Validate(specProvider, 1, out var _); - result.Should().Be(testCase.ExpectedResult); + var afterHolocene = payloadAttributes.Validate(BuildSpecProvider(isHolocene: true), 1, out var _); + afterHolocene.Should().Be(testCase.AfterHolocene); } } From 53b6f8eef13c119ed298bdcfd89a4e1e4c0b6ea5 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 17:49:19 -0300 Subject: [PATCH 21/41] Reintroduce `BaseFeeCalculator` singleton =( --- .../Validators/HeaderValidatorTests.cs | 2 +- .../CliqueBlockProducer.cs | 2 +- .../Producers/BlockProducerBase.cs | 2 +- .../Producers/TxPoolTxSource.cs | 2 +- .../Transactions/BaseFeeTxFilter.cs | 2 +- .../Transactions/MinGasPriceTxFilter.cs | 2 +- .../Validators/HeaderValidator.cs | 2 +- .../Nethermind.Core.Test/BlockHeaderTests.cs | 6 ++--- .../Nethermind.Core/BaseFeeCalculator.cs | 24 ++++++++++++++----- .../Nethermind.Facade/BlockchainBridge.cs | 2 +- .../Proxy/Models/Simulate/BlockOverride.cs | 2 +- .../Simulate/SimulateBridgeHelper.cs | 2 +- .../Eth/FeeHistory/FeeHistoryOracle.cs | 2 +- .../OptimismBaseFeeCalculatorTests.cs | 5 ++-- 14 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs index e9dba694fe6..eb28f0160bc 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/HeaderValidatorTests.cs @@ -214,7 +214,7 @@ public void When_gaslimit_is_on_london_fork(long parentGasLimit, long blockNumbe .WithMixHash(new Hash256("0xd7db5fdd332d3a65d6ac9c4c530929369905734d3ef7a91e373e81d0f010b8e8")) .WithGasLimit(gasLimit) .WithNumber(_parentBlock.Number + 1) - .WithBaseFeePerGas(new BaseFeeCalculator().Calculate(_parentBlock.Header, specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)))) + .WithBaseFeePerGas(BaseFeeCalculator.Calculate(_parentBlock.Header, specProvider.GetSpec((ForkActivation)(_parentBlock.Number + 1)))) .WithNonce(0).TestObject; _block.Header.SealEngineType = SealEngineType.None; _block.Header.Hash = _block.CalculateHash(); diff --git a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs index e36debfbaa6..f85842a83ca 100644 --- a/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs +++ b/src/Nethermind/Nethermind.Consensus.Clique/CliqueBlockProducer.cs @@ -462,7 +462,7 @@ ILogManager logManager var spec = _specProvider.GetSpec(header); - header.BaseFeePerGas = new BaseFeeCalculator().Calculate(parentHeader, spec); + header.BaseFeePerGas = BaseFeeCalculator.Calculate(parentHeader, spec); // Set the correct difficulty header.Difficulty = CalculateDifficulty(snapshot, _sealer.Address); header.TotalDifficulty = parentHeader.TotalDifficulty + header.Difficulty; diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs index 4df3218840a..d11d0190576 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockProducerBase.cs @@ -241,7 +241,7 @@ protected virtual BlockHeader PrepareBlockHeader(BlockHeader parent, if (Logger.IsDebug) Logger.Debug($"Setting total difficulty to {parent.TotalDifficulty} + {difficulty}."); - header.BaseFeePerGas = new BaseFeeCalculator().Calculate(parent, _specProvider.GetSpec(header)); + header.BaseFeePerGas = BaseFeeCalculator.Calculate(parent, _specProvider.GetSpec(header)); return header; } diff --git a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs index 5845a434947..70793885fd3 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs @@ -50,7 +50,7 @@ public IEnumerable GetTransactions(BlockHeader parent, long gasLimi { long blockNumber = parent.Number + 1; IReleaseSpec spec = _specProvider.GetSpec(parent); - UInt256 baseFee = new BaseFeeCalculator().Calculate(parent, spec); + UInt256 baseFee = BaseFeeCalculator.Calculate(parent, spec); IDictionary pendingTransactions = _transactionPool.GetPendingTransactionsBySender(); IDictionary pendingBlobTransactionsEquivalences = _transactionPool.GetPendingLightBlobTransactionsBySender(); IComparer comparer = GetComparer(parent, new BlockPreparationContext(baseFee, blockNumber)) diff --git a/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs b/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs index c8c9baf3044..39e67b95dc2 100644 --- a/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs +++ b/src/Nethermind/Nethermind.Consensus/Transactions/BaseFeeTxFilter.cs @@ -24,7 +24,7 @@ public AcceptTxResult IsAllowed(Transaction tx, BlockHeader parentHeader) { long blockNumber = parentHeader.Number + 1; IEip1559Spec specFor1559 = _specProvider.GetSpecFor1559(blockNumber); - UInt256 baseFee = new BaseFeeCalculator().Calculate(parentHeader, specFor1559); + UInt256 baseFee = BaseFeeCalculator.Calculate(parentHeader, specFor1559); bool isEip1559Enabled = specFor1559.IsEip1559Enabled; bool skipCheck = tx.IsServiceTransaction || !isEip1559Enabled; diff --git a/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs b/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs index fa7480c43f9..8959fd30fc4 100644 --- a/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs +++ b/src/Nethermind/Nethermind.Consensus/Transactions/MinGasPriceTxFilter.cs @@ -40,7 +40,7 @@ public AcceptTxResult IsAllowed(Transaction tx, BlockHeader? parentHeader, in UI IReleaseSpec spec = _specProvider.GetSpec(blockNumber, blockTimestamp); if (spec.IsEip1559Enabled) { - baseFeePerGas = new BaseFeeCalculator().Calculate(parentHeader, spec); + baseFeePerGas = BaseFeeCalculator.Calculate(parentHeader, spec); tx.TryCalculatePremiumPerGas(baseFeePerGas, out premiumPerGas); } diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 855b96ec694..399e7f205a6 100644 --- a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs +++ b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs @@ -98,7 +98,7 @@ protected virtual bool Validate1559(BlockHeader header, BlockHeader parent, IRel { if (spec.IsEip1559Enabled) { - UInt256? expectedBaseFee = new BaseFeeCalculator().Calculate(parent, spec); + UInt256? expectedBaseFee = BaseFeeCalculator.Calculate(parent, spec); if (expectedBaseFee != header.BaseFeePerGas) { diff --git a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs index 6167259185b..849c25d3364 100644 --- a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs @@ -96,7 +96,7 @@ public void Eip_1559_CalculateBaseFee_should_returns_zero_when_eip1559_not_enabl BlockHeader blockHeader = Build.A.BlockHeader.TestObject; blockHeader.Number = 2001; blockHeader.GasLimit = 100; - UInt256 baseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); + UInt256 baseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); Assert.That(baseFee, Is.EqualTo(UInt256.Zero)); } @@ -123,7 +123,7 @@ public void Eip_1559_CalculateBaseFee(long gasTarget, long baseFee, long expecte blockHeader.GasLimit = gasTarget * Eip1559Constants.DefaultElasticityMultiplier; blockHeader.BaseFeePerGas = (UInt256)baseFee; blockHeader.GasUsed = gasUsed; - UInt256 actualBaseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); + UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); Assert.That(actualBaseFee, Is.EqualTo((UInt256)expectedBaseFee)); } @@ -153,7 +153,7 @@ public void Eip_1559_CalculateBaseFee_shared_test_cases((BaseFeeTestCases Info, blockHeader.GasLimit = testCase.Info.ParentTargetGasUsed * Eip1559Constants.DefaultElasticityMultiplier; blockHeader.BaseFeePerGas = (UInt256)testCase.Info.ParentBaseFee; blockHeader.GasUsed = testCase.Info.ParentGasUsed; - UInt256 actualBaseFee = new BaseFeeCalculator().Calculate(blockHeader, releaseSpec); + UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); Assert.That(actualBaseFee, Is.EqualTo((UInt256)testCase.Info.ExpectedBaseFee), testCase.Description); } diff --git a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs index 9311eefb28d..aba13e4fb29 100644 --- a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System.Threading; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -15,12 +14,8 @@ public interface IBaseFeeCalculator /// /// Calculate BaseFee based on parent and . /// -public sealed class BaseFeeCalculator : IBaseFeeCalculator +public sealed class DefaultBaseFeeCalculator : IBaseFeeCalculator { - public static IBaseFeeCalculator Instance => - LazyInitializer.EnsureInitialized(ref _instance, () => new BaseFeeCalculator()); - private static BaseFeeCalculator? _instance; - public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) { UInt256 expectedBaseFee = parent.BaseFeePerGas; @@ -67,3 +62,20 @@ public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) return expectedBaseFee; } } + +/// +/// This class is a hack to support custom base fee calculations while still using a Singleton +/// Due to the extensive use of `BaseFeeCalculator.Calculate` in the codebase, it is not feasible to pass the calculator as a parameter. +/// Thus, for now we will use a Singleton pattern to allow for custom base fee calculations. +/// +/// When required, plugins can call to modify the global +/// +public static class BaseFeeCalculator +{ + private static IBaseFeeCalculator _instance = new DefaultBaseFeeCalculator(); + + public static void Override(IBaseFeeCalculator calculator) => _instance = calculator; + public static void Reset() => _instance = new DefaultBaseFeeCalculator(); + + public static UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) => _instance!.Calculate(parent, specFor1559); +} diff --git a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs index e7455cd400c..42e3c884e2f 100644 --- a/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs +++ b/src/Nethermind/Nethermind.Facade/BlockchainBridge.cs @@ -285,7 +285,7 @@ private TransactionResult CallAndRestore( IReleaseSpec releaseSpec = _specProvider.GetSpec(callHeader); callHeader.BaseFeePerGas = treatBlockHeaderAsParentBlock - ? new BaseFeeCalculator().Calculate(blockHeader, releaseSpec) + ? BaseFeeCalculator.Calculate(blockHeader, releaseSpec) : blockHeader.BaseFeePerGas; if (releaseSpec.IsEip4844Enabled) diff --git a/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs b/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs index 8439b7ecb92..2db5e30b88d 100644 --- a/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs +++ b/src/Nethermind/Nethermind.Facade/Proxy/Models/Simulate/BlockOverride.cs @@ -50,7 +50,7 @@ public BlockHeader GetBlockHeader(BlockHeader parent, IBlocksConfig cfg, IReleas newTime, Array.Empty()) { - BaseFeePerGas = BaseFeePerGas ?? new BaseFeeCalculator().Calculate(parent, spec), + BaseFeePerGas = BaseFeePerGas ?? BaseFeeCalculator.Calculate(parent, spec), MixHash = PrevRandao, IsPostMerge = parent.Difficulty == 0, TotalDifficulty = parent.TotalDifficulty + newDifficulty, diff --git a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs index 08851f33f28..59c97ea3c5c 100644 --- a/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs +++ b/src/Nethermind/Nethermind.Facade/Simulate/SimulateBridgeHelper.cs @@ -293,7 +293,7 @@ private BlockHeader GetCallHeader(BlockStateCall b ? block.BlockOverrides.BaseFeePerGas.Value : !payloadValidation ? 0 - : new BaseFeeCalculator().Calculate(parent, spec); + : BaseFeeCalculator.Calculate(parent, spec); result.ExcessBlobGas = spec.IsEip4844Enabled ? BlobGasCalculator.CalculateExcessBlobGas(parent, spec) : (ulong?)0; diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs index 82a2c5407e5..28fe3a15d93 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Eth/FeeHistory/FeeHistoryOracle.cs @@ -112,7 +112,7 @@ BlockFeeHistorySearchInfo BlockFeeHistorySearchInfoFromBlock(Block b) return new( b.Number, b.BaseFeePerGas, - new BaseFeeCalculator().Calculate(b.Header, _specProvider.GetSpecFor1559(b.Number + 1)), + BaseFeeCalculator.Calculate(b.Header, _specProvider.GetSpecFor1559(b.Number + 1)), feePerBlobGas == UInt256.MaxValue ? UInt256.Zero : feePerBlobGas, b.GasUsed / (double)b.GasLimit, (b.BlobGasUsed ?? 0) / (double)Eip4844Constants.MaxBlobGasPerBlock, diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs index cb13ad47fdd..d14ee290435 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -45,9 +45,10 @@ public void CalculatesBaseFee_AfterHolocene_UsingExtraDataParameters(long gasUse var specProvider = Substitute.For(); specProvider.GetSpec(blockHeader).Returns(releaseSpec); - var calculator = new OptimismBaseFeeCalculator(new BaseFeeCalculator(), specProvider); - UInt256 actualBaseFee = calculator.Calculate(blockHeader, releaseSpec); + BaseFeeCalculator.Override(new OptimismBaseFeeCalculator(new DefaultBaseFeeCalculator(), specProvider)); + UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); + BaseFeeCalculator.Reset(); actualBaseFee.Should().Be((UInt256)expectedBaseFee); } From ae5c231434e26667773f009e0512e1f23dba466d Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 17:56:44 -0300 Subject: [PATCH 22/41] Fix wrong test name --- .../Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs index c2477a32a3d..f128a880231 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -14,7 +14,7 @@ namespace Nethermind.Optimism.Test; -public class OptimismEngineModuleTests +public class OptimismPayloadAttributesTests { private static IEnumerable<(string, string)> PayloadIdTestCases() { From 97831af7be749cc91139518012fd4fc75d9660f3 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 17:59:48 -0300 Subject: [PATCH 23/41] Mark tests parallelizable --- .../Nethermind.Optimism.Test/EIP1559ParametersTests.cs | 1 + .../Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs | 1 + .../Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs | 1 + .../OptimismPayloadPreparationServiceTests.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs index 08db50d8b50..a602e46323d 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -10,6 +10,7 @@ namespace Nethermind.Optimism.Test; +[Parallelizable(ParallelScope.All)] public class EIP1559ParametersTests { private static IEnumerable<(string hexString, EIP1559Parameters expected)> DecodeBlockHeaderParametersCases() diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs index a13f024b446..8a160f80032 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -17,6 +17,7 @@ namespace Nethermind.Optimism.Test; +[Parallelizable(ParallelScope.All)] public class OptimismHeaderValidatorTests { private static IEnumerable<(string, bool)> EIP1559ParametersExtraData() diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs index f128a880231..508c42b4b28 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -14,6 +14,7 @@ namespace Nethermind.Optimism.Test; +[Parallelizable(ParallelScope.All)] public class OptimismPayloadAttributesTests { private static IEnumerable<(string, string)> PayloadIdTestCases() diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs index 6b23e2e4442..7c3dd733ede 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs @@ -25,6 +25,7 @@ namespace Nethermind.Optimism.Test; +[Parallelizable(ParallelScope.All)] public class OptimismPayloadPreparationServiceTests { [TestCase(8u, 2u)] From 44bbeebe7cb160104d72eb827c37522933fd9c72 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 13 Nov 2024 18:01:51 -0300 Subject: [PATCH 24/41] Override `BaseFeeCalculator` in OP --- src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index f61f0b8595f..ad9bd83a73f 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -123,6 +123,8 @@ public Task Init(INethermindApi api) _api.SealValidator = NullSealEngine.Instance; _api.GossipPolicy = ShouldNotGossip.Instance; + BaseFeeCalculator.Override(new OptimismBaseFeeCalculator(new DefaultBaseFeeCalculator(), _api.SpecProvider)); + _api.BlockPreprocessor.AddFirst(new MergeProcessingRecoveryStep(_api.PoSSwitcher)); return Task.CompletedTask; From db743b6e0e23e47c70fcce5338f87098954599ef Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 12:55:05 -0300 Subject: [PATCH 25/41] Use init only properties --- .../Nethermind.Core/Specs/IEip1559Spec.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index cb7a4e3ff3d..17743e52b48 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -25,13 +25,13 @@ public interface IEip1559Spec public sealed class Eip1559Spec : IEip1559Spec { - public bool IsEip1559Enabled { get; set; } - public long Eip1559TransitionBlock { get; set; } - public Address? FeeCollector { get; set; } - public UInt256? Eip1559BaseFeeMinValue { get; set; } - public UInt256 ForkBaseFee { get; set; } - public UInt256 BaseFeeMaxChangeDenominator { get; set; } - public long ElasticityMultiplier { get; set; } + public bool IsEip1559Enabled { get; init; } + public long Eip1559TransitionBlock { get; init; } + public Address? FeeCollector { get; init; } + public UInt256? Eip1559BaseFeeMinValue { get; init; } + public UInt256 ForkBaseFee { get; init; } + public UInt256 BaseFeeMaxChangeDenominator { get; init; } + public long ElasticityMultiplier { get; init; } public Eip1559Spec(IEip1559Spec spec) { From 96675ab11e4a1b4ac79fb91586d215fc41f3b137 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 13:00:04 -0300 Subject: [PATCH 26/41] Use `nameof` --- .../Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs index d8b5fdb54d4..fd0c91a8d41 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs @@ -117,12 +117,12 @@ public override PayloadAttributesValidationResult Validate(ISpecProvider specPro IReleaseSpec releaseSpec = specProvider.GetSpec(ForkActivation.TimestampOnly(Timestamp)); if (!releaseSpec.IsOpHoloceneEnabled && EIP1559Params is not null) { - error = "EIP1559Params should be null before Holocene"; + error = $"{nameof(EIP1559Params)} should be null before Holocene"; return PayloadAttributesValidationResult.InvalidPayloadAttributes; } if (releaseSpec.IsOpHoloceneEnabled && EIP1559Params?.Length != 8) { - error = "EIP1559Params should be 8 bytes long"; + error = $"{nameof(EIP1559Params)} should be 8 bytes long"; return PayloadAttributesValidationResult.InvalidPayloadAttributes; } From 1405f1b9a363316bfc8c326d71746b67c46c096a Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 13:03:59 -0300 Subject: [PATCH 27/41] Remove nullability --- .../Nethermind.Optimism.Test/EIP1559ParametersTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs index a602e46323d..a6f99e50ff8 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -29,7 +29,7 @@ public void DecodeBlockHeaderParameters((string HexString, EIP1559Parameters Exp var bytes = Bytes.FromHexString(testCase.HexString); var blockHeader = Build.A.BlockHeader.WithExtraData(bytes).TestObject; - EIP1559Parameters? decoded = blockHeader.DecodeEIP1559Parameters(); + EIP1559Parameters decoded = blockHeader.DecodeEIP1559Parameters(); decoded.Should().Be(testCase.Expected); } From 6469275512bd74cdba63295d743c01e079e452e6 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 13:14:49 -0300 Subject: [PATCH 28/41] Add Holocene timestamp for Sepolia - Base and Optimism --- src/Nethermind/Chains/base-sepolia.json | 4 +++- src/Nethermind/Chains/op-sepolia.json | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Chains/base-sepolia.json b/src/Nethermind/Chains/base-sepolia.json index 1a682af75d0..d9ab7c57857 100644 --- a/src/Nethermind/Chains/base-sepolia.json +++ b/src/Nethermind/Chains/base-sepolia.json @@ -10,6 +10,7 @@ "ecotoneTimestamp": "0x65d62c10", "fjordTimestamp": "0x66575100", "graniteTimestamp": "0x66ba3180", + "holoceneTimestamp": "0x6745e270", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -64,9 +65,10 @@ "eip4844TransitionTimestamp": "0x65D62C10", "eip5656TransitionTimestamp": "0x65D62C10", "eip6780TransitionTimestamp": "0x65D62C10", - + "rip7212TransitionTimestamp": "0x66575100", "opGraniteTransitionTimestamp": "0x66ba3180", + "opHoloceneTransitionTimestamp": "0x6745e270", "terminalTotalDifficulty": "0" }, diff --git a/src/Nethermind/Chains/op-sepolia.json b/src/Nethermind/Chains/op-sepolia.json index 038e570bdb1..f59bd1f8f34 100644 --- a/src/Nethermind/Chains/op-sepolia.json +++ b/src/Nethermind/Chains/op-sepolia.json @@ -10,6 +10,7 @@ "ecotoneTimestamp": "0x65D62C10", "fjordTimestamp": "0x66575100", "graniteTimestamp": "0x66ba3180", + "holoceneTimestamp": "0x6745e270", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -64,6 +65,7 @@ "eip6780TransitionTimestamp": "0x65D62C10", "rip7212TransitionTimestamp": "0x66575100", "opGraniteTransitionTimestamp": "0x66ba3180", + "opHoloceneTransitionTimestamp": "0x6745e270", "terminalTotalDifficulty": "0" }, "genesis": { From 4603248af53f4290ccca54f1e72572a0df42fe3b Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 13:19:52 -0300 Subject: [PATCH 29/41] Add Holocene parameters --- src/Nethermind/Nethermind.Optimism/IOptimismSpecHelper.cs | 1 + src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs | 7 ++++++- .../OptimismChainSpecEngineParameters.cs | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Optimism/IOptimismSpecHelper.cs b/src/Nethermind/Nethermind.Optimism/IOptimismSpecHelper.cs index 5b994dab33d..021fc34371a 100644 --- a/src/Nethermind/Nethermind.Optimism/IOptimismSpecHelper.cs +++ b/src/Nethermind/Nethermind.Optimism/IOptimismSpecHelper.cs @@ -15,6 +15,7 @@ public interface IOptimismSpecHelper bool IsEcotone(BlockHeader header); bool IsFjord(BlockHeader header); bool IsGranite(BlockHeader header); + bool IsHolocene(BlockHeader header); Address? Create2DeployerAddress { get; } byte[]? Create2DeployerCode { get; } } diff --git a/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs b/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs index 251803fafaa..29ae4e12bb2 100644 --- a/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs +++ b/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LGPL-3.0-only using Nethermind.Core; -using Nethermind.Specs.ChainSpecStyle; namespace Nethermind.Optimism; @@ -14,6 +13,7 @@ public class OptimismSpecHelper(OptimismChainSpecEngineParameters parameters) : private readonly ulong? _ecotoneTimestamp = parameters.EcotoneTimestamp; private readonly ulong? _fjordTimestamp = parameters.FjordTimestamp; private readonly ulong? _graniteTimestamp = parameters.GraniteTimestamp; + private readonly ulong? _holoceneTimestamp = parameters.HoloceneTimestamp; public Address? L1FeeReceiver { get; init; } = parameters.L1FeeRecipient; @@ -47,6 +47,11 @@ public bool IsGranite(BlockHeader header) return header.Timestamp >= _graniteTimestamp; } + public bool IsHolocene(BlockHeader header) + { + return header.Timestamp >= _holoceneTimestamp; + } + public Address? Create2DeployerAddress { get; } = parameters.Create2DeployerAddress; public byte[]? Create2DeployerCode { get; } = parameters.Create2DeployerCode; } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs index 37f14bb91c1..43ee07a951c 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs @@ -27,6 +27,8 @@ public class OptimismChainSpecEngineParameters : IChainSpecEngineParameters public ulong? GraniteTimestamp { get; set; } + public ulong? HoloceneTimestamp { get; set; } + public Address? L1FeeRecipient { get; set; } public Address? L1BlockAddress { get; set; } @@ -45,12 +47,14 @@ public void AddTransitions(SortedSet blockNumbers, SortedSet timest ArgumentNullException.ThrowIfNull(EcotoneTimestamp); ArgumentNullException.ThrowIfNull(FjordTimestamp); ArgumentNullException.ThrowIfNull(GraniteTimestamp); + ArgumentNullException.ThrowIfNull(HoloceneTimestamp); blockNumbers.Add(BedrockBlockNumber.Value); timestamps.Add(RegolithTimestamp.Value); timestamps.Add(CanyonTimestamp.Value); timestamps.Add(EcotoneTimestamp.Value); timestamps.Add(FjordTimestamp.Value); timestamps.Add(GraniteTimestamp.Value); + timestamps.Add(HoloceneTimestamp.Value); } public void ApplyToReleaseSpec(ReleaseSpec spec, long startBlock, ulong? startTimestamp) From c7b37b52e4da28acbf223dd8c79e439165529f77 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 14 Nov 2024 16:40:42 -0300 Subject: [PATCH 30/41] Ensure `ExtraData.Length == 9` --- .../Nethermind.Optimism/OptimismEIP1559Parameters.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs index ee1d6a27576..2c9715ef036 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -41,8 +41,7 @@ public static class EIP1559ParametersExtensions { public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) { - if (header.ExtraData.Length < EIP1559Parameters.ByteLength) throw new ArgumentException($"{nameof(header.ExtraData)} data must be at least 9 bytes long"); - // TODO: Add check for `there is no additional data beyond these 9 bytes` (whatever that means): https://github.com/roberto-bayardo/op-geth/blob/6c32375dda12d3f0b8f3498404f00fe1ae872547/consensus/misc/eip1559/eip1559.go#L112-L114 + if (header.ExtraData.Length != EIP1559Parameters.ByteLength) throw new ArgumentException($"{nameof(header.ExtraData)} data must be {EIP1559Parameters.ByteLength} bytes long"); ReadOnlySpan extraData = header.ExtraData.AsSpan(); var version = extraData.TakeAndMove(1)[0]; From d9fd3e490a7b836c5ad1f9e3ab0df06c4a591d3c Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Fri, 15 Nov 2024 11:30:32 -0300 Subject: [PATCH 31/41] Add placeholder timestamp for mainnet --- src/Nethermind/Chains/base-mainnet.json | 4 +++- src/Nethermind/Chains/op-mainnet.json | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Chains/base-mainnet.json b/src/Nethermind/Chains/base-mainnet.json index 3098cc81864..2735f2b4680 100644 --- a/src/Nethermind/Chains/base-mainnet.json +++ b/src/Nethermind/Chains/base-mainnet.json @@ -10,6 +10,7 @@ "ecotoneTimestamp": "0x65f23e01", "fjordTimestamp": "0x668eb001", "graniteTimestamp": "0x66e1be81", + "holoceneTimestamp": "0xffffffff", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -64,9 +65,10 @@ "eip4844TransitionTimestamp": "0x65f23e01", "eip5656TransitionTimestamp": "0x65f23e01", "eip6780TransitionTimestamp": "0x65f23e01", - + "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", + "opHoloceneTransitionTimestamp": "0xffffffff", "terminalTotalDifficulty": "0" }, diff --git a/src/Nethermind/Chains/op-mainnet.json b/src/Nethermind/Chains/op-mainnet.json index 7f6a7765ef3..4ecf53a081c 100644 --- a/src/Nethermind/Chains/op-mainnet.json +++ b/src/Nethermind/Chains/op-mainnet.json @@ -10,6 +10,7 @@ "ecotoneTimestamp": "0x65f23e01", "fjordTimestamp": "0x668eb001", "graniteTimestamp": "0x66e1be81", + "holoceneTimestamp": "0xffffffff", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -72,6 +73,7 @@ "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", + "opHoloceneTransitionTimestamp": "0xffffffff", "terminalTotalDifficulty": "210470125" }, From 96e57e303af3dafa51f3999dca4ba77a273a8002 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 11:59:36 -0300 Subject: [PATCH 32/41] Perform single `IsZero` check --- .../OptimismBaseFeeCalculator.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs index 1d080e4e565..081a9714601 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -23,11 +23,17 @@ public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) { // NOTE: This operation should never fail since headers should be valid at this point EIP1559Parameters eip1559Params = parent.DecodeEIP1559Parameters(); - spec = new Eip1559Spec(specFor1559) - { - ElasticityMultiplier = eip1559Params.IsZero() ? Eip1559Constants.DefaultElasticityMultiplier : eip1559Params.Elasticity, - BaseFeeMaxChangeDenominator = eip1559Params.IsZero() ? Eip1559Constants.DefaultBaseFeeMaxChangeDenominator : eip1559Params.Denominator - }; + spec = eip1559Params.IsZero() + ? new Eip1559Spec(specFor1559) + { + ElasticityMultiplier = Eip1559Constants.DefaultElasticityMultiplier, + BaseFeeMaxChangeDenominator = Eip1559Constants.DefaultBaseFeeMaxChangeDenominator + } + : new Eip1559Spec(specFor1559) + { + ElasticityMultiplier = eip1559Params.Elasticity, + BaseFeeMaxChangeDenominator = eip1559Params.Denominator + }; } return baseFeeCalculator.Calculate(parent, spec); From b1ba8a447a2e019b3f4222115f920494074db028 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 11:59:57 -0300 Subject: [PATCH 33/41] Rename class --- src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs | 4 ++-- .../Nethermind.Optimism/OptimismBaseFeeCalculator.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index 17743e52b48..0f261995cdf 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -23,7 +23,7 @@ public interface IEip1559Spec public long ElasticityMultiplier { get; } } - public sealed class Eip1559Spec : IEip1559Spec + public sealed class OverridableEip1559Spec : IEip1559Spec { public bool IsEip1559Enabled { get; init; } public long Eip1559TransitionBlock { get; init; } @@ -33,7 +33,7 @@ public sealed class Eip1559Spec : IEip1559Spec public UInt256 BaseFeeMaxChangeDenominator { get; init; } public long ElasticityMultiplier { get; init; } - public Eip1559Spec(IEip1559Spec spec) + public OverridableEip1559Spec(IEip1559Spec spec) { IsEip1559Enabled = spec.IsEip1559Enabled; Eip1559TransitionBlock = spec.Eip1559TransitionBlock; diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs index 081a9714601..cade65b8e38 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -24,12 +24,12 @@ public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) // NOTE: This operation should never fail since headers should be valid at this point EIP1559Parameters eip1559Params = parent.DecodeEIP1559Parameters(); spec = eip1559Params.IsZero() - ? new Eip1559Spec(specFor1559) + ? new OverridableEip1559Spec(specFor1559) { ElasticityMultiplier = Eip1559Constants.DefaultElasticityMultiplier, BaseFeeMaxChangeDenominator = Eip1559Constants.DefaultBaseFeeMaxChangeDenominator } - : new Eip1559Spec(specFor1559) + : new OverridableEip1559Spec(specFor1559) { ElasticityMultiplier = eip1559Params.Elasticity, BaseFeeMaxChangeDenominator = eip1559Params.Denominator From 83e2f849107fd32676061a581b9bddbbc8c064fe Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 13:28:04 -0300 Subject: [PATCH 34/41] Prefer errors over Exceptions --- .../EIP1559ParametersTests.cs | 2 +- .../OptimismPayloadPreparationServiceTests.cs | 3 +- .../OptimismBaseFeeCalculator.cs | 9 +++- .../OptimismEIP1559Parameters.cs | 47 +++++++++++++++---- .../OptimismHeaderValidator.cs | 10 +--- .../OptimismPayloadPreparationService.cs | 6 ++- 6 files changed, 55 insertions(+), 22 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs index a6f99e50ff8..87720078024 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -29,7 +29,7 @@ public void DecodeBlockHeaderParameters((string HexString, EIP1559Parameters Exp var bytes = Bytes.FromHexString(testCase.HexString); var blockHeader = Build.A.BlockHeader.WithExtraData(bytes).TestObject; - EIP1559Parameters decoded = blockHeader.DecodeEIP1559Parameters(); + blockHeader.TryDecodeEIP1559Parameters(out EIP1559Parameters decoded, out _); decoded.Should().Be(testCase.Expected); } diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs index 7c3dd733ede..d2143f16b89 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs @@ -85,6 +85,7 @@ public async Task Writes_EIP1559Params_Into_HeaderExtraData(UInt32 denominator, var currentBestBlock = context?.CurrentBestBlock!; currentBestBlock.Should().Be(block); - currentBestBlock.Header.DecodeEIP1559Parameters().Should().BeEquivalentTo(new EIP1559Parameters(0, denominator, elasticity)); + currentBestBlock.Header.TryDecodeEIP1559Parameters(out var parameters, out _).Should().BeTrue(); + parameters.Should().BeEquivalentTo(new EIP1559Parameters(0, denominator, elasticity)); } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs index cade65b8e38..c8bf8487401 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Int256; @@ -21,8 +22,12 @@ public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) var releaseSpec = specProvider.GetSpec(parent); if (releaseSpec.IsOpHoloceneEnabled) { - // NOTE: This operation should never fail since headers should be valid at this point - EIP1559Parameters eip1559Params = parent.DecodeEIP1559Parameters(); + // NOTE: This operation should never fail since headers should be valid at this point. + if (!parent.TryDecodeEIP1559Parameters(out EIP1559Parameters eip1559Params, out _)) + { + throw new InvalidOperationException($"{nameof(BlockHeader)} was not properly validated: missing {nameof(EIP1559Parameters)}"); + } + spec = eip1559Params.IsZero() ? new OverridableEip1559Spec(specFor1559) { diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs index 2c9715ef036..e6171edeac5 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -3,6 +3,7 @@ using System; using System.Buffers.Binary; +using System.Diagnostics.CodeAnalysis; using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Optimism.Rpc; @@ -19,14 +20,32 @@ public readonly struct EIP1559Parameters public EIP1559Parameters(byte version, UInt32 denominator, UInt32 elasticity) { - if (version != 0) throw new ArgumentException($"{nameof(version)} must be 0", nameof(version)); - if (denominator == 0 && elasticity != 0) throw new ArgumentException($"{nameof(denominator)} cannot be 0 unless {nameof(elasticity)} is also 0", nameof(denominator)); - Version = version; Denominator = denominator; Elasticity = elasticity; } + public static bool TryCreate(byte version, UInt32 denominator, UInt32 elasticity, out EIP1559Parameters parameters, [NotNullWhen(false)] out string? error) + { + error = null; + parameters = default; + + if (version != 0) + { + error = $"{nameof(version)} must be 0"; + return false; + } + + if (denominator == 0 && elasticity != 0) + { + error = $"{nameof(denominator)} cannot be 0 unless {nameof(elasticity)} is also 0"; + return false; + } + + parameters = new EIP1559Parameters(version, denominator, elasticity); + return true; + } + public bool IsZero() => Denominator == 0 && Elasticity == 0; public void WriteTo(Span span) @@ -39,26 +58,36 @@ public void WriteTo(Span span) public static class EIP1559ParametersExtensions { - public static EIP1559Parameters DecodeEIP1559Parameters(this BlockHeader header) + public static bool TryDecodeEIP1559Parameters(this BlockHeader header, out EIP1559Parameters parameters, [NotNullWhen(false)] out string? error) { - if (header.ExtraData.Length != EIP1559Parameters.ByteLength) throw new ArgumentException($"{nameof(header.ExtraData)} data must be {EIP1559Parameters.ByteLength} bytes long"); + if (header.ExtraData.Length != EIP1559Parameters.ByteLength) + { + parameters = default; + error = $"{nameof(header.ExtraData)} data must be {EIP1559Parameters.ByteLength} bytes long"; + return false; + } ReadOnlySpan extraData = header.ExtraData.AsSpan(); var version = extraData.TakeAndMove(1)[0]; var denominator = BinaryPrimitives.ReadUInt32BigEndian(extraData.TakeAndMove(4)); var elasticity = BinaryPrimitives.ReadUInt32BigEndian(extraData.TakeAndMove(4)); - return new EIP1559Parameters(version, denominator, elasticity); + return EIP1559Parameters.TryCreate(version, denominator, elasticity, out parameters, out error); } - public static EIP1559Parameters DecodeEIP1559Parameters(this OptimismPayloadAttributes attributes) + public static bool TryDecodeEIP1559Parameters(this OptimismPayloadAttributes attributes, out EIP1559Parameters parameters, [NotNullWhen(false)] out string? error) { - if (attributes.EIP1559Params?.Length != 8) throw new ArgumentException($"{nameof(attributes.EIP1559Params)} must be 8 bytes long"); + if (attributes.EIP1559Params?.Length != 8) + { + parameters = default; + error = $"{nameof(attributes.EIP1559Params)} must be 8 bytes long"; + return false; + } ReadOnlySpan span = attributes.EIP1559Params.AsSpan(); var denominator = BinaryPrimitives.ReadUInt32BigEndian(span.TakeAndMove(4)); var elasticity = BinaryPrimitives.ReadUInt32BigEndian(span.TakeAndMove(4)); - return new EIP1559Parameters(0, denominator, elasticity); + return EIP1559Parameters.TryCreate(0, denominator, elasticity, out parameters, out error); } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs index 11812da9079..ed511db4933 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2023 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only -using System; using System.Diagnostics.CodeAnalysis; using Nethermind.Blockchain; using Nethermind.Consensus; @@ -42,14 +41,9 @@ public override bool Validate(BlockHeader header, BlockHeader? parent, bool isUn IReleaseSpec spec = _specProvider.GetSpec(header); if (spec.IsOpHoloceneEnabled) { - try + if (!header.TryDecodeEIP1559Parameters(out _, out var decodeError)) { - // TODO: We might want to avoid using exceptions here - _ = header.DecodeEIP1559Parameters(); - } - catch (ArgumentException e) - { - error = e.Message; + error = decodeError; return false; } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs index 967410636dd..fb712cd8d04 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs @@ -61,7 +61,11 @@ protected override void ImproveBlock(string payloadId, BlockHeader parentHeader, var spec = _specProvider.GetSpec(currentBestBlock.Header); if (spec.IsOpHoloceneEnabled) { - EIP1559Parameters eip1559Parameters = optimismPayload.DecodeEIP1559Parameters(); + if (!optimismPayload.TryDecodeEIP1559Parameters(out EIP1559Parameters eip1559Parameters, out var error)) + { + throw new InvalidOperationException($"{nameof(OptimismPayloadAttributes)} was not properly validated: invalid {nameof(OptimismPayloadAttributes.EIP1559Params)}"); + } + currentBestBlock.Header.ExtraData = new byte[EIP1559Parameters.ByteLength]; eip1559Parameters.WriteTo(currentBestBlock.Header.ExtraData); } From 1d101358732c7bc749d36e7ecb3634c2c4f5a138 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 16:35:13 -0300 Subject: [PATCH 35/41] Make `IBaseFeeCalculator` part of `IEip1559Spec` --- src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs | 3 +++ src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs | 1 + src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs | 1 + src/Nethermind/Nethermind.Specs/ReleaseSpec.cs | 1 + 4 files changed, 6 insertions(+) diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index 0f261995cdf..eac393ff9c2 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -21,6 +21,7 @@ public interface IEip1559Spec public UInt256 ForkBaseFee { get; } public UInt256 BaseFeeMaxChangeDenominator { get; } public long ElasticityMultiplier { get; } + public IBaseFeeCalculator BaseFeeCalculator { get; } } public sealed class OverridableEip1559Spec : IEip1559Spec @@ -32,6 +33,7 @@ public sealed class OverridableEip1559Spec : IEip1559Spec public UInt256 ForkBaseFee { get; init; } public UInt256 BaseFeeMaxChangeDenominator { get; init; } public long ElasticityMultiplier { get; init; } + public IBaseFeeCalculator BaseFeeCalculator { get; init; } public OverridableEip1559Spec(IEip1559Spec spec) { @@ -42,6 +44,7 @@ public OverridableEip1559Spec(IEip1559Spec spec) ForkBaseFee = spec.ForkBaseFee; BaseFeeMaxChangeDenominator = spec.BaseFeeMaxChangeDenominator; ElasticityMultiplier = spec.ElasticityMultiplier; + BaseFeeCalculator = spec.BaseFeeCalculator; } } } diff --git a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs index 4e214117a67..5688b8bdbf3 100644 --- a/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs +++ b/src/Nethermind/Nethermind.Core/Specs/ReleaseSpecDecorator.cs @@ -12,6 +12,7 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec public virtual UInt256 ForkBaseFee => spec.ForkBaseFee; public virtual UInt256 BaseFeeMaxChangeDenominator => spec.BaseFeeMaxChangeDenominator; public virtual long ElasticityMultiplier => spec.ElasticityMultiplier; + public virtual IBaseFeeCalculator BaseFeeCalculator => spec.BaseFeeCalculator; public virtual bool IsEip658Enabled => spec.IsEip658Enabled; public virtual string Name => spec.Name; public virtual long MaximumExtraDataSize => spec.MaximumExtraDataSize; diff --git a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs index 8aa58b89faa..1187f58d568 100644 --- a/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs.Test/OverridableReleaseSpec.cs @@ -176,6 +176,7 @@ public ulong Eip4844TransitionTimestamp public UInt256 ForkBaseFee => _spec.ForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator => _spec.BaseFeeMaxChangeDenominator; public long ElasticityMultiplier => _spec.ElasticityMultiplier; + public IBaseFeeCalculator BaseFeeCalculator => _spec.BaseFeeCalculator; public bool IsEip6110Enabled => _spec.IsEip6110Enabled; public Address DepositContractAddress => _spec.DepositContractAddress; } diff --git a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs index 0f18787b5f6..f01c3fa9f91 100644 --- a/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Specs/ReleaseSpec.cs @@ -75,6 +75,7 @@ public bool IsEip1559Enabled public UInt256 ForkBaseFee { get; set; } = Eip1559Constants.DefaultForkBaseFee; public UInt256 BaseFeeMaxChangeDenominator { get; set; } = Eip1559Constants.DefaultBaseFeeMaxChangeDenominator; public long ElasticityMultiplier { get; set; } = Eip1559Constants.DefaultElasticityMultiplier; + public IBaseFeeCalculator BaseFeeCalculator { get; set; } = new DefaultBaseFeeCalculator(); public bool IsEip1153Enabled { get; set; } public bool IsEip3651Enabled { get; set; } public bool IsEip3855Enabled { get; set; } From 2c77b7be5f5aaa59eaf2d1e9c6dae182ae08e44e Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 16:37:21 -0300 Subject: [PATCH 36/41] Use `IEip1559Spec.IBaseFeeCalculator` in singleton --- .../Nethermind.Core/BaseFeeCalculator.cs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs index aba13e4fb29..42b25af8cb7 100644 --- a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs @@ -63,19 +63,8 @@ public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) } } -/// -/// This class is a hack to support custom base fee calculations while still using a Singleton -/// Due to the extensive use of `BaseFeeCalculator.Calculate` in the codebase, it is not feasible to pass the calculator as a parameter. -/// Thus, for now we will use a Singleton pattern to allow for custom base fee calculations. -/// -/// When required, plugins can call to modify the global -/// public static class BaseFeeCalculator { - private static IBaseFeeCalculator _instance = new DefaultBaseFeeCalculator(); - - public static void Override(IBaseFeeCalculator calculator) => _instance = calculator; - public static void Reset() => _instance = new DefaultBaseFeeCalculator(); - - public static UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) => _instance!.Calculate(parent, specFor1559); + public static UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) => + specFor1559.BaseFeeCalculator.Calculate(parent, specFor1559); } From 18444bd56c7671cba14884800935dc9abb3725ef Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 16:39:26 -0300 Subject: [PATCH 37/41] Inject `IBaseFeeCalculator` through `IChainSpecEngineParameters` --- .../OptimismBaseFeeCalculatorTests.cs | 12 +++++------- .../Nethermind.Optimism/OptimismBaseFeeCalculator.cs | 8 ++++---- .../OptimismChainSpecEngineParameters.cs | 3 +++ src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs | 2 -- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs index d14ee290435..8672907ee05 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -8,7 +8,6 @@ using Nethermind.Core.Test.Builders; using Nethermind.Int256; using Nethermind.Specs; -using NSubstitute; using NUnit.Framework; namespace Nethermind.Optimism.Test; @@ -26,10 +25,13 @@ public class OptimismBaseFeeCalculatorTests [TestCase(30_000_000, 55_000_000, 2u, 10u)] // Above public void CalculatesBaseFee_AfterHolocene_UsingExtraDataParameters(long gasUsed, long expectedBaseFee, UInt32 denominator, UInt32 elasticity) { + const ulong HoloceneTimestamp = 10_000_000; + IReleaseSpec releaseSpec = new ReleaseSpec { IsEip1559Enabled = true, - IsOpHoloceneEnabled = true + IsOpHoloceneEnabled = true, + BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp, new DefaultBaseFeeCalculator()) }; var extraData = new byte[EIP1559Parameters.ByteLength]; @@ -39,16 +41,12 @@ public void CalculatesBaseFee_AfterHolocene_UsingExtraDataParameters(long gasUse BlockHeader blockHeader = Build.A.BlockHeader .WithGasLimit(30_000_000) .WithBaseFee(10_000_000) + .WithTimestamp(HoloceneTimestamp) .WithGasUsed(gasUsed) .WithExtraData(extraData) .TestObject; - var specProvider = Substitute.For(); - specProvider.GetSpec(blockHeader).Returns(releaseSpec); - - BaseFeeCalculator.Override(new OptimismBaseFeeCalculator(new DefaultBaseFeeCalculator(), specProvider)); UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); - BaseFeeCalculator.Reset(); actualBaseFee.Should().Be((UInt256)expectedBaseFee); } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs index c8bf8487401..cf1d0d652a2 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -12,15 +12,15 @@ namespace Nethermind.Optimism; /// See /// public sealed class OptimismBaseFeeCalculator( - IBaseFeeCalculator baseFeeCalculator, - ISpecProvider specProvider) : IBaseFeeCalculator + ulong holoceneTimestamp, + IBaseFeeCalculator baseFeeCalculator +) : IBaseFeeCalculator { public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) { var spec = specFor1559; - var releaseSpec = specProvider.GetSpec(parent); - if (releaseSpec.IsOpHoloceneEnabled) + if (parent.Timestamp >= holoceneTimestamp) { // NOTE: This operation should never fail since headers should be valid at this point. if (!parent.TryDecodeEIP1559Parameters(out EIP1559Parameters eip1559Params, out _)) diff --git a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs index 43ee07a951c..2c62250a437 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs @@ -60,9 +60,12 @@ public void AddTransitions(SortedSet blockNumbers, SortedSet timest public void ApplyToReleaseSpec(ReleaseSpec spec, long startBlock, ulong? startTimestamp) { ArgumentNullException.ThrowIfNull(CanyonBaseFeeChangeDenominator); + ArgumentNullException.ThrowIfNull(HoloceneTimestamp); + if (CanyonTimestamp <= startTimestamp) { spec.BaseFeeMaxChangeDenominator = CanyonBaseFeeChangeDenominator.Value; } + spec.BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp.Value, new DefaultBaseFeeCalculator()); } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index b47878fa312..5b53320b621 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -123,8 +123,6 @@ public Task Init(INethermindApi api) _api.SealValidator = NullSealEngine.Instance; _api.GossipPolicy = ShouldNotGossip.Instance; - BaseFeeCalculator.Override(new OptimismBaseFeeCalculator(new DefaultBaseFeeCalculator(), _api.SpecProvider)); - _api.BlockPreprocessor.AddFirst(new MergeProcessingRecoveryStep(_api.PoSSwitcher)); return Task.CompletedTask; From bdf4d23e0e07ea166f98615a998aa1a853b5f333 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 16:41:35 -0300 Subject: [PATCH 38/41] Make `HoloceneTimestamp` optional --- src/Nethermind/Chains/base-mainnet.json | 2 -- src/Nethermind/Chains/op-mainnet.json | 2 -- .../OptimismChainSpecEngineParameters.cs | 10 +++++----- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Nethermind/Chains/base-mainnet.json b/src/Nethermind/Chains/base-mainnet.json index 2735f2b4680..5b5a3bb3880 100644 --- a/src/Nethermind/Chains/base-mainnet.json +++ b/src/Nethermind/Chains/base-mainnet.json @@ -10,7 +10,6 @@ "ecotoneTimestamp": "0x65f23e01", "fjordTimestamp": "0x668eb001", "graniteTimestamp": "0x66e1be81", - "holoceneTimestamp": "0xffffffff", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -68,7 +67,6 @@ "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", - "opHoloceneTransitionTimestamp": "0xffffffff", "terminalTotalDifficulty": "0" }, diff --git a/src/Nethermind/Chains/op-mainnet.json b/src/Nethermind/Chains/op-mainnet.json index 4ecf53a081c..7f6a7765ef3 100644 --- a/src/Nethermind/Chains/op-mainnet.json +++ b/src/Nethermind/Chains/op-mainnet.json @@ -10,7 +10,6 @@ "ecotoneTimestamp": "0x65f23e01", "fjordTimestamp": "0x668eb001", "graniteTimestamp": "0x66e1be81", - "holoceneTimestamp": "0xffffffff", "l1FeeRecipient": "0x420000000000000000000000000000000000001A", "l1BlockAddress": "0x4200000000000000000000000000000000000015", "canyonBaseFeeChangeDenominator": "250", @@ -73,7 +72,6 @@ "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", - "opHoloceneTransitionTimestamp": "0xffffffff", "terminalTotalDifficulty": "210470125" }, diff --git a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs index 2c62250a437..6da2a0b4911 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs @@ -47,25 +47,25 @@ public void AddTransitions(SortedSet blockNumbers, SortedSet timest ArgumentNullException.ThrowIfNull(EcotoneTimestamp); ArgumentNullException.ThrowIfNull(FjordTimestamp); ArgumentNullException.ThrowIfNull(GraniteTimestamp); - ArgumentNullException.ThrowIfNull(HoloceneTimestamp); blockNumbers.Add(BedrockBlockNumber.Value); timestamps.Add(RegolithTimestamp.Value); timestamps.Add(CanyonTimestamp.Value); timestamps.Add(EcotoneTimestamp.Value); timestamps.Add(FjordTimestamp.Value); timestamps.Add(GraniteTimestamp.Value); - timestamps.Add(HoloceneTimestamp.Value); } public void ApplyToReleaseSpec(ReleaseSpec spec, long startBlock, ulong? startTimestamp) { ArgumentNullException.ThrowIfNull(CanyonBaseFeeChangeDenominator); - ArgumentNullException.ThrowIfNull(HoloceneTimestamp); - if (CanyonTimestamp <= startTimestamp) { spec.BaseFeeMaxChangeDenominator = CanyonBaseFeeChangeDenominator.Value; } - spec.BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp.Value, new DefaultBaseFeeCalculator()); + + if (HoloceneTimestamp is not null) + { + spec.BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp.Value, new DefaultBaseFeeCalculator()); + } } } From 377829ccc21359cf3b5f5a33ffaebed6e299a6c6 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 16:59:58 -0300 Subject: [PATCH 39/41] Fix substitutes --- .../Nethermind.Core.Test/BlockHeaderTests.cs | 1 + .../Modules/FeeHistoryOracleTests.cs | 13 ++++++++++++- .../Nethermind.Mining.Test/MinGasPriceTests.cs | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs index 849c25d3364..94209e9eb35 100644 --- a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs @@ -112,6 +112,7 @@ public void Eip_1559_CalculateBaseFee_should_returns_zero_when_eip1559_not_enabl public void Eip_1559_CalculateBaseFee(long gasTarget, long baseFee, long expectedBaseFee, long gasUsed, long? minimalBaseFee = null) { IReleaseSpec releaseSpec = Substitute.For(); + releaseSpec.BaseFeeCalculator.Returns(new DefaultBaseFeeCalculator()); releaseSpec.IsEip1559Enabled.Returns(true); releaseSpec.Eip1559BaseFeeMinValue.Returns((UInt256?)minimalBaseFee); releaseSpec.ForkBaseFee.Returns(Eip1559Constants.DefaultForkBaseFee); diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs index 230ea5a6dbd..051a3f6033f 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs @@ -444,10 +444,21 @@ private static FeeHistoryOracle GetSubstitutedFeeHistoryOracle( int? cacheSize = null, int? maxDistFromHead = null) { + ISpecProvider provider; + if (specProvider is not null) + { + provider = specProvider; + } + else + { + provider = Substitute.For(); + provider.GetSpec(Arg.Any()).BaseFeeCalculator.Returns(new DefaultBaseFeeCalculator()); + } + return new( blockTree ?? Substitute.For(), receiptStorage ?? Substitute.For(), - specProvider ?? Substitute.For(), + provider, maxDistFromHead); } } diff --git a/src/Nethermind/Nethermind.Mining.Test/MinGasPriceTests.cs b/src/Nethermind/Nethermind.Mining.Test/MinGasPriceTests.cs index fc464fbfa2d..d201af8816b 100644 --- a/src/Nethermind/Nethermind.Mining.Test/MinGasPriceTests.cs +++ b/src/Nethermind/Nethermind.Mining.Test/MinGasPriceTests.cs @@ -52,6 +52,7 @@ public void Test1559(long minimum, long maxFeePerGas, long maxPriorityFeePerGas, { ISpecProvider specProvider = Substitute.For(); specProvider.GetSpec(Arg.Any()).IsEip1559Enabled.Returns(true); + specProvider.GetSpec(Arg.Any()).BaseFeeCalculator.Returns(new DefaultBaseFeeCalculator()); specProvider.GetSpec(Arg.Any()).ForkBaseFee.Returns(Eip1559Constants.DefaultForkBaseFee); specProvider.GetSpec(Arg.Any()).BaseFeeMaxChangeDenominator.Returns(Eip1559Constants.DefaultBaseFeeMaxChangeDenominator); From 0792ec4f0ac800d54ae7ccd9eafaa9409fd2d994 Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Wed, 20 Nov 2024 17:18:35 -0300 Subject: [PATCH 40/41] Avoid whitespace diff --- src/Nethermind/Chains/base-mainnet.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nethermind/Chains/base-mainnet.json b/src/Nethermind/Chains/base-mainnet.json index 5b5a3bb3880..3098cc81864 100644 --- a/src/Nethermind/Chains/base-mainnet.json +++ b/src/Nethermind/Chains/base-mainnet.json @@ -64,7 +64,7 @@ "eip4844TransitionTimestamp": "0x65f23e01", "eip5656TransitionTimestamp": "0x65f23e01", "eip6780TransitionTimestamp": "0x65f23e01", - + "rip7212TransitionTimestamp": "0x668eb001", "opGraniteTransitionTimestamp": "0x66e1be81", From e3ad51ccec829e2193d897ad307deaaa78776bec Mon Sep 17 00:00:00 2001 From: Lautaro Emanuel Date: Thu, 21 Nov 2024 11:30:38 -0300 Subject: [PATCH 41/41] Extract constant --- .../Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs index fd0c91a8d41..6cc73e91920 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs @@ -35,6 +35,7 @@ public byte[][]? Transactions /// See /// public byte[]? EIP1559Params { get; set; } + private const int EIP1559ParamsLength = 8; private int TransactionsLength => Transactions?.Length ?? 0; @@ -120,9 +121,9 @@ public override PayloadAttributesValidationResult Validate(ISpecProvider specPro error = $"{nameof(EIP1559Params)} should be null before Holocene"; return PayloadAttributesValidationResult.InvalidPayloadAttributes; } - if (releaseSpec.IsOpHoloceneEnabled && EIP1559Params?.Length != 8) + if (releaseSpec.IsOpHoloceneEnabled && EIP1559Params?.Length != EIP1559ParamsLength) { - error = $"{nameof(EIP1559Params)} should be 8 bytes long"; + error = $"{nameof(EIP1559Params)} should be {EIP1559ParamsLength} bytes long"; return PayloadAttributesValidationResult.InvalidPayloadAttributes; }