Title: EIP_2537__BLS12_precompile__discussion_thread
Category: EIPs

Author: shamatar
Date: 2020-04-07T10:33:20.900Z

Reference thread from the EIP itself.
Resources:

spec until it’s accepted from PR
test vectors

Author: shemnon
Date: 2020-04-11T22:23:47.969Z

Doing a testing pass:

First, can you provide a test vector for pairings where in the pairing contract “Any of G1 or G2 points are not in the correct subgroup” is triggered?  All the test vectors are happy path.  Not on G1 or G2 is trivial to produce, subgroup is a bit more involved.  This will be needed for reference tests.
Second, does the padded space need to be zeros only?  i.e. is ff00000000000000000000000000000012196c5a43d69224d8713389285f26b98f86ee910ab3dd668e413738282003cc5b7357af9a7af54bb713d62255e80f560000000000000000000000000000000006ba8102bfbeea4416b710c73e8cce3032c31c6269c44906f8ac4f7874ce99fb17559992486528963884ce429a992fee a valid G1 point for the contract? (the first set of zeros has junk data in it)

Author: holiman
Date: 2020-06-22T15:43:02.553Z

Just saw that the EIP now has a section about " Prevention of DDoS on error handling" . Can someone explain what distributed denial of service attack can be carried out if we blow all gas on a subroutine error (like we do on all other types of errors, always?)

Author: shamatar
Date: 2020-06-23T12:36:09.139Z

Hello Martin.
EIP text actually will be updated because now (in latest commit in master) it reflects a “desired” behavior for calls to the precompile (only nominal cost is burned), but such desired behavior will not be implemented.
In any case, there is no DDoS vector if precompile burns all the gas of the current frame (what was sent along with a staticcall) and supplied gas is more than a gas cost from the schedule.

Author: shamatar
Date: 2020-06-23T12:39:37.093Z

Negative vectors are also available in 1962 repo (in “negative” folder), there are cases with invalid subgroup there.
Padding with zeroes should be enforced with ABI because field element is required to be “in the field”, so it must be strictly less than modulus, and if leading bytes are non-zero (and we use BE encoding) the element will be not “in a field”.

Author: jochem-brouwer
Date: 2020-07-01T16:48:09.334Z

Hey, we are implementing the multiExp precompiles at EthereumJS. The gas used is not very clear to me.
In the pairing precompile it is clear that if the input length is 0, or the length of the input is not of an expected length, then we use the base gas. However, what should this be if I input an empty byte to multiExp? 0 gas (this is what Geth does)? What if I input 1 byte (i.e. the length is not a multiple of 160 bytes). Should I also use 0 gas here? What if I input 161 bytes? Should I deduct the “corresponding gas” (1 pair) or also 0 gas? Geth seems to floor the division of the input (i.e. floor(len(input)/160)), calculate the gas, and then deduct this if the len(input) % 160 != 0. (To be clear: in this case thus more than 0 gas is deducted)

Author: shamatar
Date: 2020-07-05T13:04:17.187Z

Hello @jochem-brouwer
A PR with the clarification and explicit formulas for variable length cases is pending the bot approval at the moment, but you can still access the document here.
Sincerely, Alexander

Author: jochem-brouwer
Date: 2020-07-09T11:13:05.808Z

Thanks Alexander for the update!
I have a question about the encoding of Fp points. Sorry for my ignorance about this. Is the only check necessary for a Fp point to be on the curve that it is strictly less than the base field modulus? (I.e. < 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab).
Does the same apply for Fp2 points (c0 is less than the base field modulus)? Is there any requirement for the c1 point of Fp2?
Kind regards,
Jochem

Author: shamatar
Date: 2020-07-09T17:13:48.609Z

Field elements (either Fp or Fp2) in pairs for the affine coordinates (x, y) of the curve point. They must satisfy the equation like y^2 = x^3 + b for the BLS12-381 curve (b coefficient can be found in the EIP itself). Then point is “on curve” and this check is always performed during deserialization. c0 and c1 elements of Fp2 element are just Fp elements, so all corresponding encoding rules apply.

Author: jochem-brouwer
Date: 2020-07-15T15:16:06.366Z

The input to the mapToG1 precompile takes 64 bytes (top 16 bytes are zero). This is a single number. I don’t see how I can then check if they satisfy that equation? Shouldn’t this strictly less check as mentioned before be the sole check?

Author: shamatar
Date: 2020-07-16T11:00:56.997Z

mapToG1 takes as an input a single Fp element (not a curve point) and outputs a valid curve point. So the only check here is that input element must be “in the field” (properly encoded).

Author: jochem-brouwer
Date: 2020-07-28T08:09:02.687Z

Hey @shamatar, I just had a discussion with @MariusVanDerWijden on discord about this particular test.
Geth currently outputs a zero point of this multiexp call. However, to me it seems that per the EIP, these zero points are “not on curve” and thus should throw an error instead of returning the zero point (infinity point (?)) as output. See the pic below from the EIP which is the part which confuses me. Should thus the precompile yield an output and thus make the call succeed (current implementation) or should it throw? I think by the EIP it should throw?
image789×132 15.5 KB

Author: shamatar
Date: 2020-07-28T19:23:15.037Z

Hey @jochem-brouwer
Well, it’s only a wording problem, that’s why I’ve called it “convention”. On BLS12-381 point of infinity is “virtual”, in a sense that there is no point O with some coordinates (x,y) that is on curve and that for any point P one has P + O = P. Do achieve necessary functionality nevertheless this EIP follows the BN254 one where “point of infinity” is encoded as (0,0) for both input and output purposes (it’s not that hardly needed for inputs cause it can be filtered out in Solidity if really necessary, but valid outputs can be points of infinity).

Author: jannikluhn
Date: 2020-10-27T15:39:07.553Z

Both this EIP and the existing BN254 precompile only allow performing pairing checks, not evaluations (get the result C = e(A, B) for further processing). What’s the reason for this?

Author: shamatar
Date: 2020-10-27T16:43:38.676Z

It’s kind of no point to output Gt (Fp12) element in plain text we also don’t give the Fp12 arithmetic operations. But such addition would make this (already quite large) proposal really enormous.

Author: poojaranjan
Date: 2021-01-20T04:34:15.034Z

EIP-2537 explained in five slides by Alex Vlasov - https://youtu.be/al4YpfDVmS4

Author: wbl
Date: 2022-01-06T17:14:38.231Z

I’m interested in seeing this proposal move forward, and I’m rather new to the community. Right now it is listed as stagnant despite being implemented in go-ethereum.

Author: ralexstokes
Date: 2022-01-06T22:49:23.783Z

me (and many others) are also interested in BLS precompiles in the EVM!
we made a lot of good progress but there was some hesitation around this EIP as it adds a lot of very complex functionality to the EVM (in the form of BLS arithmetic) and it wasn’t super clear at the time it delivered enough value to justify going into a crowded upgrade schedule.
so activity around it has slowed and we are in a bit of limbo as development resources at the moment are nearly exclusively focused on the merge.
this EIP could conceivably go into the upcoming execution layer upgrade Shanghai but this will likely be the second half of 2022 at the earliest. this being said it facilitates a lot of use cases around scaling rollups with sharding so should become very important very quickly once we deploy the merge.
it is on my personal bucket list to track post-merge so hopefully we see BLS precompiles sometime soon 
edit: may see some progress here: Proposal to add EIP-2537 (BLS Precompile) to Shanghai · Issue #343 · ethereum/pm · GitHub

Author: kladkogex
Date: 2022-01-17T14:03:23.286Z

We have been waiting for this for sooo long … It is amazing it is taking so much time.

Author: ralexstokes
Date: 2022-01-18T00:36:54.214Z

patience is rewarded

Author: PeterCCLiu
Date: 2022-05-25T16:46:40.767Z

Quick update in May 2022:
Summary: The implementation of EIP-2537 has been completed and is now in go-ethereum codebase— it is only pending activation of this functionality.
Next steps: Enable the precompiles by editing /core/vm/contracts.go, similar to what is done for Byzantine/Istanbul/Berlin:

Create a new default set of pre-compiled Ethereum contracts used in the next release, including the new precompiles of EIP-2537.
Modify init() and ActivePrecompiles() functions to activate the new precompiles.

Other points to mention: There exist another implementation that is marginally more performant regards to gas consumption and running time (blst utilizing wasm). But the workload to move on to the other implementation is high and discarding the past hard work is unworthy.

Author: bbuddha
Date: 2022-08-19T06:38:37.406Z

Does this EIP also add support for hash to curve operations used for BLS signatures? Specifically those used on the beacon chain…

Author: QEDK
Date: 2022-09-19T21:04:02.075Z

@shamatar I think this EIP should be tagged with #shanghai-candidate?

Author: shamatar
Date: 2022-09-20T12:04:53.609Z

Sure! Done now
Need 20 symbols…

Author: cyanface
Date: 2022-10-26T05:02:26.884Z

so what is the result? it’s not included in shanghai?

Author: ralexstokes
Date: 2022-10-26T17:10:55.811Z

we will discuss on the next ACD if there is time
there is a lot to squeeze into Shanghai so keep that in mind – many pieces to prioritize and everything needs to take its own time to get to mainnet

Author: vethereum
Date: 2022-10-28T09:13:02.975Z

Hey guys. This pre-compile is really important and under-rated. If it can be discussed on ACD that would be amazing. Thanks so much

Author: JustinZal
Date: 2022-10-28T09:14:16.366Z

Any effort to prioritize this EIP in the Shanghai would be greatly appreciated. I believe this would benefit a lot of protocols since a lot of data relating to consensus layer would be verifiable on-chain.

Author: ralexstokes
Date: 2022-10-28T15:56:34.914Z

@vethereum @JustinZal thanks for chiming in, adding your support here makes community demand for this EIP more legible and is helpful when client developers are prioritizing what to include in Ethereum upgrades.
we didn’t have time on yesterday’s ACD but it is on the agenda for the next one

Author: jtguibas
Date: 2022-10-28T21:46:18.837Z

@vethereum @JustinZal While the BLS precompile would make verifying data relating to the consensus layer easier, my team and I at Succinct have actually been able to get around this by verifying BLS signatures inside a zkSNARK and then verifying that proof on-chain.
Using this technique, we have created a gas-efficient trust-minimized light client for Ethereum which can be deployed on any execution layer that supports the BN254 curve (Proof of Consensus Bridging between Ethereum and Gnosis Chain | Succinct Labs Blog).
Inside our smart contract, we have a zkBLSVerify function which can be called to emulate the BLS precompile. The tradeoff is that a proof must be generated off-chain (< 2 minutes). The function below is taken from our smart contract (code here), so it’s specific to our use case, but it can very easily be generalized to any other application that requires the precompile.
function zkBLSVerify(bytes32 signingRoot, bytes32 syncCommitteeRoot, uint256 claimedParticipation, Groth16Proof memory proof) internal view returns (bool) {
        require(sszToPoseidon[syncCommitteeRoot] != 0, "Must map SSZ commitment to Posedion commitment");
        uint256[34] memory inputs;
        inputs[0] = claimedParticipation;
        inputs[1] = uint256(sszToPoseidon[syncCommitteeRoot]);
        uint256 signingRootNumeric = uint256(signingRoot);
        for (uint256 i = 0; i < 32; i++) {
            inputs[(32 - 1 - i) + 2] = signingRootNumeric % 2 ** 8;
            signingRootNumeric = signingRootNumeric / 2**8;
        }
        return verifySignatureProof(proof.a, proof.b, proof.c, inputs);
    }

If the BLS precompile is a blocker for an exciting application you have in mind, please let us know and we would be happy to help out (just email us at hello@succinct.xyz).

Author: weikengchen
Date: 2022-10-29T02:00:13.436Z

Agreed with @jtguibas. Without a precompile contract for BLS curve, verifying the signature can be difficult. By the way, when we talk about BLS signature, it is BLS12-381 BLS signatures right? (The two BLS are different).

Author: QEDK
Date: 2022-12-09T08:36:38.615Z

I assume at this point, it’s safe to assume that this will not be a part of the Shanghai upgrade?

Author: matt
Date: 2022-12-09T16:22:15.181Z

Yes, safe to assume at this point.

Author: dankrad
Date: 2023-01-19T10:53:39.432Z

I was wondering if there is a motivation for not including compression/decompression in the EIP? Is it because providing uncompressed elements is cheaper at 16 gas/byte?
In that case, it may be that we need to reconsider this as for rollups this ratio will be different and data will be much more expensive as compared to computation. Since the desire of many rollups is to stay as close as possible to Ethereum L1, maybe we should add precompiles for decompression?

Author: shamatar
Date: 2023-01-19T20:55:58.318Z

The reason to not include decompression is indeed the cost (on L1 ). There was already large scope in this EIP and so G1 and G2 decompressions were skipped.

Author: jeljelih
Date: 2023-04-26T05:48:50.907Z

Does anyone have an idea when this EIP can move to next stage? Do you think we can have BLS precompiles in 2023?

Author: darcys22
Date: 2023-06-21T01:07:46.307Z

Excited for this to get into a hardfork soon!

Author: shemnon
Date: 2023-07-23T00:17:03.726Z

It will not be in a 2023 hard fork.  No one on an ACD call was there to champion it during late scoping calls.

Author: CluEleSsUK
Date: 2023-07-26T15:04:32.443Z

Late to the party, but I’d like to express interest in the BLS12-381 precompiles for a variety of use cases it could open up on-chain:

registration of distributed validators
consumption and verification of drand randomness beacons on-chain (a threshold network for generating randomness, an alternative to RANDAO)
identity-based decryption (such as timelock decryption)

As another commenter noted, it would be amazing to have hash to curve and hash to field implemented as well.

Author: Dire-0x
Date: 2023-07-26T23:31:35.145Z

What’s the path to get this activated?
Also very interested in its use to utilize drand beacons onchain.

Author: _pm
Date: 2023-08-07T16:48:47.153Z

I understand there are no champions for the eip, but the situation is really bad. The operations are already present in consensus-layer clients, running on every node.
Right now, all zk apps are using bn254, which has terrible security level - much less than 128 bits.
At some point it would be, or already is - possible to forge proofs for such protocols.
Developing zk apps takes longer when compared to ordinary contracts, which means the sooner this is deployed, the better. Apps, especially ones without ownership, would not be able to easily upgrade from bn.

Author: xinbenlv
Date: 2023-08-08T21:18:29.457Z

Oh, sad. It seems another chicken-and-egg problem for requesting a precompile!
It seems a pattern that whenever a precompile is proposed, there will be a lot of debate about whether there is likely to be a lot of usage. The chicken-and-egg nature is that without deploying the precompile, we can’t calculate the adoption, and without the adoption, it’s hard to argue for making it a precompile and thus lack of interest
Good news, we are propose to have a “progressive path towards precompiles”, where author proposes a precompile, but instead of requiring it to be deployed as precompile first, they deploy a precompile candidate which is a traditional smart contract (with higher gas cost per operation, though), and see if there is usage. Once the usage is validated, the data can be used to (1) justify the need for such precompile, and (2) serve as call pattern to help client implementor tune performance
See more Progressive precompiles via CREATE2 shadowing

Author: _pm
Date: 2023-08-18T11:31:40.684Z

I don’t think BLS pairings can be implemented in pure solidity. They’re very heavy.

Author: xinbenlv
Date: 2023-08-24T14:07:19.798Z

Has anyone evaluated the gas used if using solidity to impl BLS?

Author: lei335
Date: 2023-09-12T10:17:13.932Z

Just the G1 neg will use about 4956 gas, you can image the pairing will use how many gas.

Author: QEDK
Date: 2023-11-09T09:33:01.363Z

I can see that the EIP needs a champion to take it through, hate to ask the obvious but do any of the original EIP authors intend to take on that role (courtesy ping to @shamatar @ralexstokes), failing that, could we have a long-standing community member take on that role (maybe @axic @matt @maxsam4), failing that, I would be happy to take on that role.

Author: ralexstokes
Date: 2023-11-09T11:11:06.063Z

hi @QEDK ! I intend to push for inclusion of this EIP in the next hard fork after dencun
the core dev process hasn’t really focused on planning this next hard fork yet so there hasn’t been much activity as we all focus on the deneb/cancun EIPs

Author: QEDK
Date: 2023-11-09T13:02:45.115Z

that’s great to hear, hopefully we can get this included in the fork after Dencun! Let me (or us at Avail) know if we can help speed up the process.

Author: developeruche
Date: 2023-11-26T20:51:04.271Z

I just Implemented a KZG poly-commit to replace a Merkle Tree system though the interpolation, commitment, and proof generation were done in Rust, coming to write the verification contract now and just seeing this precompile has not been included yet

Author: ralexstokes
Date: 2023-11-28T23:42:34.493Z

@shamatar can you remove the shanghai-candidate tag and add the prague-candidate  tag?
I think we are at the point where this EIP will finally ship

Author: CluEleSsUK
Date: 2023-12-06T16:41:02.537Z

