-
Notifications
You must be signed in to change notification settings - Fork 11
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
Gas cost reduction #29
Comments
In #31, there is a useful link to the gist, dedicated to gas cost reduction: https://gist.github.com/grGred/9bab8b9bad0cd42fc23d4e31e7347144#for-loops-improvement Duplicating here to keep it in mind |
As this issue was kind of opened last week, Are these two contracts made gas redundant or not? Although, I noticed that a lot of changes can be made inside the function |
The primary goal was providing compatibility with reference Rust implementation of the verifier, so for now there are various options for gas cost optimisation indeed. The systematic approach would imply evaluating current libraries (building blocks of the end-to-end verification flow) and identify what parts of the code are most / least expensive in order to prioritise the optimisation efforts. At the same time, any contribution that reduces overall gas cast is welcomed! |
Current gas cost profile (for each individual step):
Contracts from this branch were used: https://github.com/lurk-lab/solidity-verifier/commits/gas-optimizing |
Poseidon hashing In ba09754 there is an assembly implementation of Poseidon hashing. Comparing to previous non-assembly one, which takes 1,839,096 gas, assembly implementation takes 533,652 which is ~3.5x improvement. In current reference implementation of Nova e2e verification, Poseidon is used 3 times (twice in Step 2 and once in Step 3) Note: one can further reduce gas cost if we avoid extracting limbs from |
KeccakTranscript In 9e9d7eb there is an assembly implementation of KeccakTranscript. Comparing to previous non-assembly implementation, which takes 443,399 gas, assembly implementation takes 19814 (for a 32 bytes input processing), which is ~22x improvement. In current reference implementation of Nova e2e verification, KeccakTranscript is being used more than 150 times while sumcheck verification of Spartan (more precise number can be obtained later, after implementing steps in assembly). Note: one can further reduce gas cost if we use single keccak256 hashing inside the transcript and reduce the state to 32 bytes. In such case we will need to perform only single Montgomery reduction stage while squeezing. |
EqPolynomial (evaluate) In ca4cfe2 there is an assembly implementation of EqPolynomial (evaluate). Comparing to previous non-assembly implementation, which takes 14968 gas, assembly implementation takes 5114 gas (for |
IdentityPolynomial In 024abb6 there is an assembly implementation of IdentityPolynomial building block. Comparing to previous non-assembly implementation, which takes 17092 gas, assembly implementation takes 2690 gas (for |
SparsePolynomial In c7336f3 there is an assembly implementation of SparsePolynomial building block. Comparing to previous non-assembly implementation, which takes 27703 gas, assembly implementation takes 8784 gas (for |
PolyEvalInstance In 1b8b38c there is an assembly implementation of PolyEvalInstance building block. Comparing to previous, Grumpkin-based non-assembly (or partially assembly) implementation, which takes 21,871,794 gas, assembly implementation takes 12,208,521 gas (for |
Sumcheck verification In eebac31 there is an assembly implementation of Sumcheck building block. Comparing to previous, non-assembly implementation, which takes 7,346,586 gas, assembly implementation takes 320,499 (for sumcheck proof that holds 17 polynomials, with 2 coefficients each), which is ~22x improvement (essentially due to utilising assembly KeccakTranscript). |
Step 1 In fd1989d there is an assembly implementation of Step1 library as a one-line assembly function. Comparing to previous non-assembly implementation, which takes 57448 gas, assembly implementation takes only 262 gas. Step 2 In fd1989d there is an assembly implementation of Step2 as well. Comparing to previous non-assembly implementation, which takes 34,193,872 gas, assembly implementation takes 11,656,035 gas. Important note is that for a given specific proving example (that uses |
hey @storojs72, great work here! is this still being worked on? would love to be able to use this verifier on ethereum. |
Hi, @wd021! |
This is the meta-issue, dedicated specifically to reducing the gas cost of our e2e verification.
So far we have two contracts based on a different curve cycle:
Below is basic estimations of gas cost for each contract (a0ed4b7) using
cast estimate
utility:Important note: these estimations DO NOT include final step of verification (IPA / Zeromorph).
While our internal discussions, @johnchandlerburnham mentioned that the desired gas cost of the verifier should not exceed 500k of gas, so we need some strategy for gas cost reduction.
Here is a short-term roadmap for this development direction:
The first obvious step is estimating the gas cost using some alternative tools. Having 2-3 estimations produced by different independent tools should give us some average point, that we can enforce it by CI and prevent committing any code that increases this point.
We need to have more fine-grained estimations of every sequential step of e2e verification and gas cost for every building block execution. Ideally we should come up to understanding of the cost of every primitive operation (addition, multiplication, points addition, negation, etc.), then the cost of every building block can be expressed as a number of primitive operations multiplied on the cost of one. And, finally, cost of every sequential step of e2e verification can be expressed in terms of building blocks involved. For now we have 7 sequential steps in our flow, so tentatively this can visualised as following:
Every contract consists from set of steps, where each step has its own cost:
Every single step consist from set of operations which include either building blocks or more primitive operations. So we should 1 following table for each step:
And finally every building block always consists from primitive operations. So we should have 1 following table for each building block:
Once we build mentioned tables, we will have clear picture - what is most / less expensive step / building block / primitive operation and we can effectively split and prioritise the optimisation of this or that part of the codebase.
The text was updated successfully, but these errors were encountered: