Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/Nethermind/Benchmarks.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<Project Path="Nethermind.EthStats/Nethermind.EthStats.csproj" />
<Project Path="Nethermind.Evm.Test/Nethermind.Evm.Test.csproj" />
<Project Path="Nethermind.Evm/Nethermind.Evm.csproj" />
<Project Path="Nethermind.Evm.Precompiles/Nethermind.Evm.Precompiles.csproj" />
<Project Path="Nethermind.Facade/Nethermind.Facade.csproj" />
<Project Path="Nethermind.Grpc/Nethermind.Grpc.csproj" />
<Project Path="Nethermind.Init\Nethermind.Init.csproj" />
Expand Down
15 changes: 10 additions & 5 deletions src/Nethermind/Nethermind.Core/Extensions/ByteArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,23 @@ public static byte[] SliceWithZeroPaddingEmptyOnError(this byte[] bytes, int sta
return slice;
}

public static byte[] SliceWithZeroPaddingEmptyOnError(this ReadOnlySpan<byte> bytes, int startIndex, int length)
public static ReadOnlySpan<byte> SliceWithZeroPaddingEmptyOnError(this ReadOnlySpan<byte> bytes, int startIndex, int length)
{
int copiedFragmentLength = Math.Min(bytes.Length - startIndex, length);
if (copiedFragmentLength <= 0)
{
return [];
return default;
}

byte[] slice = new byte[length];
ReadOnlySpan<byte> sliced = bytes.Slice(startIndex, copiedFragmentLength);
if (copiedFragmentLength < length)
{
byte[] extended = new byte[length];
sliced.CopyTo(extended);
sliced = extended;
}

bytes.Slice(startIndex, copiedFragmentLength).CopyTo(slice.AsSpan(0, copiedFragmentLength));
return slice;
return sliced;
}

}
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public static bool IsZero(this ReadOnlySpan<byte> bytes)
return bytes.IndexOfAnyExcept((byte)0) < 0;
}

public static int LeadingZerosCount(this Span<byte> bytes, int startIndex = 0)
public static int LeadingZerosCount(this ReadOnlySpan<byte> bytes, int startIndex = 0)
{
int nonZeroIndex = bytes[startIndex..].IndexOfAnyExcept((byte)0);
return nonZeroIndex < 0 ? bytes.Length - startIndex : nonZeroIndex;
Expand Down
305 changes: 202 additions & 103 deletions src/Nethermind/Nethermind.Evm.Precompiles/ModExpPrecompile.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Nethermind.Core;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Evm.Precompiles;
using Nethermind.Int256;

namespace Nethermind.Evm.Precompiles;
Expand Down Expand Up @@ -62,7 +61,7 @@ private static long DataGasCostInternal(ReadOnlySpan<byte> extendedInput, ReadOn

UInt256 complexity = MultComplexity(UInt256.Max(baseLength, modulusLength));

byte[] expSignificantBytes =
ReadOnlySpan<byte> expSignificantBytes =
inputData.Span.SliceWithZeroPaddingEmptyOnError(96 + (int)baseLength, (int)UInt256.Min(expLength, 32));

UInt256 lengthOver32 = expLength <= 32 ? 0 : expLength - 32;
Expand Down Expand Up @@ -109,13 +108,13 @@ private static UInt256 MultComplexity(in UInt256 adjustedExponentLength)
return adjustedExponentLength * adjustedExponentLength / 16 + 480 * adjustedExponentLength - 199680;
}

private static UInt256 AdjustedExponentLength(in UInt256 lengthOver32, byte[] exponent)
private static UInt256 AdjustedExponentLength(in UInt256 lengthOver32, ReadOnlySpan<byte> exponent)
{
bool overflow = false;
bool underflow = false;
UInt256 result;

int leadingZeros = exponent.AsSpan().LeadingZerosCount();
int leadingZeros = exponent.LeadingZerosCount();
if (leadingZeros == exponent.Length)
{
overflow |= UInt256.MultiplyOverflow(lengthOver32, Eight, out result);
Expand Down
43 changes: 38 additions & 5 deletions src/Nethermind/Nethermind.Evm.Test/Eip2565Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

using System;
using System.Linq;
using System.Numerics;
using FluentAssertions;
using MathNet.Numerics.Random;
using Nethermind.Core.Extensions;
using Nethermind.Evm.Precompiles;
using Nethermind.Int256;
using Nethermind.Specs.Forks;
using NUnit.Framework;

Expand All @@ -25,13 +27,12 @@ public void Simple_routine([Random(int.MinValue, int.MaxValue, 100)] int seed)
string randomInput = string.Format("{0}{0}{0}{1}", Length64, data.ToHexString());

Prepare input = Prepare.EvmCode.FromCode(randomInput);
byte[] inputData = input.Done.ToArray();

(ReadOnlyMemory<byte>, bool) gmpPair = ModExpPrecompile.Instance.Run(input.Done.ToArray(), Berlin.Instance);
#pragma warning disable 618
(ReadOnlyMemory<byte>, bool) bigIntPair = ModExpPrecompile.OldRun(input.Done.ToArray());
#pragma warning restore 618
(ReadOnlyMemory<byte>, bool) gmpPair = ModExpPrecompile.Instance.Run(inputData, Berlin.Instance);
(ReadOnlyMemory<byte>, bool) bigIntPair = BigIntegerModExp(inputData);

Assert.That(bigIntPair.Item1.ToArray(), Is.EqualTo(gmpPair.Item1.ToArray()));
Assert.That(gmpPair.Item1.ToArray(), Is.EqualTo(bigIntPair.Item1.ToArray()));
}

[Test]
Expand All @@ -51,5 +52,37 @@ public void ModExp_run_should_not_throw_exception(string inputStr)
long gas = ModExpPrecompile.Instance.DataGasCost(input.Done, London.Instance);
gas.Should().Be(200);
}

private static (byte[], bool) BigIntegerModExp(byte[] inputData)
{
(int baseLength, int expLength, int modulusLength) = GetInputLengths(inputData);

BigInteger modulusInt = inputData
.SliceWithZeroPaddingEmptyOnError(96 + baseLength + expLength, modulusLength).ToUnsignedBigInteger();

if (modulusInt.IsZero)
{
return (new byte[modulusLength], true);
}

BigInteger baseInt = inputData.SliceWithZeroPaddingEmptyOnError(96, baseLength).ToUnsignedBigInteger();
BigInteger expInt = inputData.SliceWithZeroPaddingEmptyOnError(96 + baseLength, expLength)
.ToUnsignedBigInteger();
return (BigInteger.ModPow(baseInt, expInt, modulusInt).ToBigEndianByteArray(modulusLength), true);
}

private static (int baseLength, int expLength, int modulusLength) GetInputLengths(ReadOnlySpan<byte> inputData)
{
Span<byte> extendedInput = stackalloc byte[96];
inputData[..Math.Min(96, inputData.Length)]
.CopyTo(extendedInput[..Math.Min(96, inputData.Length)]);

int baseLength = (int)new UInt256(extendedInput[..32], true);
UInt256 expLengthUint256 = new(extendedInput.Slice(32, 32), true);
int expLength = expLengthUint256 > Array.MaxLength ? Array.MaxLength : (int)expLengthUint256;
int modulusLength = (int)new UInt256(extendedInput.Slice(64, 32), true);

return (baseLength, expLength, modulusLength);
}
}
}
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.Evm.Test/Eip7823Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ public void TestInvalid(string inputHex)