woohooo! any word on whether hash_to_curve will be included?
I see there’s an EIP for hash_to_curve for BN254 (EIP-3068: Precompile for BN256 HashToCurve Algorithms), but it seems to have gone stale.
Trying to use BN254 has proved pretty fruitless without hash_to_curve and I’d be happy to help make it happen

Author: JayWhite2357
Date: 2024-01-09T21:02:55.053Z

Glad to hear it’s finally getting in!
Is there any reason not to add BLS12_GTADD, BLS12_GTMUL, and BLS12_GTMULTIEXP as well? Additionally, why not have both BLS12_PAIRING_RESULT which returns the actual pairing result and BLS12_PAIRING which checks that the pairing gives the identity. (Even SUB may be wise to add, but it’s non-essential.)
All of these are a rather trivial addition when it comes to implementation details, and this is the logical time to add them rather than waiting another 3 years or so. Personally, I want it, and it feels like the right decision to have actually full-blown support rather than just partial support for the curves.

Author: AnomalRoil
Date: 2024-01-15T12:33:27.686Z

@ralexstokes It’s great to see we might finally get this EIP in!
A few comments:

it might be worth considering updating its status from “Stagnant” back to “Review”, as mentioned here. Only you or another author can do it as per EIP-1:

Stagnant - Any EIP in Draft or Review or Last Call if inactive for a period of 6 months or greater is moved to Stagnant. An EIP may be resurrected from this state by Authors or EIP Editors through moving it back to Draft or it’s earlier status. If not resurrected, a proposal may stay forever in this status.


There is a PR currently open around it to correct gas costs to match the existing ones found in geth. It requires an author’s approval too. Current gas costs were set based on a Rust implementation, IIUC, but it seem the Go one is a bit slower.
I believe the EIP should explicitly say that the BLS12_MAP_FP_TO_G1 and BLS12_MAP_FP2_TO_G2 precompiles are clearing the cofactor (I know it’s specified in its own document, but having it in the main EIP would make it clearer).

Finally, given there’s both BLS12_MAP_FP_TO_G1 and BLS12_MAP_FP2_TO_G2 precompiles in this EIP, and I see it says:

Mapping function does NOT perform mapping of the byte string into field element (as it can be implemented in many different ways and can be efficiently performed in EVM), but only does field arithmetic to map field element into curve point.

It might be worth providing an example solidity hash_to_field as well to showcase it’s actually efficiently performed in EVM? Does anyone have one?
Because I’d expect most users to have to rely on hash_to_curve rather than just map_to_curve.

Author: marchhill
Date: 2024-01-19T15:14:07.672Z

@CluEleSsUK @JayWhite2357 I agree these changes could improve the UX for the precompile and allow more applications. To summarise the suggestions:
Implementing hash_to_curve as specified in the RFC standard

Currently only map_to_curve is supported, for most applications this would be used to implement hash_to_curve
Encourages use of the correct hashing technique, rather than the more obvious but less secure method to hash and multiply by the generator (not suitable in the random oracle model)
Standardises the hashing technique (and therefore BLS signatures) used in contracts
Should implement the standard suites

Introduce a new operation BLS12_PAIRING and change the existing operation with that name to BLS12_PAIRING_VERIFY, and add operations on the group GT: BLS12_GTADD, BLS12_GTMUL, BLS12_GTMULTIEXP

Currently the pairing operation can only be used to verify if results in GT are equal, this is useul for BLS signatures
The new version of BLS12_PAIRING would return the element of GT
Supporting operations on GT would increase the applications of the precompile beyond signatures
This functionality is useful for identity based encryption (Boneh-Franklin scheme), attribute-based encryption, functional encryption

Author: daira
Date: 2024-02-07T22:20:58.056Z

Glad to see progress on this! +1 for including hash_to_curve and $\mathbb{G}_T$ operations.

Author: flyq
Date: 2024-02-16T08:16:25.656Z

developeruche:

I just Implemented a KZG poly-commit to replace a Merkle Tree system though the interpolation, commitment, and proof generation were done in Rust, coming to write the verification contract now and just seeing this precompile has not been included yet 


You can use BN254 first, it is included in precompile.  
example:

github.com/flyq/Ethereal


src/Pairing.sol

master


      
                  }
              }
              require(success, "pairing-mul-failed");
          }
          
          /* @return The result of computing the pairing check
           *         e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
           *         For example,
           *         pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
           */
          function pairing(
              G1Point memory a1,
              G2Point memory a2,
              G1Point memory b1,
              G2Point memory b2
          ) internal view returns (bool) {
              G1Point[2] memory p1 = [a1, b1];
              G2Point[2] memory p2 = [a2, b2];
          
              uint256 inputSize = 12;
              uint256[] memory input = new uint256[](inputSize);

Author: developeruche
Date: 2024-02-16T08:37:33.180Z

Thank you very much @flyq, this weekend would be productive…
Your code is lit btw

Author: ethyla
Date: 2024-03-26T21:34:09.701Z

Hi everyone, I saw @ralexstokes tweet on the hash to curve matter a few weeks ago and started an implementation here: GitHub - ethyla/bls12-381-hash-to-curve
Hash to field is already working nicely and passes all tests from the rfc. Costs currently 30.000 - 45.000 gas so could be called “efficient”.
Two issues remain. I can’t test the hash to curve implementation as foundry doesn’t implement the precompiles yet and I found no simple way to setup a local node with them activated to test using hardhat.
Also I can’t clear the cofactor yet for the g2 implementation, as the scalar I need to multiply by is way to big for the precompile. I started looking into the Budroni-Pintore optimization, but I fear it will be very expensive gas wise.

Author: shamatar
Date: 2024-03-27T13:46:40.192Z

EIP 2537 is defined to clear a cofactor for field-to-curve mappings

Author: ralexstokes
Date: 2024-04-04T23:16:42.088Z

Hi all, there was some request above for exposing the BLS12-381 pairing product, and after some discussion with core devs and others, I’m currently leaning towards leaving them out.
Please take a look here with my summary/rationale and please post here or there if I’m missing something: Update EIP-2537: rename PAIRING to PAIRING_CHECK; introduce PAIRING_PRODUCT precomiple by zhenfeizhang · Pull Request #8309 · ethereum/EIPs · GitHub

Author: mratsim
Date: 2024-04-06T13:09:06.570Z

I’m currently adding EIP-2537 support to Constantine and at the same-time providing metering for the worst-case scenario using the optimized constant-time routines and detailed operation counts.
See benchmarks: EIP-2537 - BLS12-381 precompiles for the EVM by mratsim · Pull Request #368 · mratsim/constantine · GitHub
image527×223 15 KB
and detailed operation counts https://github.com/mratsim/constantine/blob/eip2537/metering/eip2537.md#metering (note: the metering slows down execution by ~5x)
However, while subgroup checks are mandatory for pairings, they are not for:

elliptic curve addition
elliptic curve multiplication
multiscalar multiplication (MSM)

For addition, this is not a problem for Jacobian coordinates, however if using Projective coordinates, in particular the complete formulas from Complete addition formulas for prime order elliptic curves there might be an assumption of being in the prime order subgroup. This needs test vectors with a point not in subgroup.
For multiplication, endomorphism acceleration is widely used in BLS12-381 libraries, for a minimum of ~30% speedup. It will give wrong result if the point is not in the correct subgroup.
The spec needs to spell out that implementation should not use endomorphism acceleration.
(Or that endomorphism acceleration is mandatory, and a test should be done to check that all implementations, give the same wrong result.)
This needs test vectors with a point not in subgroup.
For multi-scalar multiplication, we will have the same issue as scalar multiplication. This needs test vectors with points not in subgroup.
To be clear because 33% one way is 50% another, it’s 50us vs 75us per operation on my machine.
image1668×760 266 KB
As an aside, the EIP should pick either the additive notation or the multiplicative notation but not mix both, i.e. replace the name “multiexponentiation” with the “multi-scalar-multiplication” (MSM).
I can open 2 PRs for what I think is the sensible choice:

Do not use endomorphism acceleration.
Rename to multi-scalar-multiplication.
edit: PR open Update EIP-2537: Rename multiexponentiation to MSM by mratsim · Pull Request #8393 · ethereum/EIPs · GitHub

Tagging @asanso re test vectors.

Author: ethyla
Date: 2024-04-07T13:45:34.263Z

You saved me a lot of wasted time, thanks.
I picked it up again this week and now have a working implementation. It passes the rfc tests against a modified geth.
Though some more cleanup and optimizations are needed.

Author: asanso
Date: 2024-04-08T09:41:14.760Z

Thanks a lot @mratsim  for the great analysis.
W,r.t the subgroup check I totally agree with you about the GLV optimization.
Now my take would be to enforce subgroup check for all the operations (see  PR 8322).
The analysis done by @MariusVanDerWijden shows though that this would not be possible for BLS12_G1ADD and BLS12_G2ADD (too expensive) but feasible for the other operations.
Now it might be a bit “ugly” to force subgroup check for all but BLS12_G1ADD and BLS12_G2ADD but IMHO it’s the best way to go. Thoughts ?

Author: mratsim
Date: 2024-04-10T09:23:53.490Z

I’ll split my reply into an analysis and factual part and then my own opinion piece, then open up on something related to EIP-7667 on gas cost.
Analysis
I see 3 different paths:

No change approach. no subgroup check, price like double-and-add and Pippenger today and add test vectors. For MSMs, in both Gnark and Constantine, endomorphism acceleration was not a problem or was even slower due to probably decomposition overhead + memory bandwidth / hardware cache limitations (at least on x86, Apple memory likely behaves differently).
Misuse resistance approach. See @asanso, enforce subgroup check except on addition.
Modular approach or performance approach, provide subgroup_check and clear_cofactor as separate primitives. People can cache subgroup_checks and ensure their cost is only paid once. Issue: they need to learn what a subgroup and cofactor is.

Now, for a more informed decision, we’re okay with the pricing of regular scalar-multiplication as double-and-add, but is endomorphism acceleration important for MSM?
When optimizing for G1, on my machines beyond 2^10 points (1024), endomorphisms were slower and so are deactivated: constantine/constantine/math/elliptic/ec_multi_scalar_mul.nim at 976c8bb215a3f0b21ce3d05f894eb506072a6285 · mratsim/constantine · GitHub
One extra remark is that on G1, endomorphism cut the scalar size from 255 bits to 128 bits. On G2, you cut the scalar size from 255 bit to 64 bit, further benchmarks confirm that similarly, there is a threshold, where endomorphism acceleration doesn’t really help.
With endomorphism:
spectacle_20240410_1008421920×1245 391 KB
spectacle_20240410_1009451920×1252 397 KB
No endomorphism:
spectacle_20240410_1013451920×1238 389 KB
spectacle_20240410_1014531920×1241 398 KB
Opinion
1 If not properly tested, the “no change” approach is likely to lead to state split if people start using point on curve but not in subgroup. If properly tested, the only difficulty is if some libraries do not allow selection of scalar multiplication algorithm and use endomorphism by default.
3 is similar to 1 in the happy path. In the unhappy path, will users respect a “Before calling the scalar-mul precompile, input MUST be subgroup-checked or coming from clear_cofactor or mapping_to_curve)”? If not, libraries can’t use the endomorphism accelerated path and we might as well use 1.
2 sounds like the reasonable choice.
Source code: constantine/constantine/math/elliptic/ec_multi_scalar_mul.nim at 976c8bb215a3f0b21ce3d05f894eb506072a6285 · mratsim/constantine · GitHub
Now for pricing the subgroup checks, I’ll use Scott, https://eprint.iacr.org/2021/1130.pdf, but there is a later note from El Housni:

G1: P is in the 𝔾1 subgroup iff ϕ(P) == -u², ϕ is cubic root of unity endomorphism and costs 1 fp_mul, u is the curve parameter and is 64-bit with low hamming weight, hence about ~130 fp_muls for subgroup checks. A 255-bit double-and-add is on average 255+127.5 fp_mul so the ratio subgroup_check/scalar_mul is about 1/3.
G2: P is in the 𝔾2 subgroup iff ψ(P) == u ψ is Frobenius endomorphism and costs 1 fp2_mul, u is the curve parameter and is 64-bit with low hamming weight, hence about ~70 fp2_muls for subgroup checks. A 255-bit double-and-add is on average 255+127.5 fp_mul so the ratio is about 1/5.

El Housni latest paper on subgroup checks:

On subgroup checks for BLS12 - HackMD
https://eprint.iacr.org/2022/352.pdf
https://members.loria.fr/AGuillevic/files/talks/22-Aarhus-Crypto-Day.pdf

Gas costs
Better discussed here probably: EIP-7667: Raise gas costs of hash functions - #6 by mratsim
but here is the gist, some operations are very expensive in zkEVMs and zkVMs like hash functions, and the Verge roadmap endgame is having a snarkified EVM (text: Annotated Ethereum Roadmap and https://twitter.com/VitalikButerin/status/1741190491578810445)
image1286×1282 169 KB
So EIP-7667 proposes to raise gas cost of hash functions. But should BN254 and BLS12-381 also reflect that?

Author: shamatar
Date: 2024-04-10T09:45:05.335Z

I think decision on whether to enforce or not subgroup checks should be biased on the use modes, namely “is there any use case that doesn’t eventually want to have everything in the main subgroup?”. So far it looks like if we do clear cofactors in “map to curve” implementations (both fp → G1 and fp_2 → G2) then all the cases would be happy to work with points in the subgroup.
So, at this moment I would be 99% for inclusion of subgroup checks into all relevant precompiles (so everything except additions and map-to-curve). Also, it’ll eventually someone from mistake or misuse.

Author: mratsim
Date: 2024-04-10T13:15:48.240Z

The only legitimate use-case I have for no subgroup check is if several consecutive operations are needed and the subgroup check cost can only be paid once.
For example, verifying KZG commitments would incur that: constantine/constantine/commitments/kzg_polynomial_commitments.nim at 976c8bb215a3f0b21ce3d05f894eb506072a6285 · mratsim/constantine · GitHub.
But this is only a matter of gas pricing.
The non-legitimate use-case would be malicious:

double-spend attacks on a on-chain BLS signature protocol like monero: Disclosure of a Major Bug in CryptoNote Based Currencies | Monero - secure, private, untraceable,
or causing a chain split if some clients use endomorphisms and get the wrong result on a point not in the proper subgroup.

Author: mratsim
Date: 2024-04-10T13:18:57.109Z

Some benches from Rollcall discussion
image1280×425 109 KB
gas costs in ratio of G1 scalarmul
original: eip2537_constantine_bench.csv · GitHub
image1280×520 136 KB
Reproduction
git clone https://github.com/mratsim/constantine
cd constantine
git checkout eip2537
CC=clang nimble bench_eip2537_subgroup_checks_impact

Author: asanso
Date: 2024-04-15T13:31:46.341Z

One other things that came out chatting with @dankrad  is this. `POINT_EVALUATION_PRECOMPILE_GAS in EIP-4844 is about 50000. Now the POINT_EVALUATION_PRECOMPILE is about 2 pairings. But if we do the equivalent of 2 pairings in EIP-2537 is 151000. Isn’t it really inconsistent?
Thoughts @mratsim @shamatar @ralexstokes ?

Author: mratsim
Date: 2024-04-18T09:19:12.487Z

In my noisy bench (browser, discord, telegram running), I can confirm your observations.
This is the details of operation cost for BLS12-381 primitives
image1694×1087 305 KB
And this is for EIP-4844:
spectacle_20240418_1046531515×1171 241 KB
Pairing is 415us, verify_kzg_proof is 779us.
verify_kzg_proof is indeed a 2-way multi-pairing: constantine/constantine/ethereum_eip4844_kzg.nim at 976c8bb215a3f0b21ce3d05f894eb506072a6285 · mratsim/constantine · GitHub
image1233×741 140 KB
2*415/779 = 1.065x hence I concur that having a ratio of 3 between both is way to big of a difference.
However, if we focus at multipairing closely in Constantine, a dedicated multi-pairing function grows much slower.
image1477×888 215 KB
It grows by about 120us per extra pair so ~30%.
Note: every 8 pairs, there is a slight cost bump as my multi-pairing batch size is 8
That said, in my analysis the cost bump is less than 0.1% per pairing

github.com/mratsim/constantine


constantine/math/pairings/miller_accumulators.nim

976c8bb


      
          const MillerAccumMax = 8
          # Max buffer size before triggering a Miller Loop.
          # Assuming pairing costs 100, with 50 for Miller Loop and 50 for Final exponentiation.
          #
          # N unbatched pairings would cost              N*100
          # N maximally batched pairings would cost      N*50 + 50
          # N AccumMax batched pairings would cost       N*50 + N/MillerAccumMax*(Fpᵏ mul) + 50
          #
          # Fpᵏ mul costs 0.7% of a Miller Loop and so is negligeable.
          # By choosing AccumMax = 8, we amortized the cost to below 0.1% per pairing.
      
    





I can do a repricing proposal based on 30 Mgas/s on my PC.
If wanted I can also compare to BN254 precompile.

Author: mratsim
Date: 2024-04-18T09:58:01.350Z

here is a repricing, using the number above.
Disclaimers:

CPU is Ryzen Pro 7840U, mobile 8-core CPU. The low-power version of their flagship 7840HS. It’s a 15W to 30W CPU (while flagship can go up to 54W): https://www.amd.com/fr/products/apu/amd-ryzen-7-pro-7840u
Turbo was not disabled
Library is Constantine, it has significant optimizations that do not always exist in BLST, Gnark, arkworks, bellman, …
Unless noted, library is constant-time, meaning perf is always worst-case.
For vartime scalarmul operation about half the bits are set (random initialization) while the spec use the worst-case all-bits set.
The raw cryptographic timing are done and do not account for overhead of converting from EVM ABI to internal ABI, checking that a number is lower than the modulus, etc …





Operation + Algorithm
Curve
Throughput (ops/s)
Gas cost (target 30 Mgas/s)
Latency (ns/op)
CPU cycle count (approx)




EC Add         G1
EC_Prj[Fp[BLS12_381]]
2732240.437
10.98
366
1206


EC Add vartime G1
EC_Prj[Fp[BLS12_381]]
4975124.378
6.03
201
664


EC Double G1
EC_Prj[Fp[BLS12_381]]
4219409.283
7.11
237
781


EC Add         G1
EC_Jac[Fp[BLS12_381]]
2188183.807
13.71
457
1507


EC Add vartime G1
EC_Jac[Fp[BLS12_381]]
5780346.821
5.19
173
572


EC Double G1
EC_Jac[Fp[BLS12_381]]
4566210.046
6.57
219
723


EC Add         G2
EC_Prj[Fp2[BLS12_381]]
943396.226
31.80
1060
3494


EC Add vartime G2
EC_Prj[Fp2[BLS12_381]]
1869158.879
16.05
535
1763


EC Double G2
EC_Prj[Fp2[BLS12_381]]
1564945.227
19.17
639
2107


EC Add         G2
EC_Jac[Fp2[BLS12_381]]
810372.771
37.02
1234
4066


EC Add vartime G2
EC_Jac[Fp2[BLS12_381]]
2207505.519
13.59
453
1493


EC Double G2
EC_Jac[Fp2[BLS12_381]]
2000000
15.00
500
1648


EC ScalarMul         255-bit G1
EC_Prj[Fp[BLS12_381]]
16451.427
1 823.55
60785
200212


EC ScalarMul vartime 255-bit G1
EC_Prj[Fp[BLS12_381]]
19455.253
1 542.00
51400
169301


EC ScalarMul         255-bit G1
EC_Jac[Fp[BLS12_381]]
16266.511
1 844.28
61476
202489


EC ScalarMul vartime 255-bit G1
EC_Jac[Fp[BLS12_381]]
20193.861
1 485.60
49520
163106


EC ScalarMul         255-bit G2
EC_Prj[Fp2[BLS12_381]]
8268.768
3 628.11
120937
398339


EC ScalarMul vartime 255-bit G2
EC_Prj[Fp2[BLS12_381]]
9304.836
3 224.13
107471
353983


EC ScalarMul         255-bit G2
EC_Jac[Fp2[BLS12_381]]
8717.332
3 441.42
114714
377842


EC ScalarMul vartime 255-bit G2
EC_Jac[Fp2[BLS12_381]]
10393.498
2 886.42
96214
316905


Miller Loop BLS12
BLS12_381
5241.695
5 723.34
190778
628376


Final Exponentiation BLS12
BLS12_381
4379.089
6 850.74
228358
752155


Pairing BLS12
BLS12_381
2408.223
12 457.32
415244
1367710


Hash to G1 (SSWU - Draft #14)
BLS12_381
23078.698
1 299.90
43330
142721


Hash to G2 (SSWU - Draft #14)
BLS12_381
7504.015
3 997.86
133262
438933


Subgroup check
EC_Jac[Fp[BLS12_381]]
30690.851
977.49
32583
107323


Subgroup check
EC_Jac[Fp2[BLS12_381]]
26205.451
1 144.80
38160
125690


Miller Loop BLS12
BLS12_381
5222.423
5 744.46
191482
630693


Final Exponentiation BLS12
BLS12_381
4385.138
6 841.29
228043
751118


Pairing BLS12
BLS12_381
2408.269
12 457.08
415236
1367683


Pairing BLS12 batched:     1
BLS12_381
2408.014
12 458.40
415280
1367824


Pairing BLS12 non-batched: 2
BLS12_381
1200.394
24 991.79
833060
2743882


Pairing BLS12 batched:     2
BLS12_381
1882.654
15 934.95
531165
1749524


Pairing BLS12 non-batched: 3
BLS12_381
801.33
37 437.76
1247925
4110348


Pairing BLS12 batched:     3
BLS12_381
1530.618
19 599.93
653331
2151906


Pairing BLS12 batched:     4
BLS12_381
1301.712
23 046.57
768219
2530312


Pairing BLS12 batched:     5
BLS12_381
1124.613
26 675.84
889195
2928783


Pairing BLS12 batched:     6
BLS12_381
992.763
30 218.69
1007290
3317752


Pairing BLS12 batched:     7
BLS12_381
885.784
33 868.30
1128943
3718451


Pairing BLS12 batched:     8
BLS12_381
804.54
37 288.39
1242946
4093950


verify_kzg_proof (EIP-4844)
BLS12_381 serial
1284.248
23 359.97
778666
2564722

Author: mratsim
Date: 2024-04-18T10:41:53.434Z

Now the gas price I suggest assuming we want to allow mobile CPUs from 10 years ago to be competitive.
Old benchmark of Constantine on i5-5257U (mobile Broadwell 2.7GHz without ADCX, ADOX instructions for bigint): Multipairing by mratsim · Pull Request #165 · mratsim/constantine · GitHub
image1055×723 180 KB
The CPU is about 2.5x slower (single-threaded) than 7840U with Constantine. We assume that all modern BLS12-381 backend are within 20% of Constantine, so multiply ideal gas cost on 7840U by 3 and round up generously. Also we use the constant-time jacobian coordinates as reference as every library but Constantine, Halo2 and Icicle use Jacobian coordinates.
For pairing cost, the formula used is:
<Miller Loop> * k * 0.8 + <Final exponentiation>, the 0.8 is due to acceleration tricks when batching multiple miller loops.




Operation + Algorithm
Curve
Throughput (ops/s)
Gas cost (target 30 Mgas/s)
Current gas
Suggested gas (CPU 2015, 2.5x slower, framework 20% slower, => x3)
Subgroup checks cost
Total gas cost




EC Add         G1
Prj G1
2732240.437
10.98






EC Add vartime G1
Prj G1
4975124.378
6.03






EC Double G1
Prj G1
4219409.283
7.11






EC Add         G1
Jac G1
2188183.807
13.71
500
40

40


EC Add vartime G1
Jac G1
5780346.821
5.19






EC Double G1
Jac G1
4566210.046
6.57






EC Add         G2
Prj G2
943396.226
31.80






EC Add vartime G2
Prj G2
1869158.879
16.05






EC Double G2
Prj G2
1564945.227
19.17






EC Add         G2
Jac G2
810372.771
37.02
800
110

110


EC Add vartime G2
Jac G2
2207505.519
13.59






EC Double G2
Jac G2
2000000
15.00






Subgroup check
Jac G1
16451.427
1 823.55

3000

3000


Subgroup check
Jac G2
19455.253
1 542.00

3500

3500


EC ScalarMul         255-bit G1
Prj G1
16266.511
1 844.28






EC ScalarMul vartime 255-bit G1
Prj G1
20193.861
1 485.60






EC ScalarMul         255-bit G1
Jac G1
8268.768
3 628.11
12000
5500
3000
8500


EC ScalarMul vartime 255-bit G1
Jac G1
9304.836
3 224.13






EC ScalarMul         255-bit G2
Prj G2
8717.332
3 441.42






EC ScalarMul vartime 255-bit G2
Prj G2
10393.498
2 886.42






EC ScalarMul         255-bit G2
Jac G2
5241.695
5 723.34
45000
10300
3500
13800


EC ScalarMul vartime 255-bit G2
Jac G2
4379.089
6 850.74






Miller Loop BLS12

2408.223
12 457.32






Final Exponentiation BLS12

23078.698
1 299.90






Pairing BLS12

7504.015
3 997.86






Hash to G1 (SSWU - Draft #14)

30690.851
977.49






Hash to G2 (SSWU - Draft #14)

26205.451
1 144.80






Miller Loop BLS12

5222.423
5 744.46

18000




Final Exponentiation BLS12

4385.138
6 841.29

20500




Pairing BLS12

2408.269
12 457.08
108000
38500
6500
45000


Pairing BLS12 batched:     1

2408.014
12 458.40






Pairing BLS12 non-batched: 2

1200.394
24 991.79






Pairing BLS12 batched:     2

1882.654
15 934.95
151000
49300
13000
62300


Pairing BLS12 non-batched: 3

801.33
37 437.76






Pairing BLS12 batched:     3

1530.618
19 599.93
194000
63700
19500
83200


Pairing BLS12 batched:     4

1301.712
23 046.57
237000
78100
26000
104100


Pairing BLS12 batched:     5

1124.613
26 675.84
280000
92500
32500
125000


Pairing BLS12 batched:     6

992.763
30 218.69
323000
106900
39000
145900


Pairing BLS12 batched:     7

885.784
33 868.30
366000
121300
45500
166800


Pairing BLS12 batched:     8

804.54
37 288.39
409000
135700
52000
187700


verify_kzg_proof (EIP-4844)

1284.248
23 359.97
50000
70000
(priced in)
70000

Author: QEDK
Date: 2024-04-21T18:01:16.073Z

Agree with the premise of this, we should bring the pricing of these in-line with current KZG proof costs.

Author: mratsim
Date: 2024-04-27T07:25:57.445Z

I notice that unlike EIP-196, EIP-2537 does not allow truncated / virtually padded with zeros.

Encoding
Field elements and scalars are encoded as 32 byte big-endian numbers. Curve points are encoded as two field elements (x, y), where the point at infinity is encoded as (0, 0).
Tuples of objects are encoded as their concatenation.
For both precompiled contracts, if the input is shorter than expected, it is assumed to be virtually padded with zeros at the end (i.e. compatible with the semantics of the CALLDATALOAD opcode). If the input is longer than expected, surplus bytes at the end are ignored.
The length of the returned data is always as specified (i.e. it is not “unpadded”).

Is this intentional? I wonder if we can make a retroactive EIP to enshrine the same for EIP-196.

Author: mratsim
Date: 2024-05-01T23:26:22.073Z

I finished fully implemented EIP-2537 in Constantine in EIP-2537 - BLS12-381 precompiles for the EVM by mratsim · Pull Request #368 · mratsim/constantine · GitHub and now can provide accurate benchmarks that take into account (de)serialization and other miscellaneous costs.
First on AMD Ryzen 7840U (2023, low power / ultraportable version of their flagship 7840U, TDP 28W down to 15W, 3.3GHz turbo 5.1GHz) with assembly and Clang.
image1285×967 237 KB
Second on i5-5257U (Macbook Pro 2015, low power dual core Broadwell, TDP 15W down to 7.5W, 2.3GHz turbo 2.9GHz) without assembly (it doesn’t have ADOX/ADCX instructions as well), but we do use the add-with-carry intrinsics. This is a 9 year old x86 CPU.
image824×704 110 KB
Finally on a Raspberry Pi 4 (2019, TDP 10W, 1.5GHz) without assembly, and without any add-with-carry intrinsics. No add-with-carry makes bigint operations up to 3x more costly has add-with-carry need to be replaced by main addition, comparison/carry check, adding the carry.

See also compiler codegen Compiler Explorer and Tracking compiler inefficiencies · Issue #357 · mratsim/constantine · GitHub

image927×797 149 KB

Price suggestions



 mratsim:

The CPU is about 2.5x slower (single-threaded) than 7840U with Constantine.


This remark is confirmed with end-to-end test.
Besides MSMs all operations can achieve 60Mgas/s on a 9 year old very low-powered CPU (15W!), without assembly. and even 230Mgas/s for mapping to G2. This strongly suggests that those operations are overcosted.
I think that 9 year old, 15W CPU is significantly outdated and achieving 20Mgas/s without assembly on it should be fine.
Hence I suggest the following price changes:




Primitive
Current price
Suggested price
Ryzen 7840U 28W 3.3GHz turbo 5.1GHz - clang+ASM - current
Ryzen 7840U 28W 3.3GHz turbo 5.1GHz - clang+ASM - repriced
Intel i5-5257U 15W 2.3GHz turbo 2.8GHz - clang+noASM+intrinsics - current
Intel i5-5257U 15W 2.3GHz turbo 2.8GHz - clang+noASM+intrinsics - repriced




BLS12_G1ADD
500
125
185.60 MGas/s
46.4
79.24 MGas/s
19.81


BLS12_G1MUL
12000
3800
144.80 MGas/s
45.9
52.94 MGas/s
16.8


BLS12_G2ADD
800
170
218.28 MGas/s
46.325
86.30 MGas/s
18.33


BLS12_G2MUL
45000
6100
346.23 MGas/s
46.9
113.23 MGas/s
15.35


BLS12_MAP_FP_TO_G1
5500
1600
161.09 MGas/s
46.86
59.91 MGas/s
17.43


BLS12_MAP_FP2_TO_G2
75000
5000
702.17 MGas/s
46.8
231.85 MGas/s
15.46



The rationale for those changes is targeting a CPU 2~3 years older than my own for 30Mgas/s, CPUs are progressing on average by 10~15%/year, something that is 66% perf is reasonable (so around 45Mgas/s). But that said that CPU is still a low-power CPU, so instead of targeting 45Mgas on it we can target 40Mgas or even 35Mgas.
In any case it is clear that G2 functions are severely overcosted, especially mapping to G2.
Pairings
For pairings, the evolution of cost for multipairing is stable but the price is 5x the actual cost.
Hence I suggest moving from 43000*k + 65000 to 8600*k + 13000
MSMs
The discounts are too steep and need adjustment.




Primitive
Current cost
Suggested cost
Ryzen 7840U 28W 3.3GHz turbo 5.1GHz - clang+ASM - current
Ryzen 7840U 28W 3.3GHz turbo 5.1GHz - clang+ASM - repriced
Intel i5-5257U 15W 2.3GHz turbo 2.8GHz - clang+noASM+intrinsics - current
Intel i5-5257U 15W 2.3GHz turbo 2.8GHz - clang+noASM+intrinsics - repriced




G1MSM 2
2 * 12000 * 888 / 1000 = 21312
2 * 3800 * 999 / 1000 = 7592
121.74 Mgas/s
43.37 Mgas/s
39.46 Mgas/s
14.06 Mgas/s


G1MSM 4
4 * 12000 * 641 / 1000 = 30768
4 * 3800 * 919 / 1000 = 13969
102.15 Mgas/s
46.38 Mgas/s
31.77 Mgas/s
14.42 Mgas/s


G1MSM 8
8 * 12000 * 453 / 1000 = 43488
8 * 3800 * 813 / 1000 = 24715
81.67 Mgas/s
46.41 Mgas/s
28.10 Mgas/s
15.97 Mgas/s


G1MSM 16
16 * 12000 * 334 / 1000 = 64128
16 * 3800 * 728 / 1000 = 44262
67.23 Mgas/s
46.40 Mgas/s
22.78 Mgas/s
15.72 Mgas/s


G1MSM 32
32 * 12000 * 268 / 1000 = 102912
32 * 3800 * 668 / 1000 = 81229
59.01 Mgas/s
46.58 Mgas/s
20.42 Mgas/s
16.12 Mgas/s


G1MSM 64
64 * 12000 * 221 / 1000 = 169728
64 * 3800 * 630 / 1000 = 153216
51.40 Mgas/s
46.40 Mgas/s
18.18 Mgas/s
16.41 Mgas/s


G1MSM 128
128 * 12000 * 174 / 1000 = 267264
128 * 3800 * 602 / 1000 = 292813
42.37 Mgas/s
46.42 Mgas/s
15.22 Mgas/s
16.65 Mgas/s


G2MSM 2
2 * 45000 * 888 / 1000 = 79920
2 * 6100 * 999 / 1000 = 12188
274.23 Mgas/s
41.82 Mgas/s
85.47 Mgas/s
13.03 Mgas/s


G2MSM 4
4 * 45000 * 641 / 1000 = 115380
4 * 6100 * 919 / 1000 = 22424
168.31 Mgas/s
32.71 Mgas/s
69.02 Mgas/s
13.41 Mgas/s


G2MSM 8
8 * 45000 * 453 / 1000 = 163080
8 * 6100 * 813 / 1000 = 39674
177.98 Mgas/s
42.77 Mgas/s
55.77 Mgas/s
13.57 Mgas/s


G2MSM 16
16 * 45000 * 334 / 1000 = 240480
16 * 6100 * 728 / 1000 = 71053
150.39 Mgas/s
44.43 Mgas/s
48.60 Mgas/s
14.36 Mgas/s


G2MSM 32
32 * 45000 * 268 / 1000 = 385920
32 * 6100 * 668 / 1000 = 130394
136.80 Mgas/s
46.22 Mgas/s
44.21 Mgas/s
14.94 Mgas/s


G2MSM 64
64 * 45000 * 221 / 1000 = 636480
64 * 6100 * 630 / 1000 = 245952
119.28 Mgas/s
46.09 Mgas/s
39.68 Mgas/s
15.33 Mgas/s


G2MSM 128
128 * 45000 * 174 / 1000 = 1002240
128 * 6100 * 602 / 1000 = 470042
101.59 Mgas/s
47.64 Mgas/s
33.94 Mgas/s
15.92 Mgas/s



Comparison with EIP-4844 KZG
EIP-4844 introduced a point evaluation precompile that is mostly a double pairing at 50000 gas: EIP-4844: Shard Blob Transactions
On my machine that is 1283.318 operations/s so 64.17Mgas/s
image1474×834 174 KB
Comparison with EIP-196 and EIP-197

BN254 ECADD is at 500
BN254 ECMUL is at 40000
BN254 PAIRING is at 80000*k + 100000

I can introduce benchmarks later but they seem mispriced by a factor over 10x for multiplication and about 10x for pairings.

Author: mratsim
Date: 2024-05-05T17:33:34.267Z

Bench update with SHA256 and BN254 precompiles
image1307×1155 308 KB

Author: poojaranjan
Date: 2024-07-19T14:51:21.777Z

EIP-2537: Precompile for BLS12-381 curve operations with @ralexstokes

Author: jwasinger
Date: 2024-08-16T20:08:02.065Z

I’m sure this has been covered previously, but I can’t find it:
Why is the ABI encoding for Fp elements aligned along 64 bytes instead of 48 bytes?
it’s discussed in the EIP text.

Author: chfast
Date: 2024-09-11T12:40:09.543Z

Is there a mathematical formula for discount?

Author: chfast
Date: 2024-10-03T20:08:58.841Z

BLS precompile g1msm gas cost analysis
We’ve implemented EIP-2537: Precompile for BLS12-381 curve operations in evmone using blst v0.3.13 library. Then, we’ve benchmark multi-scalar multiplication g1msm in this implementation and compared timings with the specified gas schedule.
Benchmarks have been done on 2 different CPU architectures:

x86-64 Intel Ultra 7 155U Performance Core 4.0GHz
arm64 Apple M3 Pro

Timings have been normalized to single point multiplication (precompile g1mul). I.e. single naive multiplication is 1.
We also benchmark variant of g1msm with subgroup checks disabled (“nocheck”).
The chart also includes the line “gas” of by-the-spec gas cost. It is “discounted” up to 128, then linear with slope 174/1000.
Finally, we’ve added the “check only” line which is difference between default and “nocheck” series.
1684×1268 188 KB
Conclusions
The real cost of the precompile is close to linear with slope 100/256 and is significantly higher than the estimated by the gas cost. While, the cost of the precompile without subgroup checks is sub-linear and “relatively” matches the gas cost.
In order to approximate the cost of the subgroup check component of the operation, we analyze the difference of time taken by the “check” and “nocheck” variant. It is visible from the chart that slope of the data is constant for various $n$ (contrary to that of the “check” and “nocheck” slopes which gradually decrease). So we have a sub-linear (piecewise linear) component of the MS itself and a linear component of the subgroup check, which aligns with the theoretical expectations
In other words, the g1msm precompile is compound of linear and sub-linear operations, therefore it total complexity is linear.
The gas schedule, specifically the discount table, was proposed in the initial commit BLS12-381 curve operations from 2020 and hadn’t been updated since.
The subgroup check was added two years later in 2022 by the Update EIP-2537: Rephrased subgroup check part.
We should get under consideration once again if the subgroup check is needed for point multiplication. The algorithms (mul and msm) works well on every point from the curve. This check seems to be added only because of not very clear security consideration. Moreover, making this check for every call is unnecessary when the programmer is sure that the input is in proper subgroup. I.e it’s a result of previous operation which does not change the subgroup.
By @chfast @rodiazet @pdobacz

Author: shamatar
Date: 2024-10-10T17:40:10.569Z

The original gas table implied/required that MSM will be implemented via peppinger algorithm, that has roughly N/logN assymptotics, and the original table was derived from empirical benchmarks for such case, that included subgroup checks. I will also note that subgroup checks were implemented with trivial multiplication by group order, while any more modern sane implementation  should use fast subgroup checks

Author: chfast
Date: 2024-10-11T09:40:27.997Z

Can you point to the source code of this?

Author: rodiazet
Date: 2024-10-11T09:44:05.587Z

I’ve just verified once again and blst which we used for the benchmarks uses fast group check. (not naive multiplication by group order)
G1: blst/src/map_to_g1.c at master · supranational/blst · GitHub
G2: blst/src/map_to_g2.c at master · supranational/blst · GitHub
Paper:

eprint.iacr.org



1130.pdf
275.60 KB






Can you provide please code you used for the benchmarks?

Author: chfast
Date: 2024-10-11T13:58:07.519Z

There is a flaw in the spec in the context of the BLST library.
In the inputs to multiplication (both MUL and MSM) the spec allows scalars bigger than the main subgroup order q, e.g. 2^256-1. In the BLST implementation this case is handled by slower non-GLV multiplication implementation “added for formal completeness”. In other words, users can bypass GLV multiplication by sending nonsensical inputs.
        if (check_mod_256(val.s, BLS12_381_r))  /* z^4 is the formal limit */
            POINTonE1_mult_glv(out, a, val.s);
        else    /* should never be the case, added for formal completeness */
            POINTonE1_mult_w5(out, a, scalar, nbits);



github.com


supranational/blst/blob/v0.3.13/src/e1.c#L524-L527

      
          if (check_mod_256(val.s, BLS12_381_r))  /* z^4 is the formal limit */
              POINTonE1_mult_glv(out, a, val.s);
          else    /* should never be the case, added for formal completeness */
              POINTonE1_mult_w5(out, a, scalar, nbits);
      
    





Found by @rodiazet.

Author: jwasinger
Date: 2024-10-11T14:16:01.523Z

The behavior in gnark is to reduce a given scalar by the curve base field modulus when deserializing it: gnark-crypto/ecc/bls12-381/fr/element.go at master · Consensys/gnark-crypto · GitHub .
I assume implementations should do the same here to avoid that case.
Edit:  i have to add this to allow ethmagis to let me repost deleted text…

Author: shamatar
Date: 2024-10-11T16:51:15.407Z

The main motivation for subgroup checks during the discussion was to allow no-brainer use of GLV optimization for multiplication implementation. GLV is very beneficial for e.g. secp256k1 that doesn’t need subgroup checks, but may be inefficient (I’ll give the example at the end why) for curves that require such check and where inputs are not trusted. After extra consideration below I may actually agree that subgroup checks are useless for computational purposes and should become a part of the application that uses such precompiles rather than part of the precompiles themself, of course with the exception of the pairing precompile, where subgroup checks are mandatory as otherwise pairing operation is undefined. But we may open a can of worms for one of the cases of BLS signature aggregation if implementors are not careful enough, and I do not have an upfront answer about some security features there.
Before continuing, for original table generation I used for the EIP was generated via quite convoluted procedure of doing multiparameter fitting for EIP1962 for all BLS12 curve family, and then BLS12-381 parameters were plugged in there. It was done quite a few years ago and indeed didn’t have subgroup checks included into such benchmarks as they were originally not in the scope of the precompile.
Here are the benchmarks I quickly did using eip1962 code
test bench::eip_2537::bench_eip_2537_fast_subgroup_g1                                   ... bench:      47,731.77 ns/iter (+/- 2,404.57)
test bench::eip_2537::bench_eip_2537_multiexp_2                                         ... bench:     272,435.40 ns/iter (+/- 4,215.22)
test bench::eip_2537::bench_eip_2537_multiexp_3                                         ... bench:     328,689.55 ns/iter (+/- 3,044.60)
test bench::eip_2537::bench_eip_2537_multiexp_4                                         ... bench:     373,639.55 ns/iter (+/- 9,547.45)
test bench::eip_2537::bench_eip_2537_multiexp_5                                         ... bench:     424,831.20 ns/iter (+/- 7,974.20)
test bench::eip_2537::bench_eip_2537_multiexp_6                                         ... bench:     459,172.40 ns/iter (+/- 9,935.16)
test bench::eip_2537::bench_eip_2537_multiexp_7                                         ... bench:     494,158.30 ns/iter (+/- 24,647.01)
test bench::eip_2537::bench_eip_2537_multiexp_8                                         ... bench:     529,041.70 ns/iter (+/- 22,740.11)
test bench::eip_2537::bench_eip_2537_part_of_subgroup_check                             ... bench:      40,565.00 ns/iter (+/- 674.59)
test bench::eip_2537::bench_eip_2537_raw_g1_multiplication                              ... bench:     139,856.95 ns/iter (+/- 2,869.93)

And a short explanation of tradeoffs and computational details under the hood for BLS12-381 curve.
First of all, subgroup checks can not be batched, and indeed our MSM gas formulas should have a linear term in them if subgroup checks are mandatory (in the same sense as linear slope after 128 points in the input). Technically how fast subgroup check works: one applies few endomorphisms (technically endomorphism does one field multiplication with X coordinate of the point) few times, subtracts them (EC point subtraction), and then subtracts from such result a product of the original point and scalar [0x0000000055555555, 0x396c8c005555e156] (LE words). This is fixed scalar and it allows optimizations of operations via addition chains, but in general let’s consider it as multiplication by 128 bit (half-width) scalar. You can see how long it takes in the benchmark that does naive double-and-add multiplication by such scalar. So subgroup check speed is dominated by speed of such operation, unless there came even faster check that I’m not aware of. From the benchmarks you can see that fast subgroup check time is almost equal to the multiplication time by such special scalar (bench_eip_2537_part_of_subgroup_check) and that it’s about two times faster that multiplication by u256::MAX (bench_eip_2537_raw_g1_multiplication).
Here I’ll give the example why for computational purposes subgroup check is actually not beneficial by itself indeed. In the degenerate case of multiexp of 1 point (and MSM via Peppinger algorithm will be discounted for large number of points) the best multiplication strategy if we know that point is in the main subgroup (we did subgroup check!) is to apply GLV and then degrade a task of point * 256 bit scalar multiplication to MSM of two (point * 128 bit scalar) pairs. For the single point GLV is usually 30-35% faster than naive multiplication, so we can say that subgroup check is roughly equal to 128 bit multiplication and single point MSM is ~180 bit multiplication, so total cost of single multiplication is roughly as it would be 310 bits scalar by point multiplication. So indeed for BLS12-381 combined it’s still more expensive than single multiplication by 256 bit scalar. For large size MSM if we assume roughly the same amortization scaling for MSM it may be always true that MSM cost without subgroup checks 256 * amortization_fn(number of points) is smaller than cost for the case with subgroup checks 128 * number_of_points + 128 * amortization_fn(number_of_points * 2) (because we can do GLV for MSM too).
But let’s get back to the case of BLS signature verification. There are usually two cases there

every public key is verified upfront with proof of knowledge of exponent (effectively - providing a valid signature for such public key during setup phase, called “proof of possession” in IETF standard). In such case sane implementation will and MUST check that public key is in G1 subgroup, and signature’s aggregated public key is just pk1 + pk2 + ... without randomization. I believe that it’s the mode people will use most
public keys are not verified, and so signature aggregation must be done via randomized linear combination pk1 + r * pk2 + r^2 * pk3 + ... where randomness r would come via commitment to the set of public keys. Here please pay attention - even though resulting linear combination may fall into G1, it doesn’t mean that all public keys are in G1, and probability of that is high due to BLS12-381 curve subgroups’ structure. Application designer must(!) take extra considerations here whether it’s acceptable or not. I couldn’t fine an example why for any sane cause it should be acceptable, but if we do check for subgroups for MSM, we can brick contracts that are poorly written. Other side - if we ignore sugroups, then security of such contracts is questionable. Maybe it’s just wise to not try to solve it at the precompile design layer

Author: shamatar
Date: 2024-10-11T16:56:40.648Z

Blockquote
There is a flaw in the spec in the context of the BLST library.
In the inputs to multiplication (both MUL and MSM ) the spec allows scalars bigger than the main subgroup order q , e.g. 2^256-1 . In the BLST implementation this case is handled by slower non-GLV multiplication implementation “added for formal completeness”. In other words, users can bypass GLV multiplication by sending nonsensical inputs

It’s just another can of worms that can happen - if we do not require subgroup checks, then we may get into the situation when too high level API is called by final implementation and such API (as in the example of blst above) may have assumptions about point being on curve or not. I will also add that it’s much cheaper to reduce the scalar to the proper range than do non-optimized routine there, and caller will have a hard time figuring this. Maybe we should just say that even though ABI allows arbitrary 256 bit scalars, implementations are advised and even may be required to reduce the scalar to canonical range unless they control the internals

Author: mratsim
Date: 2024-10-14T12:02:53.054Z

shamatar:

First of all, subgroup checks can not be batched


They actually can, see: constantine/constantine/named/constants/bls12_381_subgroups.nim at 2c3de8738d031394dfb78e3fc839cf5d12e42b92 · mratsim/constantine · GitHub
func isInSubgroup*(P: EC_ShortW[Fp[BLS12_381], G1]): SecretBool =
  ## Returns true if P is in 𝔾1 subgroup, i.e. P is a point of order r.
  ## A point may be on a curve but not on the prime order r subgroup.
  ## Not checking subgroup exposes a protocol to small subgroup attacks.
  ##
  ## Warning ⚠: Assumes that P is on curve
  # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf
  #   A note on group membership tests for 𝔾1, 𝔾2 and 𝔾T
  #   on BLS pairing-friendly curves
  #   P is in the 𝔾1 subgroup iff ϕ(P) == [-u²](P)
  var t0{.noInit.}, t1{.noInit.}: typeof(P)

  # [-u²]P
  t0.pow_bls12_381_x(P)
  t1.pow_bls12_381_minus_x(t0)

  # ϕ(P)
  t0.x.prod(P.x, BLS12_381.getCubicRootOfUnity_mod_p())
  t0.y = P.y
  t0.z = P.z

  return t0 == t1

func isInSubgroup*(P: EC_ShortW[Fp2[BLS12_381], G2]): SecretBool =
  ## Returns true if P is in 𝔾2 subgroup, i.e. P is a point of order r.
  ## A point may be on a curve but not on the prime order r subgroup.
  ## Not checking subgroup exposes a protocol to small subgroup attacks.
  ##
  ## Warning ⚠: Assumes that P is on curve
  # Implementation: Scott, https://eprint.iacr.org/2021/1130.pdf
  #   A note on group membership tests for 𝔾1, 𝔾2 and 𝔾T
  #   on BLS pairing-friendly curves
  #   P is in the 𝔾1 subgroup iff ψ(P) == [u](P)
  var t0{.noInit.}, t1{.noInit.}: typeof(P)
  t0.pow_bls12_381_x(P) # [u]P
  t1.frobenius_psi(P)   # ψ(P)

  return t0 == t1

A subgroup check is a scalar multiplication by a ~64 or ~128 bit number. Meaning we can use MSM on the subgroup checks to batch many at once and get sub-linear behavior.
Then there is a question of an attacker exploiting batched subgroup checks by forging inputs that cancel each others out when summed. Ideally we scale both side by a random factor local to the implementation that a malicious party cannot now but it’s yet an extra cost. (see Security of BLS batch verification - Cryptography - Ethereum Research )

Author: shamatar
Date: 2024-10-14T12:59:27.027Z

mratsim:

Then there is a question of an attacker exploiting batched subgroup checks by forging inputs that cancel each others out when summed


You kind of self-answered why it’s not batchable. Second, keep in mind that non-main BLS12-381 subgroups have small size. So if we assume P to be a generator of the small subgroup (~13 bits or so, I do not remember), and Q - of the main subgroup, then if I submit a point in the form for P + Q and your random challenge is 0 mod small_subgroup_size, then result of (P + Q) * random_scalar = Q * random_scalar, and your test would pass, but point is not in the main subgroup. As subgroup size is small, it’s easy to craft such point.

Author: sylvain
Date: 2025-01-30T19:43:37.107Z

The current version of the EIP mentions :

[…] the existing BN254 precompile that only provides 80 bits of security.

Where this security level comes from? Is there a new optimization of previous attacks? As far as I know the security level was estimated a bit under 100 bits (see draft-irtf-cfrg-pairing-friendly-curves-11)

Author: mehdi-defiesta
Date: 2025-04-14T09:12:25.444Z

Is there a way of testing those precompiles using foundry ? Currently it always returns InvalidOperandOOG error when I try to call any of them.