Skip to content

eip-7883 implementation #8489

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

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.Core/Specs/IReleaseSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ public interface IReleaseSpec : IEip1559Spec, IReceiptSpec
/// </summary>
bool IsEip7623Enabled { get; }

/// <summary>
/// Increase ModExp Gas Cost
/// </summary>
bool IsEip7883Enabled { get; }

/// <summary>
/// Should transactions be validated against chainId.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public class ReleaseSpecDecorator(IReleaseSpec spec) : IReleaseSpec
public virtual bool IsOpGraniteEnabled => spec.IsOpGraniteEnabled;
public virtual bool IsOpHoloceneEnabled => spec.IsOpHoloceneEnabled;
public virtual bool IsEip7623Enabled => spec.IsEip7623Enabled;
public bool IsEip7883Enabled => spec.IsEip7883Enabled;
public virtual ulong WithdrawalTimestamp => spec.WithdrawalTimestamp;
public virtual ulong Eip4844TransitionTimestamp => spec.Eip4844TransitionTimestamp;
public virtual bool IsEip158IgnoredAccount(Address address) => spec.IsEip158IgnoredAccount(address);
Expand Down
96 changes: 96 additions & 0 deletions src/Nethermind/Nethermind.Evm.Test/Eip7883Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using FluentAssertions;
using Nethermind.Core.Specs;
using Nethermind.Evm.Precompiles;
using Nethermind.Int256;
using Nethermind.Specs;
using NUnit.Framework;

namespace Nethermind.Evm.Test;

public class Eip7883Tests
{
[Test]
public void DataGasCost([ValueSource(nameof(Eip7883TestCases))] Eip7883TestCase test)
{
var inputData = PrepareInput(test.BaseLength, test.ExpLength, test.ModulusLength);

long gas = ModExpPrecompile.Instance.DataGasCost(inputData, test.Spec);
gas.Should().Be(test.Result);
}

public class Eip7883TestCase
{
public IReleaseSpec Spec { get; set; }
public UInt256 BaseLength { get; set; }
public UInt256 ExpLength { get; set; }
public UInt256 ModulusLength { get; set; }
public long Result { get; set; }
}

private static readonly IReleaseSpec SpecEipEnabled = new ReleaseSpec
{
IsEip7883Enabled = true,
IsEip2565Enabled = true,
};

private static readonly IReleaseSpec SpecEipDisabled = new ReleaseSpec
{
IsEip7883Enabled = false,
IsEip2565Enabled = true,
};

private static IEnumerable<Eip7883TestCase> Eip7883TestCases()
{
// eip enabled test cases
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 32, ExpLength = 32, ModulusLength = 32, Result = 500L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 32, ExpLength = 32, ModulusLength = 10000, Result = 1041666L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 32, ExpLength = 10000, ModulusLength = 32, Result = 850602L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 10000, ExpLength = 32, ModulusLength = 32, Result = 1041666L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 32, ExpLength = 10000, ModulusLength = 10000, Result = 166133333333L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 10000, ExpLength = 10000, ModulusLength = 32, Result = 166133333333L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 10000, ExpLength = 32, ModulusLength = 10000, Result = 1041666L };
yield return new Eip7883TestCase
{ Spec = SpecEipEnabled, BaseLength = 10000, ExpLength = 10000, ModulusLength = 10000, Result = 166133333333L };

// eip disabled test cases
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 32, ExpLength = 32, ModulusLength = 32, Result = 200L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 32, ExpLength = 32, ModulusLength = 10000, Result = 520833L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 32, ExpLength = 10000, ModulusLength = 32, Result = 425301L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 10000, ExpLength = 32, ModulusLength = 32, Result = 520833L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 32, ExpLength = 10000, ModulusLength = 10000, Result = 41533333333L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 10000, ExpLength = 10000, ModulusLength = 32, Result = 41533333333L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 10000, ExpLength = 32, ModulusLength = 10000, Result = 520833L };
yield return new Eip7883TestCase
{ Spec = SpecEipDisabled, BaseLength = 10000, ExpLength = 10000, ModulusLength = 10000, Result = 41533333333L };
}

