-
Notifications
You must be signed in to change notification settings - Fork 1k
Add Ethereum-compatible aliases for BLS12-381 #4186
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
base: dev
Are you sure you want to change the base?
Changes from 8 commits
1a443fe
67b0fbb
7f3fe50
dc9b374
1f82a9b
be21a84
3f69ba9
9607bd7
b6fb04b
5f4dbe5
2e8378c
434c62e
dc23a56
bcabd4e
5b40385
15cb14b
a39ed8e
7fa60b7
a49a6b7
7d468ce
df66f45
93713e8
8dedeed
f2c75e6
421bc93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,8 @@ | |
| using Neo.Cryptography.BLS12_381; | ||
| using Neo.VM.Types; | ||
| using System; | ||
| using Array = Neo.VM.Types.Array; | ||
| using VMBuffer = Neo.VM.Types.Buffer; | ||
|
|
||
| namespace Neo.SmartContract.Native | ||
| { | ||
|
|
@@ -119,6 +121,77 @@ public static InteropInterface Bls12381Mul(InteropInterface x, byte[] mul, bool | |
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Multi exponentiation operation for bls12381 points. | ||
| /// </summary> | ||
| /// <param name="pairs">Array of [point, scalar] pairs.</param> | ||
| /// <returns>The accumulated point.</returns> | ||
| [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 23)] | ||
| public static InteropInterface Bls12381MultiExp(Array pairs) | ||
| { | ||
| if (pairs is null || pairs.Count == 0) | ||
| throw new ArgumentException("BLS12-381 multi exponent requires at least one pair"); | ||
|
|
||
| bool? useG2 = null; | ||
| G1Projective g1Accumulator = G1Projective.Identity; | ||
| G2Projective g2Accumulator = G2Projective.Identity; | ||
|
|
||
| foreach (StackItem item in pairs) | ||
| { | ||
| if (item is not Array pair || pair.Count != 2) | ||
| throw new ArgumentException("BLS12-381 multi exponent pair must contain point and scalar"); | ||
|
|
||
| if (pair[0] is not InteropInterface pointInterface) | ||
| throw new ArgumentException("BLS12-381 multi exponent requires interop points"); | ||
|
|
||
| var point = pointInterface.GetInterface<object>(); | ||
| switch (point) | ||
| { | ||
| case G1Affine g1Affine: | ||
| EnsureGroupType(ref useG2, false); | ||
| { | ||
| var scalar = ParseScalar(pair[1]); | ||
| if (!scalar.IsZero) | ||
| g1Accumulator += new G1Projective(g1Affine) * scalar; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to make sure that subgroup check is executed before any multiplication operation. This was fixed in Ethereum through ethereum/EIPs#8456. Briefly speaking, we need:
About the detailed implementation of these checks, please ref https://github.com/Consensys/gnark-crypto/blob/v0.19.0/ecc/bls12-381/g1.go#L193-L218 and https://github.com/Consensys/gnark-crypto/blob/v0.19.0/ecc/bls12-381/g2.go#L200-L223.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to what I've mentioned in #4185 (comment), |
||
| } | ||
| break; | ||
| case G1Projective g1Projective: | ||
| EnsureGroupType(ref useG2, false); | ||
| { | ||
| var scalar = ParseScalar(pair[1]); | ||
| if (!scalar.IsZero) | ||
| g1Accumulator += g1Projective * scalar; | ||
| } | ||
| break; | ||
| case G2Affine g2Affine: | ||
| EnsureGroupType(ref useG2, true); | ||
| { | ||
| var scalar = ParseScalar(pair[1]); | ||
| if (!scalar.IsZero) | ||
| g2Accumulator += new G2Projective(g2Affine) * scalar; | ||
| } | ||
| break; | ||
| case G2Projective g2Projective: | ||
| EnsureGroupType(ref useG2, true); | ||
| { | ||
| var scalar = ParseScalar(pair[1]); | ||
| if (!scalar.IsZero) | ||
| g2Accumulator += g2Projective * scalar; | ||
| } | ||
| break; | ||
| default: | ||
| throw new ArgumentException("BLS12-381 type mismatch"); | ||
| } | ||
| } | ||
|
|
||
| if (useG2 is null) | ||
| throw new ArgumentException("BLS12-381 multi exponent requires at least one valid pair"); | ||
|
|
||
| return useG2.Value | ||
| ? new InteropInterface(g2Accumulator) | ||
| : new InteropInterface(g1Accumulator); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Pairing operation of g1 and g2 | ||
| /// </summary> | ||
|
|
@@ -142,5 +215,41 @@ public static InteropInterface Bls12381Pairing(InteropInterface g1, InteropInter | |
| }; | ||
| return new(Bls12.Pairing(in g1a, in g2a)); | ||
| } | ||
|
|
||
| private static void EnsureGroupType(ref bool? current, bool isG2) | ||
| { | ||
| if (current is null) | ||
| { | ||
| current = isG2; | ||
| } | ||
| else if (current.Value != isG2) | ||
| { | ||
| throw new ArgumentException("BLS12-381 multi exponent cannot mix groups"); | ||
| } | ||
| } | ||
|
|
||
| private static Scalar ParseScalar(StackItem scalarItem) | ||
| { | ||
| ReadOnlySpan<byte> data = scalarItem switch | ||
| { | ||
| ByteString bs when bs.GetSpan().Length == Scalar.Size => bs.GetSpan(), | ||
| VMBuffer buffer when buffer.Size == Scalar.Size => buffer.InnerBuffer.Span, | ||
| _ => throw new ArgumentException("BLS12-381 scalar must be 32 bytes"), | ||
| }; | ||
|
|
||
| Span<byte> littleEndian = stackalloc byte[Scalar.Size]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this an incorrect naming or really using the little-endian encoding? Since you're trying to handle big-endian in BN254, and it is big-endian in Gnark and EVM, at least we need to keep the same encoding.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'll check |
||
| data.CopyTo(littleEndian); | ||
|
|
||
| try | ||
| { | ||
| return Scalar.FromBytes(littleEndian); | ||
| } | ||
| catch (FormatException) | ||
| { | ||
| var wide = new byte[Scalar.Size * 2]; | ||
| littleEndian.CopyTo(wide); | ||
| return Scalar.FromBytesWide(wide); | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we need to check the length, a max is required or it could deny the service with 1024 pairs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cpu should be paid in each iteration