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": { diff --git a/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs b/src/Nethermind/Nethermind.Consensus/Validators/HeaderValidator.cs index 58ae2a709b3..4664674f6bd 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.Core.Test/BlockHeaderTests.cs b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs index f6f5b24331f..0c8016d5f8d 100644 --- a/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/BlockHeaderTests.cs @@ -109,6 +109,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.Core/BaseFeeCalculator.cs b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs index 268997f6df9..42b25af8cb7 100644 --- a/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs +++ b/src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs @@ -4,55 +4,67 @@ using Nethermind.Core.Specs; using Nethermind.Int256; -namespace Nethermind.Core +namespace Nethermind.Core; + +public interface IBaseFeeCalculator { - /// Calculate BaseFee based on block parent and release spec. - public static class BaseFeeCalculator + UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559); +} + +/// +/// Calculate BaseFee based on parent and . +/// +public sealed class DefaultBaseFeeCalculator : IBaseFeeCalculator +{ + 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) + { + gasDelta = parent.GasUsed - parentGasTarget; + feeDelta = UInt256.Max( + parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator, + UInt256.One); + expectedBaseFee = parentBaseFee + feeDelta; + } + else { - 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 = parentGasTarget - parent.GasUsed; + feeDelta = parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator; + expectedBaseFee = UInt256.Max(parentBaseFee - feeDelta, 0); } - return expectedBaseFee; + if (isForkBlockNumber) + { + expectedBaseFee = specFor1559.ForkBaseFee; + } + + if (specFor1559.Eip1559BaseFeeMinValue.HasValue) + { + expectedBaseFee = UInt256.Max(expectedBaseFee, specFor1559.Eip1559BaseFeeMinValue.Value); + } } + + return expectedBaseFee; } } + +public static class BaseFeeCalculator +{ + public static UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) => + specFor1559.BaseFeeCalculator.Calculate(parent, specFor1559); +} diff --git a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs index 811a4510a63..eac393ff9c2 100644 --- a/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs +++ b/src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs @@ -21,5 +21,30 @@ public interface IEip1559Spec public UInt256 ForkBaseFee { get; } public UInt256 BaseFeeMaxChangeDenominator { get; } public long ElasticityMultiplier { get; } + public IBaseFeeCalculator BaseFeeCalculator { get; } + } + + public sealed class OverridableEip1559Spec : IEip1559Spec + { + 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 IBaseFeeCalculator BaseFeeCalculator { get; init; } + + public OverridableEip1559Spec(IEip1559Spec spec) + { + IsEip1559Enabled = spec.IsEip1559Enabled; + Eip1559TransitionBlock = spec.Eip1559TransitionBlock; + FeeCollector = spec.FeeCollector; + Eip1559BaseFeeMinValue = spec.Eip1559BaseFeeMinValue; + ForkBaseFee = spec.ForkBaseFee; + BaseFeeMaxChangeDenominator = spec.BaseFeeMaxChangeDenominator; + ElasticityMultiplier = spec.ElasticityMultiplier; + BaseFeeCalculator = spec.BaseFeeCalculator; + } } } 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..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; @@ -80,6 +81,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.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs index 9fe649a7b3c..62ac4aa497d 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/FeeHistoryOracleTests.cs @@ -443,10 +443,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.Merge.Plugin/MergeHeaderValidator.cs b/src/Nethermind/Nethermind.Merge.Plugin/MergeHeaderValidator.cs index 56b83fd49f5..ba2ffebab83 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) 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); diff --git a/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs new file mode 100644 index 00000000000..87720078024 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs @@ -0,0 +1,36 @@ +// 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; + +[Parallelizable(ParallelScope.All)] +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)); + 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; + + blockHeader.TryDecodeEIP1559Parameters(out EIP1559Parameters decoded, out _); + + decoded.Should().Be(testCase.Expected); + } +} diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs new file mode 100644 index 00000000000..8672907ee05 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismBaseFeeCalculatorTests.cs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Int256; +using Nethermind.Specs; +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) + { + const ulong HoloceneTimestamp = 10_000_000; + + IReleaseSpec releaseSpec = new ReleaseSpec + { + IsEip1559Enabled = true, + IsOpHoloceneEnabled = true, + BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp, new DefaultBaseFeeCalculator()) + }; + + var extraData = new byte[EIP1559Parameters.ByteLength]; + var parameters = new EIP1559Parameters(0, denominator, elasticity); + parameters.WriteTo(extraData); + + BlockHeader blockHeader = Build.A.BlockHeader + .WithGasLimit(30_000_000) + .WithBaseFee(10_000_000) + .WithTimestamp(HoloceneTimestamp) + .WithGasUsed(gasUsed) + .WithExtraData(extraData) + .TestObject; + + UInt256 actualBaseFee = BaseFeeCalculator.Calculate(blockHeader, releaseSpec); + + actualBaseFee.Should().Be((UInt256)expectedBaseFee); + } +} diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs new file mode 100644 index 00000000000..8a160f80032 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismHeaderValidatorTests.cs @@ -0,0 +1,106 @@ +// 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; + +[Parallelizable(ParallelScope.All)] +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); + 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(EIP1559ParametersExtraData))] + public void Validates_EIP1559Parameters_InExtraData_AfterHolocene((string HexString, bool IsValid) 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 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().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(); + } +} diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs new file mode 100644 index 00000000000..508c42b4b28 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadAttributesTests.cs @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +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; + +[Parallelizable(ParallelScope.All)] +public class OptimismPayloadAttributesTests +{ + 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, PayloadAttributesValidationResult)> Validate_EIP1559Params_Holocene_TestCases() + { + 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_Holocene_TestCases))] + public void Validate_EIP1559Params_Holocene((byte[]? Eip1559Params, PayloadAttributesValidationResult BeforeHolocene, PayloadAttributesValidationResult AfterHolocene) testCase) + { + var payloadAttributes = new OptimismPayloadAttributes + { + GasLimit = 1, + Transactions = [], + PrevRandao = Hash256.Zero, + SuggestedFeeRecipient = TestItem.AddressA, + EIP1559Params = testCase.Eip1559Params + }; + + 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 afterHolocene = payloadAttributes.Validate(BuildSpecProvider(isHolocene: true), 1, out var _); + afterHolocene.Should().Be(testCase.AfterHolocene); + } +} diff --git a/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs new file mode 100644 index 00000000000..d2143f16b89 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/OptimismPayloadPreparationServiceTests.cs @@ -0,0 +1,91 @@ +// 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; + +[Parallelizable(ParallelScope.All)] +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.TryDecodeEIP1559Parameters(out var parameters, out _).Should().BeTrue(); + parameters.Should().BeEquivalentTo(new EIP1559Parameters(0, denominator, elasticity)); + } +} 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 262304bdf20..29ae4e12bb2 100644 --- a/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs +++ b/src/Nethermind/Nethermind.Optimism/OPConfigHelper.cs @@ -13,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; @@ -46,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/OptimismBaseFeeCalculator.cs b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs new file mode 100644 index 00000000000..cf1d0d652a2 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/OptimismBaseFeeCalculator.cs @@ -0,0 +1,46 @@ +// 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; + +namespace Nethermind.Optimism; + +/// +/// See +/// +public sealed class OptimismBaseFeeCalculator( + ulong holoceneTimestamp, + IBaseFeeCalculator baseFeeCalculator +) : IBaseFeeCalculator +{ + public UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559) + { + var spec = specFor1559; + + 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 _)) + { + throw new InvalidOperationException($"{nameof(BlockHeader)} was not properly validated: missing {nameof(EIP1559Parameters)}"); + } + + spec = eip1559Params.IsZero() + ? new OverridableEip1559Spec(specFor1559) + { + ElasticityMultiplier = Eip1559Constants.DefaultElasticityMultiplier, + BaseFeeMaxChangeDenominator = Eip1559Constants.DefaultBaseFeeMaxChangeDenominator + } + : new OverridableEip1559Spec(specFor1559) + { + ElasticityMultiplier = eip1559Params.Elasticity, + BaseFeeMaxChangeDenominator = eip1559Params.Denominator + }; + } + + return baseFeeCalculator.Calculate(parent, spec); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs index abc4ed95aa0..6da2a0b4911 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismChainSpecEngineParameters.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; using Nethermind.Core; using Nethermind.Int256; using Nethermind.Specs; @@ -26,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; } @@ -36,6 +39,22 @@ public class OptimismChainSpecEngineParameters : IChainSpecEngineParameters public byte[]? Create2DeployerCode { get; set; } + public void AddTransitions(SortedSet blockNumbers, SortedSet timestamps) + { + ArgumentNullException.ThrowIfNull(BedrockBlockNumber); + ArgumentNullException.ThrowIfNull(RegolithTimestamp); + ArgumentNullException.ThrowIfNull(CanyonTimestamp); + ArgumentNullException.ThrowIfNull(EcotoneTimestamp); + ArgumentNullException.ThrowIfNull(FjordTimestamp); + ArgumentNullException.ThrowIfNull(GraniteTimestamp); + blockNumbers.Add(BedrockBlockNumber.Value); + timestamps.Add(RegolithTimestamp.Value); + timestamps.Add(CanyonTimestamp.Value); + timestamps.Add(EcotoneTimestamp.Value); + timestamps.Add(FjordTimestamp.Value); + timestamps.Add(GraniteTimestamp.Value); + } + public void ApplyToReleaseSpec(ReleaseSpec spec, long startBlock, ulong? startTimestamp) { ArgumentNullException.ThrowIfNull(CanyonBaseFeeChangeDenominator); @@ -43,5 +62,10 @@ public void ApplyToReleaseSpec(ReleaseSpec spec, long startBlock, ulong? startTi { spec.BaseFeeMaxChangeDenominator = CanyonBaseFeeChangeDenominator.Value; } + + if (HoloceneTimestamp is not null) + { + spec.BaseFeeCalculator = new OptimismBaseFeeCalculator(HoloceneTimestamp.Value, new DefaultBaseFeeCalculator()); + } } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs new file mode 100644 index 00000000000..e6171edeac5 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/OptimismEIP1559Parameters.cs @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Buffers.Binary; +using System.Diagnostics.CodeAnalysis; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Optimism.Rpc; + +namespace Nethermind.Optimism; + +public readonly struct EIP1559Parameters +{ + public const int ByteLength = 9; + + public byte Version { get; } + public UInt32 Denominator { get; } + public UInt32 Elasticity { get; } + + public EIP1559Parameters(byte version, UInt32 denominator, UInt32 elasticity) + { + 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) + { + span[0] = Version; + BinaryPrimitives.WriteUInt32BigEndian(span.Slice(1, 4), Denominator); + BinaryPrimitives.WriteUInt32BigEndian(span.Slice(5, 4), Elasticity); + } +} + +public static class EIP1559ParametersExtensions +{ + public static bool TryDecodeEIP1559Parameters(this BlockHeader header, out EIP1559Parameters parameters, [NotNullWhen(false)] out string? error) + { + 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 EIP1559Parameters.TryCreate(version, denominator, elasticity, out parameters, out error); + } + + public static bool TryDecodeEIP1559Parameters(this OptimismPayloadAttributes attributes, out EIP1559Parameters parameters, [NotNullWhen(false)] out string? error) + { + 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 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 e907458a957..ed511db4933 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismHeaderValidator.cs @@ -36,5 +36,20 @@ 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) + { + if (!header.TryDecodeEIP1559Parameters(out _, out var decodeError)) + { + error = decodeError; + return false; + } + } + + return base.Validate(header, parent, isUncle, out error); + } + protected override bool ValidateGasLimitRange(BlockHeader header, BlockHeader parent, IReleaseSpec spec, ref string? error) => true; } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs b/src/Nethermind/Nethermind.Optimism/OptimismPayloadPreparationService.cs index 35fd2e3abb2..fb712cd8d04 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,24 @@ 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) + { + 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); + } + } + + base.ImproveBlock(payloadId, parentHeader, payloadAttributes, currentBestBlock, startDateTime); + } } } diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index 8e9c37af52a..5b53320b621 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -215,6 +215,7 @@ public async Task InitRpcModules() TimeSpan.FromSeconds(_blocksConfig.SecondsPerSlot)); OptimismPayloadPreparationService payloadPreparationService = new( + _api.SpecProvider, (PostMergeBlockProducer)_api.BlockProducer, improvementContextFactory, _api.TimerFactory, diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs index 8db3156fcae..6cc73e91920 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismPayloadAttributes.cs @@ -30,6 +30,13 @@ 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 const int EIP1559ParamsLength = 8; + private int TransactionsLength => Transactions?.Length ?? 0; private Transaction[]? _transactions; @@ -64,8 +71,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 +97,12 @@ protected override int WritePayloadIdMembers(BlockHeader parentHeader, Span _spec.IsEip4844Enabled; public bool IsRip7212Enabled => _spec.IsRip7212Enabled; public bool IsOpGraniteEnabled => _spec.IsOpGraniteEnabled; + public bool IsOpHoloceneEnabled => _spec.IsOpHoloceneEnabled; private bool? _isOntakeEnabled; public bool IsOntakeEnabled @@ -175,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/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..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; } @@ -83,6 +84,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; }