Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add OP Holocene fork #7761

Merged
merged 46 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
6f0395e
Perform single dependency capture
emlautarom1 Nov 7, 2024
fda85b9
Add Holocene to ReleaseSpec
emlautarom1 Nov 7, 2024
32ee4f7
Decode EIP1559 parameters from BlockHeader
emlautarom1 Nov 7, 2024
5a8e70c
Validate ExtraData fields post-Holocene
emlautarom1 Nov 7, 2024
c8aa891
Collapse test cases
emlautarom1 Nov 7, 2024
e0f5d5e
Test for pre-Holocene ExtraData validation
emlautarom1 Nov 7, 2024
d9a5c12
Extract to interface
emlautarom1 Nov 8, 2024
35d6ef0
Allow for "zero" EIP1559 parameters
emlautarom1 Nov 8, 2024
412fdbc
Add concrete `Eip1559Spec : IEip1559Spec`
emlautarom1 Nov 11, 2024
315cc23
Add helper methods
emlautarom1 Nov 11, 2024
0ce5908
Add OptimismBaseFeeCalculator
emlautarom1 Nov 11, 2024
22fc7ad
Remove unused using
emlautarom1 Nov 11, 2024
3d14ce3
Add `EIP1559Params` to `OptimismPayloadAttributes`
emlautarom1 Nov 11, 2024
a66b367
Rename file
emlautarom1 Nov 12, 2024
defe8f7
Use correct version
emlautarom1 Nov 13, 2024
ecabab6
Decode EIP1559 parameters from `OptimismPayloadAttributes`
emlautarom1 Nov 13, 2024
b7db888
Include EIP1559 params from attributes into block header
emlautarom1 Nov 13, 2024
aef8a7c
Extract required length to constant
emlautarom1 Nov 13, 2024
67c6577
Remove unused constructor
emlautarom1 Nov 13, 2024
551976c
Test payload validation before Holocene
emlautarom1 Nov 13, 2024
53b6f8e
Reintroduce `BaseFeeCalculator` singleton
emlautarom1 Nov 13, 2024
ae5c231
Fix wrong test name
emlautarom1 Nov 13, 2024
97831af
Mark tests parallelizable
emlautarom1 Nov 13, 2024
44bbeeb
Override `BaseFeeCalculator` in OP
emlautarom1 Nov 13, 2024
db743b6
Use init only properties
emlautarom1 Nov 14, 2024
96675ab
Use `nameof`
emlautarom1 Nov 14, 2024
1405f1b
Remove nullability
emlautarom1 Nov 14, 2024
6469275
Add Holocene timestamp for Sepolia
emlautarom1 Nov 14, 2024
4603248
Add Holocene parameters
emlautarom1 Nov 14, 2024
fcd733d
Merge branch 'master' into feat/optimism-holocene
emlautarom1 Nov 14, 2024
c7b37b5
Ensure `ExtraData.Length == 9`
emlautarom1 Nov 14, 2024
d9fd3e4
Add placeholder timestamp for mainnet
emlautarom1 Nov 15, 2024
4013c7a
Merge branch 'master' into feat/optimism-holocene
benaadams Nov 19, 2024
96e57e3
Perform single `IsZero` check
emlautarom1 Nov 20, 2024
b1ba8a4
Rename class
emlautarom1 Nov 20, 2024
83e2f84
Prefer errors over Exceptions
emlautarom1 Nov 20, 2024
1d10135
Make `IBaseFeeCalculator` part of `IEip1559Spec`
emlautarom1 Nov 20, 2024
2c77b7b
Use `IEip1559Spec.IBaseFeeCalculator` in singleton
emlautarom1 Nov 20, 2024
18444bd
Inject `IBaseFeeCalculator` through `IChainSpecEngineParameters`
emlautarom1 Nov 20, 2024
bdf4d23
Make `HoloceneTimestamp` optional
emlautarom1 Nov 20, 2024
377829c
Fix substitutes
emlautarom1 Nov 20, 2024
0792ec4
Avoid whitespace diff
emlautarom1 Nov 20, 2024
9ea6945
Merge branch 'master' into feat/optimism-holocene
emlautarom1 Nov 20, 2024
e3ad51c
Extract constant
emlautarom1 Nov 21, 2024
5ff8dff
Merge branch 'master' into feat/optimism-holocene
emlautarom1 Nov 21, 2024
55e1fb6
Merge branch 'master' into feat/optimism-holocene
emlautarom1 Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/Nethermind/Chains/base-sepolia.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"ecotoneTimestamp": "0x65d62c10",
"fjordTimestamp": "0x66575100",
"graniteTimestamp": "0x66ba3180",
"holoceneTimestamp": "0x6745e270",
"l1FeeRecipient": "0x420000000000000000000000000000000000001A",
"l1BlockAddress": "0x4200000000000000000000000000000000000015",
"canyonBaseFeeChangeDenominator": "250",
Expand Down Expand Up @@ -64,9 +65,10 @@
"eip4844TransitionTimestamp": "0x65D62C10",
"eip5656TransitionTimestamp": "0x65D62C10",
"eip6780TransitionTimestamp": "0x65D62C10",