private static ReadOnlyMemory<byte> PrepareInput(UInt256 baseLength, UInt256 expLength, UInt256 modulusLength)
{
var inputBytes = new byte[96];

Array.Copy(baseLength.ToBigEndian(), 0, inputBytes, 0, 32);
Array.Copy(expLength.ToBigEndian(), 0, inputBytes, 32, 32);
Array.Copy(modulusLength.ToBigEndian(), 0, inputBytes, 64, 32);

return new ReadOnlyMemory<byte>(inputBytes);
}
}
16 changes: 9 additions & 7 deletions src/Nethermind/Nethermind.Evm/Precompiles/ModExpPrecompile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec
UInt256 expLength = new(extendedInput.Slice(32, 32), true);
UInt256 modulusLength = new(extendedInput.Slice(64, 32), true);

UInt256 complexity = MultComplexity(baseLength, modulusLength);
UInt256 complexity = MultComplexity(baseLength, modulusLength, releaseSpec.IsEip7883Enabled);

UInt256 expLengthUpTo32 = UInt256.Min(32, expLength);
UInt256 startIndex = 96 + baseLength; //+ expLength - expLengthUpTo32; // Geth takes head here, why?
UInt256 exp = new(inputData.Span.SliceWithZeroPaddingEmptyOnError((int)startIndex, (int)expLengthUpTo32), true);
UInt256 iterationCount = CalculateIterationCount(expLength, exp);
UInt256 iterationCount = CalculateIterationCount(expLength, exp, releaseSpec.IsEip7883Enabled);
bool overflow = UInt256.MultiplyOverflow(complexity, iterationCount, out UInt256 result);
result /= 3;
return result > long.MaxValue || overflow ? long.MaxValue : Math.Max(200L, (long)result);
return result > long.MaxValue || overflow ? long.MaxValue : Math.Max(releaseSpec.IsEip7883Enabled ? 500L : 200L, (long)result);
Copy link
Contributor

Choose a reason for hiding this comment

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

