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

EIP-2537 #6964

Merged
merged 28 commits into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
07abc26
start implementing subgroup checks
Marchhill Apr 25, 2024
8e674bb
g1 subgroup check
Marchhill Apr 25, 2024
4071c3a
g2 subgroup checks
Marchhill Apr 26, 2024
09bf7c5
fix subgroup checks
Marchhill Apr 26, 2024
5bd7c3b
checks for multiexp and pairing
Marchhill Apr 26, 2024
bb47028
fix whitespace
Marchhill Apr 27, 2024
a5235a1
optimise sg checks
Marchhill Apr 27, 2024
08823f4
change constant endianness
Marchhill Apr 27, 2024
28ee4b4
eip2537 tests submodule
Marchhill Apr 30, 2024
4a4e239
load paris tests
Marchhill May 1, 2024
9eedb1d
timestamp enabled
Marchhill May 1, 2024
c5e660f
add Eip2537Tests
MarekM25 May 1, 2024
c3dac14
add some gas costs
Marchhill May 1, 2024
a2a117b
Merge branch 'feature/fast-sg-checks' into feature/bls-tests
Marchhill May 1, 2024
85dd47c
fix precompile addresses
MarekM25 May 2, 2024
f99119a
precompile address extensions
Marchhill May 2, 2024
9e8e520
update pairing and g2 multiexp costs
Marchhill May 2, 2024
dee0fb9
restore launch settings
Marchhill May 2, 2024
3c76afa
updated ethereum tests
Marchhill May 2, 2024
4094349
Merge remote-tracking branch 'upstream/master' into feature/bls-tests
Marchhill May 2, 2024
7cf4691
fix some tests
Marchhill May 2, 2024
ac6eba2
update tests submodue stCreate2
Marchhill May 3, 2024
655e615
try disabling eip2537 state tests
Marchhill May 3, 2024
a48a24f
restore eip2537 tests
Marchhill May 3, 2024
2e7436c
revert to old eth tests
Marchhill May 3, 2024
4ba8b31
fix whitespace
Marchhill May 3, 2024
86abf8f
revert metrics change
Marchhill May 7, 2024
998227e
remove old comments
Marchhill May 7, 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
26 changes: 26 additions & 0 deletions src/Nethermind/Ethereum.Blockchain.Test/Eip2537Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using Ethereum.Test.Base;
using NUnit.Framework;

namespace Ethereum.Blockchain.Test
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
public class Eip2537Tests : GeneralStateTestBase
{
[TestCaseSource(nameof(LoadTests))]
public void Test(GeneralStateTest test)
{
Assert.True(RunTest(test).Pass);
}

public static IEnumerable<GeneralStateTest> LoadTests()
{
var loader = new TestsSourceLoader(new LoadGeneralStateTestsStrategy(), "../EIPTests/StateTests/stEIP2537");
return (IEnumerable<GeneralStateTest>)loader.LoadTests();
}
}
}
2 changes: 2 additions & 0 deletions src/Nethermind/Ethereum.Test.Base/JsonToEthereumTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ private static IReleaseSpec ParseSpec(string network)
"GrayGlacier" => GrayGlacier.Instance,
"Shanghai" => Shanghai.Instance,
"Cancun" => Cancun.Instance,
"Paris" => Paris.Instance,
"Prague" => Prague.Instance,
_ => throw new NotSupportedException()
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static bool IsPrecompile(this Address address, IReleaseSpec releaseSpec)
0x08 => releaseSpec.Bn128Enabled,
0x09 => releaseSpec.BlakeEnabled,
0x0a => releaseSpec.IsEip4844Enabled,
0x0b => releaseSpec.Bls381Enabled,
0x0c => releaseSpec.Bls381Enabled,
0x0d => releaseSpec.Bls381Enabled,
0x0e => releaseSpec.Bls381Enabled,
Expand All @@ -35,7 +36,6 @@ public static bool IsPrecompile(this Address address, IReleaseSpec releaseSpec)
0x11 => releaseSpec.Bls381Enabled,
0x12 => releaseSpec.Bls381Enabled,
0x13 => releaseSpec.Bls381Enabled,
0x14 => releaseSpec.Bls381Enabled,
_ => false
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ private G1AddPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x0c);
public static Address Address { get; } = Address.FromNumber(0x0b);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
return 600L;
return 500L;
}

