-
Notifications
You must be signed in to change notification settings - Fork 1k
Add bn254 native contract bindings and tests #4185
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: master
Are you sure you want to change the base?
Conversation
c796a25 to
3188ee3
Compare
|
BouncyCastle can do the curve public static ECDomainParameters GetBN254G1DomainParameters()
{
// Define the prime modulus 'p' of the base field.
// p = 21888242871839275222246405745257275088696311157297823662689037894645226208583
BigInteger p = new BigInteger("21888242871839275222246405745257275088696311157297823662689037894645226208583");
// Define the curve equation y^2 = x^3 + a*x + b.
// For BN254, a = 0 and b = 3.
BigInteger a = BigInteger.Zero;
BigInteger b = new BigInteger("3");
// Define the order of the G1 group 'n'.
// n = 21888242871839275222246405745257275088548364400416034343698204186575808495617
BigInteger n = new BigInteger("21888242871839275222246405745257275088548364400416034343698204186575808495617");
// The cofactor 'h' is usually a small integer. For BN254, it is (p+1-t)/n.
BigInteger h = BigInteger.One;
// Construct the finite field F_p.
ECCurve curve = new FpCurve(p, a, b, n, h);
// Define the G1 generator point coordinates (x, y).
// x = 1, y = 2.
BigInteger gx = new BigInteger("1");
BigInteger gy = new BigInteger("2");
// Create the ECPoint for the G1 generator.
ECPoint g = curve.CreatePoint(gx, gy);
// Combine parameters into an ECDomainParameters object.
return new ECDomainParameters(curve, g, n, h);
} |
AnnaShaleva
left a comment
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.
Need some more time to verify the math.
|
No progress? |
|
I’ve reworked the BN254 native contract so it no |
|
@neo-project/ngd-shanghai Need testing. |
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.
Pull Request Overview
This PR adds BN254 elliptic curve cryptography support to the Neo native CryptoLib contract, mirroring the Ethereum/EVM precompile interface. The implementation enables elliptic curve addition, scalar multiplication, and pairing operations through three new contract methods (bn254Add, bn254Mul, bn254Pairing) that handle Ethereum's big-endian payload layout and zero-on-error semantics. Unit tests include canonical Ethereum GeneralStateTests pairing vectors to ensure cross-chain compatibility.
Key Changes
- Adds three BN254 native contract methods with Ethereum-compatible interfaces
- Implements managed field arithmetic (Fp, Fp2, Fp6, Fp12) and curve operations for BN254
- Includes comprehensive test coverage using Ethereum test vectors
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Neo.UnitTests/SmartContract/Native/UT_NativeContract.cs | Updates native contract manifest snapshot to include new bn254 methods |
| tests/Neo.UnitTests/SmartContract/Native/UT_CryptoLib.cs | Adds comprehensive unit tests for BN254 operations using Ethereum test vectors |
| src/Neo/SmartContract/Native/CryptoLib.BN254.cs | Implements the three public BN254 native contract methods with input validation |
| src/Neo/Cryptography/BN254.cs | Core BN254 implementation handling Add, Mul, and Pairing operations with BouncyCastle integration |
| src/Neo/Cryptography/BN254.Managed.Fields.cs | Implements field arithmetic for Fp and Fp2 |
| src/Neo/Cryptography/BN254.Managed.Extensions.cs | Implements extension field arithmetic for Fp6 and Fp12 |
| src/Neo/Cryptography/BN254.Managed.Curves.cs | Implements curve point operations and Miller loop for pairing |
| docs/native-contracts-api.md | Documents the three new BN254 contract methods |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| if (input.Length % BN254.PairInputLength != 0) | ||
| throw new ArgumentException("Invalid BN254 pairing input length", nameof(input)); |
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.
Seems already checked in Pairing method
|
I briefly read through the implementation, and it looks compatible with the Solidity/EVM precompile interface. To verify the equivalence, please consider using the EVM test data, ref https://github.com/ethereum/go-ethereum/tree/v1.16.7/core/vm/testdata/precompiles. |
@Jim8y Nice, could you add them to test? |
654b38c to
3d61cbc
Compare
ac43dc7 to
b71f990
Compare
b71f990 to
9973b98
Compare
|
@txhsl please check again |
txhsl
left a comment
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.
Only a performance concern, otherwise all good to me.
src/Neo/Cryptography/BN254.cs
Outdated
| hasEffectivePair = true; | ||
|
|
||
| mclBnGT current = default; | ||
| Mcl.mclBn_pairing(ref current, g1, g2); |
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 mclBn_millerLoop is better here, ref https://github.com/NethermindEth/nethermind/blob/1d85282f2d88b092469a57673dc8fb9a403b3a13/src/Nethermind/Nethermind.Evm.Precompiles/BN254.cs#L108 and https://github.com/herumi/mcl/blob/e4c8bbe4af0013e00244c661836f3a3676f21024/src/pairing_impl.hpp#L774.
The longer a correct input we have, the more times the finalExp is executed. Anyway, it depends on your design of the procedure.
AnnaShaleva
left a comment
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.
Please, consider also to update the price: https://github.com/neo-project/neo/pull/4185/files#r2399242679.
| partial class CryptoLib | ||
| { | ||
| [ContractMethod(Hardfork.HF_Gorgon, CpuFee = 1 << 19)] | ||
| public static byte[] Bn254Add(byte[] input) |
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.
We have a divergence from the CryptoLib interface. Existing BLS12-381 methods like bls12381Add and bls12381Mul work with InteropInterface which contains G1/G2/GT point instances under the hood:
| public static InteropInterface Bls12381Mul(InteropInterface x, byte[] mul, bool neg) |
I think compatibility with an existing CryptoLib interface is important, so it would be good to refactor BN254 methods to work with InteropInterface types instead of raw byte arrays. And we also should introduce bn254Serialize and bn254Deserialize for that.
Given this approach, user will deserialize input points once, then perform a set of operations, then serialize the result (also once per contract method). This approach is more effective when there's a set of BN254 operations (which is usually the case), we just won't perform deserialization/serialization on every BN254 operation.
|
Conflicts. |
i am waiting for erik to complete his refactoring. |
| } | ||
|
|
||
| [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 19, Name = "bn254_add")] | ||
| public static byte[] Bn254AddRaw(byte[] input) |
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.
Please, remove Bn254*Raw methods. Standard methods that work with InteropInterface are sufficient.
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.
I need @neo-project/core opinion on this issue. input here contains 2 serialized points (as in Ethereum, I suppose). To me, it's sufficient to have bn254Deserialize, bn254DeserializeEthereum and bn254Add(x, y) methods in CryptoLib API, and then input byte array in both Neo/Ethereum format may be parsed on the contract side using these methods, instead of exposing yet another bn254AddRaw from the CryptoLib.
Otherwise if we don't keep compatibility with the existing CryptoLib interface, it will turn into a mess.
| } | ||
|
|
||
| [ContractMethod(Hardfork.HF_Faun, CpuFee = 1 << 21)] | ||
| public static byte[] Bn254Pairing(Array 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.
This method should also return an InteropInterface.
Description
Proposal neo-project/proposals#205 . Adds a bn254 native contract implementation that mirrors the Solidity/EVM precompile interface.
The contract now routes elliptic curve add, scalar multiplication, and pairing through Nethermind’s
MCL bindings, handling Ethereum’s big-endian payload layout and zero-on-error semantics. Unit
coverage includes the canonical Ethereum GeneralStateTests pairing vectors to ensure cross-chain
compatibility. The native manifest snapshot was refreshed so genesis exposes the new entry points.
Fixes # (issue)
Type of change
expected)
How Has This Been Tested?
Test Configuration:
c67e485ff8b5be9abc8ad15345ec21aa22e290d9)
Checklist: