Skip to content

Conversation

@Jim8y
Copy link
Contributor

@Jim8y Jim8y commented Sep 22, 2025

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

  • Optimization (the change is only an optimization)
  • Style (the change is only a code style for better maintenance or standard purpose)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as
    expected)
  • This change requires a documentation update

How Has This Been Tested?

  • dotnet test tests/Neo.UnitTests/Neo.UnitTests.csproj --filter CryptoLib

Test Configuration:

  • OS: same as CI image
  • .NET SDK: 9.0.301
  • Tests reused Ethereum fixtures (GeneralStateTests/stZeroKnowledge/ecpairing_inputs.json, commit
    c67e485ff8b5be9abc8ad15345ec21aa22e290d9)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

@Jim8y Jim8y force-pushed the feature/bn254-curve-support branch from c796a25 to 3188ee3 Compare September 22, 2025 03:59
@cschuchardt88
Copy link
Member

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);
}

Copy link
Member

@AnnaShaleva AnnaShaleva left a 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.

@Wi1l-B0t
Copy link
Contributor

No progress?

@Jim8y
Copy link
Contributor Author

Jim8y commented Oct 23, 2025

I’ve reworked the BN254 native contract so it no
longer relies on Nethermind.MclBindings—the entire G1/G2 arithmetic and
Miller loop now live in BN254.Managed.* and are backed by BouncyCastle.
The wrapper in BN254.cs uses those managed routines, and Neo.csproj no
longer references the Nethermind package. I also updated the docs and tests to
reflect the EVM-style 32-byte success word, and the pairing vectors all pass
(dotnet test tests/Neo.UnitTests/Neo.UnitTests.csproj). Please take another
look.

@erikzhang
Copy link
Member

@neo-project/ngd-shanghai Need testing.

Copy link
Contributor

Copilot AI left a 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.

Comment on lines 43 to 44
if (input.Length % BN254.PairInputLength != 0)
throw new ArgumentException("Invalid BN254 pairing input length", nameof(input));
Copy link
Member

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

@neo-project neo-project deleted a comment from Copilot AI Oct 24, 2025
@txhsl
Copy link

txhsl commented Nov 5, 2025

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.

@shargon
Copy link
Member

shargon commented Nov 5, 2025

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?

@Jim8y Jim8y force-pushed the feature/bn254-curve-support branch from 654b38c to 3d61cbc Compare November 7, 2025 15:04
@Jim8y Jim8y changed the base branch from dev to master November 7, 2025 16:25
@Jim8y Jim8y force-pushed the feature/bn254-curve-support branch 2 times, most recently from ac43dc7 to b71f990 Compare November 7, 2025 17:07
@Jim8y Jim8y force-pushed the feature/bn254-curve-support branch from b71f990 to 9973b98 Compare November 7, 2025 17:08
@Jim8y
Copy link
Contributor Author

Jim8y commented Nov 8, 2025

@txhsl please check again

@Jim8y Jim8y added Port-to-3.x Feature or PR must be ported to Neo 3.x branch and removed Need Testing labels Nov 8, 2025
Copy link

@txhsl txhsl left a 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.

hasEffectivePair = true;

mclBnGT current = default;
Mcl.mclBn_pairing(ref current, g1, g2);
Copy link

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.

Copy link
Member

@AnnaShaleva AnnaShaleva left a 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)
Copy link
Member

@AnnaShaleva AnnaShaleva Nov 10, 2025

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.

@Wi1l-B0t
Copy link
Contributor

Conflicts.

@Jim8y
Copy link
Contributor Author

Jim8y commented Nov 13, 2025

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)
Copy link
Member

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.

Copy link
Member

@AnnaShaleva AnnaShaleva Nov 14, 2025

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)
Copy link
Member

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.

@Jim8y Jim8y changed the base branch from master to master-n3 November 14, 2025 09:23
@Jim8y Jim8y changed the base branch from master-n3 to master November 14, 2025 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Core Hardfork Port-to-3.x Feature or PR must be ported to Neo 3.x branch Waiting for Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants