Skip to content

Commit

Permalink
BLS precompile native benchmarking & improvements (#7544)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marchhill authored and kamilchodola committed Oct 16, 2024
1 parent 5006e80 commit ec51a10
Show file tree
Hide file tree
Showing 32 changed files with 341 additions and 240 deletions.
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,5 +363,6 @@ public void Dispose()

public Span<T> AsSpan() => _array.AsSpan(0, Count);

public ReadOnlyMemory<T> AsMemory() => new(_array, 0, Count);
public Memory<T> AsMemory() => new(_array, 0, Count);
public ReadOnlyMemory<T> AsReadOnlyMemory() => new(_array, 0, Count);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@

namespace Nethermind.Evm.Test;

public class BlsMultiMulG1PrecompileTests
public class BlsG1MSMPrecompileTests
{
[Test]
public void Test()
{
foreach ((byte[] input, ReadOnlyMemory<byte> expectedResult) in Inputs)
{
IPrecompile precompile = G1MultiMulPrecompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, MuirGlacier.Instance);
IPrecompile precompile = G1MSMPrecompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, Prague.Instance);
output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray());
success.Should().BeTrue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace Nethermind.Evm.Test;

public class BlsAddG2PrecompileTests
public class BlsG2AddPrecompileTests
{
[Test]
public void Test()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
using Nethermind.Evm.Precompiles;
using Nethermind.Evm.Precompiles.Bls;
using NUnit.Framework;
using static Nethermind.Specs.Forks.MuirGlacier;
using Nethermind.Specs.Forks;

namespace Nethermind.Evm.Test;

public class BlsMultiMulG2PrecompileTests
public class BlsG2MSMPrecompileTests
{
[Test]
public void Test()
{
foreach ((byte[] input, ReadOnlyMemory<byte> expectedResult) in Inputs)
{
IPrecompile precompile = G2MultiMulPrecompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, Instance);
IPrecompile precompile = G2MSMPrecompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, Prague.Instance);
output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray());
success.Should().BeTrue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@

namespace Nethermind.Evm.Test;

public class BlsMapToG2Tests
public class BlsMapFp2ToG2Tests
{
[Test]
public void Test()
{
foreach ((byte[] input, ReadOnlyMemory<byte> expectedResult) in Inputs)
{
IPrecompile precompile = MapToG2Precompile.Instance;
IPrecompile precompile = MapFp2ToG2Precompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, MuirGlacier.Instance);

output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@

namespace Nethermind.Evm.Test;

public class BlsMapToG1Tests
public class BlsMapFpToG1Tests
{
[Test]
public void Test()
{
foreach ((byte[] input, ReadOnlyMemory<byte> expectedResult) in Inputs)
{
IPrecompile precompile = MapToG1Precompile.Instance;
IPrecompile precompile = MapFpToG1Precompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, MuirGlacier.Instance);

output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@

namespace Nethermind.Evm.Test;

public class BlsPairingPrecompileTests
public class BlsPairingCheckPrecompileTests
{
[Test]
public void Test()
{
foreach ((byte[] input, ReadOnlyMemory<byte> expectedResult) in Inputs)
{
IPrecompile precompile = PairingPrecompile.Instance;
IPrecompile precompile = PairingCheckPrecompile.Instance;
(ReadOnlyMemory<byte> output, bool success) = precompile.Run(input, MuirGlacier.Instance);

output.ToArray().Should().BeEquivalentTo(expectedResult.ToArray());
Expand Down
10 changes: 5 additions & 5 deletions src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ private static FrozenDictionary<AddressAsKey, CodeInfo> InitializePrecompiledCon

[G1AddPrecompile.Address] = new(G1AddPrecompile.Instance),
[G1MulPrecompile.Address] = new(G1MulPrecompile.Instance),
[G1MultiMulPrecompile.Address] = new(G1MultiMulPrecompile.Instance),
[G1MSMPrecompile.Address] = new(G1MSMPrecompile.Instance),
[G2AddPrecompile.Address] = new(G2AddPrecompile.Instance),
[G2MulPrecompile.Address] = new(G2MulPrecompile.Instance),
[G2MultiMulPrecompile.Address] = new(G2MultiMulPrecompile.Instance),
[PairingPrecompile.Address] = new(PairingPrecompile.Instance),
[MapToG1Precompile.Address] = new(MapToG1Precompile.Instance),
[MapToG2Precompile.Address] = new(MapToG2Precompile.Instance),
[G2MSMPrecompile.Address] = new(G2MSMPrecompile.Instance),
[PairingCheckPrecompile.Address] = new(PairingCheckPrecompile.Instance),
[MapFpToG1Precompile.Address] = new(MapFpToG1Precompile.Instance),
[MapFp2ToG2Precompile.Address] = new(MapFp2ToG2Precompile.Instance),

[PointEvaluationPrecompile.Address] = new(PointEvaluationPrecompile.Instance),

Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsConst.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Nethermind.Evm.Precompiles.Bls;

public static class BlsConst
{
public const bool DisableConcurrency = false;
public const bool DisableSubgroupChecks = false;
public const int LenFr = 32;
public const int LenFp = 64;
public const int LenFpTrimmed = 48;
Expand All @@ -16,6 +18,7 @@ public static class BlsConst
public const int LenG1Trimmed = 2 * LenFpTrimmed;
public const int LenG2 = 4 * LenFp;
public const int LenG2Trimmed = 4 * LenFpTrimmed;

public static readonly byte[] BaseFieldOrder = [0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab];
public static readonly ReadOnlyMemory<byte> G1Inf = Enumerable.Repeat<byte>(0, 128).ToArray();
public static readonly ReadOnlyMemory<byte> G2Inf = Enumerable.Repeat<byte>(0, 256).ToArray();
Expand Down
76 changes: 62 additions & 14 deletions src/Nethermind/Nethermind.Evm/Precompiles/Bls/BlsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,6 @@ public static bool TryDecodeRaw(this G1 p, ReadOnlySpan<byte> raw)
return true;
}

public static ReadOnlyMemory<byte> EncodeRaw(this G1 p)
{
if (p.IsInf())
{
return BlsConst.G1Inf;
}

byte[] raw = new byte[BlsConst.LenG1];
ReadOnlySpan<byte> trimmed = p.Serialize();
trimmed[..BlsConst.LenFpTrimmed].CopyTo(raw.AsSpan()[BlsConst.LenFpPad..BlsConst.LenFp]);
trimmed[BlsConst.LenFpTrimmed..].CopyTo(raw.AsSpan()[(BlsConst.LenFp + BlsConst.LenFpPad)..]);
return raw;
}

public static bool TryDecodeRaw(this G2 p, ReadOnlySpan<byte> raw)
{
if (raw.Length != BlsConst.LenG2)
Expand Down Expand Up @@ -96,6 +82,20 @@ public static bool TryDecodeRaw(this G2 p, ReadOnlySpan<byte> raw)
return true;
}

public static ReadOnlyMemory<byte> EncodeRaw(this G1 p)
{
if (p.IsInf())
{
return BlsConst.G1Inf;
}

byte[] raw = new byte[BlsConst.LenG1];
ReadOnlySpan<byte> trimmed = p.Serialize();
trimmed[..BlsConst.LenFpTrimmed].CopyTo(raw.AsSpan()[BlsConst.LenFpPad..BlsConst.LenFp]);
trimmed[BlsConst.LenFpTrimmed..].CopyTo(raw.AsSpan()[(BlsConst.LenFp + BlsConst.LenFpPad)..]);
return raw;
}

public static ReadOnlyMemory<byte> EncodeRaw(this G2 p)
{
if (p.IsInf())
Expand Down Expand Up @@ -128,4 +128,52 @@ public static bool ValidRawFp(ReadOnlySpan<byte> fp)
// check that fp < base field order
return fp[BlsConst.LenFpPad..].SequenceCompareTo(BlsConst.BaseFieldOrder.AsSpan()) < 0;
}

public static bool TryDecodeG1ToBuffer(ReadOnlyMemory<byte> inputData, Memory<long> pointBuffer, Memory<byte> scalarBuffer, int dest, int index)
=> TryDecodePointToBuffer(inputData, pointBuffer, scalarBuffer, dest, index, BlsConst.LenG1, G1MSMPrecompile.ItemSize, DecodeAndCheckG1);

public static bool TryDecodeG2ToBuffer(ReadOnlyMemory<byte> inputData, Memory<long> pointBuffer, Memory<byte> scalarBuffer, int dest, int index)
=> TryDecodePointToBuffer(inputData, pointBuffer, scalarBuffer, dest, index, BlsConst.LenG2, G2MSMPrecompile.ItemSize, DecodeAndCheckG2);

private static bool DecodeAndCheckG1(ReadOnlyMemory<byte> rawPoint, Memory<long> pointBuffer, int dest)
{
G1 p = new(pointBuffer.Span[(dest * G1.Sz)..]);
return p.TryDecodeRaw(rawPoint.Span) && (BlsConst.DisableSubgroupChecks || p.InGroup());
}

private static bool DecodeAndCheckG2(ReadOnlyMemory<byte> rawPoint, Memory<long> pointBuffer, int dest)
{
G2 p = new(pointBuffer.Span[(dest * G2.Sz)..]);
return p.TryDecodeRaw(rawPoint.Span) && (BlsConst.DisableSubgroupChecks || p.InGroup());
}

private static bool TryDecodePointToBuffer(
ReadOnlyMemory<byte> inputData,
Memory<long> pointBuffer,
Memory<byte> scalarBuffer,
int dest,
int index,
int pointLen,
int itemSize,
Func<ReadOnlyMemory<byte>, Memory<long>, int, bool> decodeAndCheckPoint)
{
if (dest == -1)
{
return true;
}

int offset = index * itemSize;
ReadOnlyMemory<byte> rawPoint = inputData[offset..(offset + pointLen)];
ReadOnlyMemory<byte> reversedScalar = inputData[(offset + pointLen)..(offset + itemSize)];

if (!decodeAndCheckPoint(rawPoint, pointBuffer, dest))
{
return false;
}

int destOffset = dest * 32;
reversedScalar.CopyTo(scalarBuffer[destOffset..]);
scalarBuffer[destOffset..(destOffset + 32)].Span.Reverse();
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Runtime.CompilerServices;
using Nethermind.Core;
using Nethermind.Core.Specs;

Expand All @@ -26,6 +27,7 @@ private G1AddPrecompile()

public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec) => 0L;

[SkipLocalsInit]
public (ReadOnlyMemory<byte>, bool) Run(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
const int expectedInputLength = 2 * BlsConst.LenG1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@
using Nethermind.Core.Collections;

using G1 = Nethermind.Crypto.Bls.P1;
using System.Runtime.CompilerServices;

namespace Nethermind.Evm.Precompiles.Bls;

/// <summary>
/// https://eips.ethereum.org/EIPS/eip-2537
/// </summary>
public class G1MultiMulPrecompile : IPrecompile<G1MultiMulPrecompile>
public class G1MSMPrecompile : IPrecompile<G1MSMPrecompile>
{
public static readonly G1MultiMulPrecompile Instance = new();
public static readonly G1MSMPrecompile Instance = new();

private G1MultiMulPrecompile()
private G1MSMPrecompile()
{
}

Expand All @@ -32,8 +33,9 @@ public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec
return 12000L * k * Discount.For(k) / 1000;
}

private const int ItemSize = 160;
public const int ItemSize = 160;

[SkipLocalsInit]
public (ReadOnlyMemory<byte>, bool) Run(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
if (inputData.Length % ItemSize > 0 || inputData.Length == 0)
Expand All @@ -43,7 +45,7 @@ public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec

int nItems = inputData.Length / ItemSize;

using ArrayPoolList<long> rawPoints = new(nItems * 18, nItems * 18);
using ArrayPoolList<long> rawPoints = new(nItems * G1.Sz, nItems * G1.Sz);
using ArrayPoolList<byte> rawScalars = new(nItems * 32, nItems * 32);
using ArrayPoolList<int> pointDestinations = new(nItems);

Expand All @@ -64,34 +66,39 @@ public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec
}

bool fail = false;
Parallel.ForEach(pointDestinations, (dest, state, i) =>

#pragma warning disable CS0162 // Unreachable code detected
if (BlsConst.DisableConcurrency)
{
if (dest != -1)
for (int i = 0; i < pointDestinations.Count; i++)
{
int offset = (int)i * ItemSize;
ReadOnlySpan<byte> rawPoint = inputData[offset..(offset + BlsConst.LenG1)].Span;
ReadOnlySpan<byte> rawScalar = inputData[(offset + BlsConst.LenG1)..(offset + ItemSize)].Span;

G1 p = new(rawPoints.AsSpan()[(dest * 18)..]);

if (!p.TryDecodeRaw(rawPoint) || !p.InGroup())
if (!BlsExtensions.TryDecodeG1ToBuffer(inputData, rawPoints.AsMemory(), rawScalars.AsMemory(), pointDestinations[i], i))
{
fail = true;
state.Break();
break;
}

int destOffset = dest * 32;
rawScalar.CopyTo(rawScalars.AsSpan()[destOffset..]);
rawScalars.AsSpan()[destOffset..(destOffset + 32)].Reverse();
}
});
}
else
{
Parallel.ForEach(pointDestinations, (dest, state, i) =>
{
int index = (int)i;
if (!BlsExtensions.TryDecodeG1ToBuffer(inputData, rawPoints.AsMemory(), rawScalars.AsMemory(), dest, index))
{
fail = true;
state.Break();
}
});
}
#pragma warning restore CS0162 // Unreachable code detected

if (fail)
{
return IPrecompile.Failure;
}

G1 res = new G1().MultiMult(rawPoints.AsSpan(), rawScalars.AsSpan(), npoints);
G1 res = new G1(stackalloc long[G1.Sz]).MultiMult(rawPoints.AsSpan(), rawScalars.AsSpan(), npoints);
return (res.EncodeRaw(), true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Runtime.CompilerServices;
using Nethermind.Core;
using Nethermind.Core.Specs;
using G1 = Nethermind.Crypto.Bls.P1;
Expand All @@ -25,6 +26,7 @@ private G1MulPrecompile()

public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec) => 0L;

[SkipLocalsInit]
public (ReadOnlyMemory<byte>, bool) Run(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
const int expectedInputLength = BlsConst.LenG1 + BlsConst.LenFr;
Expand All @@ -34,7 +36,7 @@ private G1MulPrecompile()
}

G1 x = new(stackalloc long[G1.Sz]);
if (!x.TryDecodeRaw(inputData[..BlsConst.LenG1].Span) || !x.InGroup())
if (!x.TryDecodeRaw(inputData[..BlsConst.LenG1].Span) || !(BlsConst.DisableSubgroupChecks || x.InGroup()))
{
return IPrecompile.Failure;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Runtime.CompilerServices;
using Nethermind.Core;
using Nethermind.Core.Specs;

Expand All @@ -26,6 +27,7 @@ private G2AddPrecompile()

public long DataGasCost(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec) => 0L;

[SkipLocalsInit]
public (ReadOnlyMemory<byte>, bool) Run(ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
const int expectedInputLength = 2 * BlsConst.LenG2;
Expand Down
Loading

0 comments on commit ec51a10

Please sign in to comment.