"rip7212TransitionTimestamp": "0x66575100",
"opGraniteTransitionTimestamp": "0x66ba3180",
"opHoloceneTransitionTimestamp": "0x6745e270",

"terminalTotalDifficulty": "0"
},
Expand Down
2 changes: 2 additions & 0 deletions src/Nethermind/Chains/op-sepolia.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"ecotoneTimestamp": "0x65D62C10",
"fjordTimestamp": "0x66575100",
"graniteTimestamp": "0x66ba3180",
"holoceneTimestamp": "0x6745e270",
"l1FeeRecipient": "0x420000000000000000000000000000000000001A",
"l1BlockAddress": "0x4200000000000000000000000000000000000015",
"canyonBaseFeeChangeDenominator": "250",
Expand Down Expand Up @@ -64,6 +65,7 @@
"eip6780TransitionTimestamp": "0x65D62C10",
"rip7212TransitionTimestamp": "0x66575100",
"opGraniteTransitionTimestamp": "0x66ba3180",
"opHoloceneTransitionTimestamp": "0x6745e270",
"terminalTotalDifficulty": "0"
},
"genesis": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
109 changes: 66 additions & 43 deletions src/Nethermind/Nethermind.Core/BaseFeeCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,78 @@
using Nethermind.Core.Specs;
using Nethermind.Int256;

namespace Nethermind.Core
namespace Nethermind.Core;

public interface IBaseFeeCalculator
{
/// <summary>Calculate BaseFee based on block parent and release spec.</summary>
public static class BaseFeeCalculator
UInt256 Calculate(BlockHeader parent, IEip1559Spec specFor1559);
}

/// <summary>
/// Calculate <c>BaseFee</c> based on parent <see cref="BlockHeader"/> and <see cref="IEip1559Spec"/>.
/// </summary>
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
{
gasDelta = parentGasTarget - parent.GasUsed;
feeDelta = parentBaseFee * (UInt256)gasDelta / (UInt256)parentGasTarget / specFor1559.BaseFeeMaxChangeDenominator;
expectedBaseFee = UInt256.Max(parentBaseFee - feeDelta, 0);
}

if (isForkBlockNumber)
{
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);
}
expectedBaseFee = specFor1559.ForkBaseFee;
}

return expectedBaseFee;
if (specFor1559.Eip1559BaseFeeMinValue.HasValue)
{
expectedBaseFee = UInt256.Max(expectedBaseFee, specFor1559.Eip1559BaseFeeMinValue.Value);
}
}

return expectedBaseFee;
}
}

/// <remarks>
/// 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 <see cref="Override"/> to modify the global <see cref="IBaseFeeCalculator"/>
/// </remarks>
public static class BaseFeeCalculator
emlautarom1 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we could remove this static class altogether by leveraging extension methods but for now I'll leave it as it is to avoid more diffs.

{
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);
}
22 changes: 22 additions & 0 deletions src/Nethermind/Nethermind.Core/Specs/IEip1559Spec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,26 @@ public interface IEip1559Spec
public UInt256 BaseFeeMaxChangeDenominator { get; }
public long ElasticityMultiplier { get; }
}

public sealed class Eip1559Spec : IEip1559Spec
emlautarom1 marked this conversation as resolved.
Show resolved Hide resolved
{
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)
{
IsEip1559Enabled = spec.IsEip1559Enabled;
Eip1559TransitionBlock = spec.Eip1559TransitionBlock;
FeeCollector = spec.FeeCollector;
Eip1559BaseFeeMinValue = spec.Eip1559BaseFeeMinValue;
ForkBaseFee = spec.ForkBaseFee;
BaseFeeMaxChangeDenominator = spec.BaseFeeMaxChangeDenominator;
ElasticityMultiplier = spec.ElasticityMultiplier;
}
}
}
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec
/// OP Granite
bool IsOpGraniteEnabled { get; }

/// OP Holocene
bool IsOpHoloceneEnabled { get; }

/// Taiko Ontake
bool IsOntakeEnabled { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down
36 changes: 36 additions & 0 deletions src/Nethermind/Nethermind.Optimism.Test/EIP1559ParametersTests.cs
Original file line number Diff line number Diff line change
@@ -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;

EIP1559Parameters decoded = blockHeader.DecodeEIP1559Parameters();

decoded.Should().Be(testCase.Expected);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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 NSubstitute;
using NUnit.Framework;

namespace Nethermind.Optimism.Test;

public class OptimismBaseFeeCalculatorTests
{
/// <remarks>
/// Tests sourced from <see href="https://github.com/ethereum-optimism/op-geth/blob/1e60ba82d31bc17111481998100cd948ee06c0ab/consensus/misc/eip1559/eip1559_test.go#L191"/>
/// </remarks>
[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[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)
.WithGasUsed(gasUsed)
.WithExtraData(extraData)
.TestObject;

var specProvider = Substitute.For<ISpecProvider>();
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);
}
}
Loading
Loading