public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private G1MulPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x0d);
public static Address Address { get; } = Address.FromNumber(0x0c);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
Expand All @@ -39,13 +39,12 @@ public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseS
return (Array.Empty<byte>(), false);
}

// Span<byte> inputDataSpan = stackalloc byte[expectedInputLength];
// inputData.PrepareEthInput(inputDataSpan);

(byte[], bool) result;

Span<byte> output = stackalloc byte[2 * BlsParams.LenFp];
bool success = Pairings.BlsG1Mul(inputData.Span, output);
bool success = SubgroupChecks.G1IsInSubGroup(inputData.Span[..(2 * BlsParams.LenFp)])
&& Pairings.BlsG1Mul(inputData.Span, output);

if (success)
{
result = (output.ToArray(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private G1MultiExpPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x0e);
public static Address Address { get; } = Address.FromNumber(0x0d);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
Expand All @@ -41,6 +41,15 @@ public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseS
return (Array.Empty<byte>(), false);
}

for (int i = 0; i < (inputData.Length / ItemSize); i++)
{
int offset = i * ItemSize;
if (!SubgroupChecks.G1IsInSubGroup(inputData.Span[offset..(offset + (2 * BlsParams.LenFp))]))
{
return (Array.Empty<byte>(), false);
}
}

(byte[], bool) result;

Span<byte> output = stackalloc byte[2 * BlsParams.LenFp];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ private G2AddPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x0f);
public static Address Address { get; } = Address.FromNumber(0x0e);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
return 4500L;
return 800L;
}

public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ private G2MulPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x10);
public static Address Address { get; } = Address.FromNumber(0x0f);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
return 55000L;
return 45000L;
}

public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
Expand All @@ -45,7 +45,8 @@ public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseS
(byte[], bool) result;

Span<byte> output = stackalloc byte[4 * BlsParams.LenFp];
bool success = Pairings.BlsG2Mul(inputData.Span, output);
bool success = SubgroupChecks.G2IsInSubGroup(inputData.Span[..(4 * BlsParams.LenFp)])
&& Pairings.BlsG2Mul(inputData.Span, output);
if (success)
{
result = (output.ToArray(), true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private G2MultiExpPrecompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x11);
public static Address Address { get; } = Address.FromNumber(0x10);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
Expand All @@ -29,7 +29,7 @@ public long BaseGasCost(IReleaseSpec releaseSpec)
public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
int k = inputData.Length / ItemSize;
return 55000L * k * Discount.For(k) / 1000;
return 45000L * k * Discount.For(k) / 1000;
}

private const int ItemSize = 288;
Expand All @@ -41,6 +41,15 @@ public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseS
return (Array.Empty<byte>(), false);
}

for (int i = 0; i < (inputData.Length / ItemSize); i++)
{
int offset = i * ItemSize;
if (!SubgroupChecks.G2IsInSubGroup(inputData.Span[offset..(offset + (4 * BlsParams.LenFp))]))
{
return (Array.Empty<byte>(), false);
}
}

(byte[], bool) result;

Span<byte> output = stackalloc byte[4 * BlsParams.LenFp];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private MapToG1Precompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x13);
public static Address Address { get; } = Address.FromNumber(0x12);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ private MapToG2Precompile()
{
}

public static Address Address { get; } = Address.FromNumber(0x14);
public static Address Address { get; } = Address.FromNumber(0x13);

public long BaseGasCost(IReleaseSpec releaseSpec)
{
return 110000;
return 75000;
}

public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
Expand Down
23 changes: 20 additions & 3 deletions src/Nethermind/Nethermind.Evm/Precompiles/Bls/PairingPrecompile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ public class PairingPrecompile : IPrecompile<PairingPrecompile>

private PairingPrecompile() { }

public static Address Address { get; } = Address.FromNumber(0x12);
public static Address Address { get; } = Address.FromNumber(0x11);

public static PairingPrecompile Instance = new PairingPrecompile();

public long BaseGasCost(IReleaseSpec releaseSpec) => 115000L;
public long BaseGasCost(IReleaseSpec releaseSpec) => 65000L;