should we create constants in GasCostOf for these values?

}
catch (OverflowException)
{
Expand Down Expand Up @@ -173,12 +173,12 @@ public static (byte[], bool) OldRun(byte[] inputData)
/// return words**2
/// </summary>
/// <returns></returns>
private static UInt256 MultComplexity(in UInt256 baseLength, in UInt256 modulusLength)
private static UInt256 MultComplexity(in UInt256 baseLength, in UInt256 modulusLength, bool isEip7883Enabled)
{
UInt256 maxLength = UInt256.Max(baseLength, modulusLength);
UInt256.Mod(maxLength, 8, out UInt256 mod8);
UInt256 words = (maxLength / 8) + ((mod8.IsZero) ? UInt256.Zero : UInt256.One);
return words * words;
return maxLength > 32 && isEip7883Enabled ? 2 * words * words : words * words;
Copy link
Contributor

Choose a reason for hiding this comment

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

should we create constants in GasCostOf for these values? (the 2x multiplier)

}

/// <summary>
Expand All @@ -191,8 +191,9 @@ private static UInt256 MultComplexity(in UInt256 baseLength, in UInt256 modulusL
/// </summary>
/// <param name="exponentLength"></param>
/// <param name="exponent"></param>
/// <param name="isEip7883Enabled"></param>
/// <returns></returns>
private static UInt256 CalculateIterationCount(UInt256 exponentLength, UInt256 exponent)
private static UInt256 CalculateIterationCount(UInt256 exponentLength, UInt256 exponent, bool isEip7883Enabled)
{
try
{
Expand All @@ -209,7 +210,8 @@ private static UInt256 CalculateIterationCount(UInt256 exponentLength, UInt256 e
bitLength--;
}

bool overflow = UInt256.MultiplyOverflow((exponentLength - 32), 8, out UInt256 multiplicationResult);
var multiplier = (UInt256)(isEip7883Enabled ? 16 : 8);
Copy link
Contributor

Choose a reason for hiding this comment

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

should we create constants in GasCostOf for these values?

bool overflow = UInt256.MultiplyOverflow(exponentLength - 32, multiplier, out UInt256 multiplicationResult);
overflow |= UInt256.AddOverflow(multiplicationResult, (UInt256)bitLength, out iterationCount);
if (overflow)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public UInt256 BlockReward

public bool IsEip7623Enabled => _spec.IsEip7623Enabled;

public bool IsEip7883Enabled => _spec.IsEip7883Enabled;

public bool IsEip3607Enabled { get; set; }

public bool IsEip158IgnoredAccount(Address address) => _spec.IsEip158IgnoredAccount(address);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public class ChainParameters
public ulong? OpGraniteTransitionTimestamp { get; set; }
public ulong? OpHoloceneTransitionTimestamp { get; set; }
public ulong? Eip7623TransitionTimestamp { get; set; }
public ulong? Eip7883TransitionTimestamp { get; set; }

public Dictionary<string, ChainSpecBlobCountJson> BlobSchedule { get; set; } = [];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ protected virtual ReleaseSpec CreateReleaseSpec(ChainSpec chainSpec, long releas
releaseSpec.IsEip7251Enabled = (chainSpec.Parameters.Eip7251TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp;
releaseSpec.Eip7251ContractAddress = chainSpec.Parameters.Eip7251ContractAddress;
releaseSpec.IsEip7623Enabled = (chainSpec.Parameters.Eip7623TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp;
releaseSpec.IsEip7883Enabled = (chainSpec.Parameters.Eip7883TransitionTimestamp ?? ulong.MaxValue) <= releaseStartTimestamp;

bool eip1559FeeCollector = releaseSpec.IsEip1559Enabled && (chainSpec.Parameters.Eip1559FeeCollectorTransition ?? long.MaxValue) <= releaseStartBlock;
bool eip4844FeeCollector = releaseSpec.IsEip4844Enabled && (chainSpec.Parameters.Eip4844FeeCollectorTransitionTimestamp ?? long.MaxValue) <= releaseStartTimestamp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ bool GetForInnerPathExistence(KeyValuePair<string, JsonElement> o) =>
() => chainSpecJson.Params.ChainId == BlockchainIds.Mainnet ? Eip6110Constants.MainnetDepositContractAddress : null),
Eip7002TransitionTimestamp = chainSpecJson.Params.Eip7002TransitionTimestamp,
Eip7623TransitionTimestamp = chainSpecJson.Params.Eip7623TransitionTimestamp,
Eip7883TransitionTimestamp = chainSpecJson.Params.Eip7883TransitionTimestamp,
Eip7002ContractAddress = chainSpecJson.Params.Eip7002ContractAddress ?? Eip7002Constants.WithdrawalRequestPredeployAddress,
Eip7251TransitionTimestamp = chainSpecJson.Params.Eip7251TransitionTimestamp,
Eip7251ContractAddress = chainSpecJson.Params.Eip7251ContractAddress ?? Eip7251Constants.ConsolidationRequestPredeployAddress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ internal class ChainSpecParamsJson
public Address Eip7251ContractAddress { get; set; }
public ulong? Rip7212TransitionTimestamp { get; set; }
public ulong? Eip7702TransitionTimestamp { get; set; }
public ulong? Eip7883TransitionTimestamp { get; set; }
public ulong? OpGraniteTransitionTimestamp { get; set; }
public ulong? OpHoloceneTransitionTimestamp { get; set; }
public Dictionary<string, ChainSpecBlobCountJson> BlobSchedule { get; set; } = [];
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Specs/ReleaseSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public bool IsEip1559Enabled
public bool IsOpGraniteEnabled { get; set; }
public bool IsOpHoloceneEnabled { get; set; }
public bool IsEip7623Enabled { get; set; }
public bool IsEip7883Enabled { get; set; }
public bool IsEip5656Enabled { get; set; }
public bool IsEip6780Enabled { get; set; }
public bool IsEip4788Enabled { get; set; }
Expand Down