-
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
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. |
||
| } | ||
| 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]; | ||
| 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