byte[] input = Bytes.FromHexString(inputHex);

Assert.Throws<OverflowException>(() => TestSuccess(input, specDisabled));
Assert.Throws<OverflowException>(() => TestSuccess(input, specEnabled));
Assert.That(TestSuccess(input, specDisabled), Is.EqualTo(false));
Assert.That(TestSuccess(input, specEnabled), Is.EqualTo(false));

Assert.That(TestGas(input, specDisabled), Is.EqualTo(long.MaxValue));
Assert.That(TestGas(input, specEnabled), Is.EqualTo(long.MaxValue));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ public static EvmExceptionType InstructionExp<TTracingInst>(VirtualMachine vm, r
goto StackUnderflow;

// Pop the exponent as a 256-bit word.
Span<byte> bytes = stack.PopWord256();
ReadOnlySpan<byte> bytes = stack.PopWord256();

// Determine the effective byte-length of the exponent.
int leadingZeros = bytes.LeadingZerosCount();
Expand Down
20 changes: 5 additions & 15 deletions src/Nethermind/Nethermind.Precompiles.Benchmark/ModExpBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,12 @@

using System;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using Nethermind.Evm.Precompiles;

namespace Nethermind.Precompiles.Benchmark
{
public class ModExpBenchmark : PrecompileBenchmarkBase
{
protected override IEnumerable<IPrecompile> Precompiles => new[] { ModExpPrecompile.Instance };
protected override string InputsDirectory => "modexp";
namespace Nethermind.Precompiles.Benchmark;

[Benchmark]
public (ReadOnlyMemory<byte>, bool) BigInt()
{
#pragma warning disable CS0618 // Type or member is obsolete
return ModExpPrecompile.OldRun(Input.Bytes);
#pragma warning restore CS0618 // Type or member is obsolete
}
}
public class ModExpBenchmark : PrecompileBenchmarkBase
{
protected override IEnumerable<IPrecompile> Precompiles => new[] { ModExpPrecompile.Instance };
protected override string InputsDirectory => "modexp";
}