public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
{
return 23000L * (inputData.Length / PairSize);
return 43000L * (inputData.Length / PairSize);
}

public (ReadOnlyMemory<byte>, bool) Run(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseSpec)
Expand All @@ -38,6 +38,23 @@ public long DataGasCost(in ReadOnlyMemory<byte> inputData, IReleaseSpec releaseS
(byte[], bool) result;

Span<byte> output = stackalloc byte[32];

for (int i = 0; i < (inputData.Length / PairSize); i++)
{
int offset = i * PairSize;
if (!SubgroupChecks.G1IsInSubGroup(inputData.Span[offset..(offset + (2 * BlsParams.LenFp))]))
{
return (Array.Empty<byte>(), false);
}

offset += 2 * BlsParams.LenFp;

if (!SubgroupChecks.G2IsInSubGroup(inputData.Span[offset..(offset + (4 * BlsParams.LenFp))]))
{
return (Array.Empty<byte>(), false);
}
}

bool success = Pairings.BlsPairing(inputData.Span, output);
if (success)
{
Expand Down
135 changes: 135 additions & 0 deletions src/Nethermind/Nethermind.Evm/Precompiles/Bls/SubgroupChecks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;

namespace Nethermind.Evm.Precompiles.Bls;

/// <summary>
/// https://eips.ethereum.org/EIPS/eip-2537
/// </summary>
public class SubgroupChecks
{
internal static readonly BigInteger BlsBaseFieldOrder = new([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], true, true);
internal static readonly BigInteger BlsSubgroupOrder = new([0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01], true, true);
internal static readonly BigInteger Beta = new([0x5F, 0x19, 0x67, 0x2F, 0xDF, 0x76, 0xCE, 0x51, 0xBA, 0x69, 0xC6, 0x07, 0x6A, 0x0F, 0x77, 0xEA, 0xDD, 0xB3, 0xA9, 0x3B, 0xE6, 0xF8, 0x96, 0x88, 0xDE, 0x17, 0xD8, 0x13, 0x62, 0x0A, 0x00, 0x02, 0x2E, 0x01, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE], true, true);
internal static readonly BigInteger X = Normalise(new BigInteger(15132376222941642752));
internal static readonly byte[] G1SubgroupMultiplier = (X * X % BlsSubgroupOrder).ToBigEndianByteArray(32);
internal static readonly byte[] G2SubgroupMultiplier = X.ToBigEndianByteArray(32);
internal static readonly Fp2 R = new(0, new BigInteger([0x1A, 0x01, 0x11, 0xEA, 0x39, 0x7F, 0xE6, 0x99, 0xEC, 0x02, 0x40, 0x86, 0x63, 0xD4, 0xDE, 0x85, 0xAA, 0x0D, 0x85, 0x7D, 0x89, 0x75, 0x9A, 0xD4, 0x89, 0x7D, 0x29, 0x65, 0x0F, 0xB8, 0x5F, 0x9B, 0x40, 0x94, 0x27, 0xEB, 0x4F, 0x49, 0xFF, 0xFD, 0x8B, 0xFD, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAD], true, true));
internal static readonly Fp2 S = new(new BigInteger([0x13, 0x52, 0x03, 0xE6, 0x01, 0x80, 0xA6, 0x8E, 0xE2, 0xE9, 0xC4, 0x48, 0xD7, 0x7A, 0x2C, 0xD9, 0x1C, 0x3D, 0xED, 0xD9, 0x30, 0xB1, 0xCF, 0x60, 0xEF, 0x39, 0x64, 0x89, 0xF6, 0x1E, 0xB4, 0x5E, 0x30, 0x44, 0x66, 0xCF, 0x3E, 0x67, 0xFA, 0x0A, 0xF1, 0xEE, 0x7B, 0x04, 0x12, 0x1B, 0xDE, 0xA2], true, true), new BigInteger([0x06, 0xAF, 0x0E, 0x04, 0x37, 0xFF, 0x40, 0x0B, 0x68, 0x31, 0xE3, 0x6D, 0x6B, 0xD1, 0x7F, 0xFE, 0x48, 0x39, 0x5D, 0xAB, 0xC2, 0xD3, 0x43, 0x5E, 0x77, 0xF7, 0x6E, 0x17, 0x00, 0x92, 0x41, 0xC5, 0xEE, 0x67, 0x99, 0x2F, 0x72, 0xEC, 0x05, 0xF4, 0xC8, 0x10, 0x84, 0xFB, 0xED, 0xE3, 0xCC, 0x09], true, true));

[SkipLocalsInit]
public static bool G1IsInSubGroup(ReadOnlySpan<byte> p)
{
Span<byte> buf = stackalloc byte[3 * 128];
Span<byte> mulArgs = buf[128..(2 * 128 + 32)];
Span<byte> addArgs = buf[..(2 * 128)];
Span<byte> res = buf[(2 * 128)..];

p.CopyTo(mulArgs);
G1SubgroupMultiplier.CopyTo(mulArgs[128..]);

if (!Pairings.BlsG1Mul(mulArgs, addArgs[..128]))
{
return false;
}

Phi(p, addArgs[128..]);

if (!Pairings.BlsG1Add(addArgs, res))
{
return false;
}

return res.IndexOfAnyExcept((byte)0) == -1;
}

[SkipLocalsInit]
public static bool G2IsInSubGroup(ReadOnlySpan<byte> p)
{
Span<byte> buf = stackalloc byte[3 * 256];
Span<byte> mulArgs = buf[256..(2 * 256 + 32)];
Span<byte> addArgs = buf[..(2 * 256)];
Span<byte> res = buf[(2 * 256)..];

p.CopyTo(mulArgs);
G2SubgroupMultiplier.CopyTo(mulArgs[256..]);
if (!Pairings.BlsG2Mul(mulArgs, addArgs[..256]))
{
return false;
}

Psi(p, addArgs[256..]);

if (!Pairings.BlsG2Add(addArgs, res))
{
return false;
}

return res.IndexOfAnyExcept((byte)0) == -1;
}

internal static void Phi(ReadOnlySpan<byte> p, Span<byte> res)
{
BigInteger x = new(p[..64], true, true);
x = Beta * x % BlsBaseFieldOrder;
x.ToBigEndianByteArray(64).CopyTo(res);
p[64..].CopyTo(res[64..]);
}

internal static void Psi(ReadOnlySpan<byte> p, Span<byte> res)
{
Fp2 x = new Fp2(p[..128]).Conjugate() * R;
Fp2 y = new Fp2(p[128..]).Conjugate() * S;
x.ToBytes(res[..128]);
y.ToBytes(res[128..]);
}

internal static BigInteger Normalise(in BigInteger x)
{
BigInteger unnormalised = x % BlsBaseFieldOrder;
Copy link
Member

Choose a reason for hiding this comment

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

BigIntegers are painfully slow, do we need simple UInt512 types?

Copy link
Contributor Author

@Marchhill Marchhill May 3, 2024

Choose a reason for hiding this comment

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

At some point the subgroup checks should be implemented in the cryptography library, so probably not worth optimising for now. Just implemented them in C# quickly for the devnet, and because we still might change libraries

return unnormalised >= 0 ? unnormalised : (BlsBaseFieldOrder + unnormalised);
}

internal class Fp2
{
public BigInteger c0, c1;

public Fp2(BigInteger c0, BigInteger c1)
{
this.c0 = c0;
this.c1 = c1;
}

public Fp2(ReadOnlySpan<byte> bytes)
{
if (bytes.Length != 128)
{
throw new Exception("Could not decode Fp2 point, incorrect length.");
}
c0 = new(bytes[..64], true, true);
c1 = new(bytes[64..], true, true);
}

public static Fp2 operator *(Fp2 a, Fp2 b)
=> new Fp2(Normalise((a.c0 * b.c0) - (a.c1 * b.c1)), Normalise((a.c1 * b.c0) + (a.c0 * b.c1)));

public Fp2 Conjugate()
{
c1 = Normalise(-c1);
return this;
}

public void ToBytes(Span<byte> res)
{
c0.ToBigEndianByteArray(64).CopyTo(res);
c1.ToBigEndianByteArray(64).CopyTo(res[64..]);
}
}
}
Loading
Loading