owned this note
owned this note
Published
Linked with GitHub
###### tags: ` estimation`
# On-chain verifier
In this document we make some back-of-the-envelope computations to see how feasible it is to verify SNARKs on main-net. We have some preliminary benchmarks made by Kenneth that show how expensive each function is. The goal of this document is to use those values and estimate SNARK verification on-chain. Similarly, we will estimate how much data the script needs to process, to understand if we are within the script data budget.
We analyse two SNARKs systems, Groth16 and vanilla-Plonk.
## Preliminary benchmarks
The current limit for on-chain scripts is 10,000,000,000 of the units in the file, but the basic Plutus stuff will add a lot on top of the costs of executing the builtins.
The values of interest for this analysis are the following (were x is the size of the input in bits divided by 64):
```
bls12_381_G1_compress : 3341914
bls12_381_G1_uncompress : 16511372
bls12_381_G1_add : 1046420
bls12_381_G1_equal : 545063
bls12_381_G1_hashToCurve : 66311195 + 23097*x
bls12_381_G1_mul : 94607019 + 87060*x (we use 94955259, with x = 4)
bls12_381_G1_neg : 292890
bls12_381_G2_compress : 3948421
bls12_381_G2_uncompress : 33114723
bls12_381_G2_add : 2359410
bls12_381_G2_equal : 1102635
bls12_381_G2_hashToCurve : 204557793 + 23271*x
bls12_381_G2_mul : 190191402 + 85902*x
bls12_381_G2_neg : 307813
bls12_381_GT_finalVerify : 388656972
bls12_381_GT_millerLoop : 402099373
bls12_381_GT_mul : 2533975
blake2b_256 : 358499 + 10186*x (521475, with x = 16)
addInteger : 85664 + 712*max(x,y) (88512, with x = y = 4)
multiplyInteger : 1000 + 55553*(x+y) (641924, with x = y = 4, and we include the price of modular reduction, as we need one per mult)
divideInteger : if x>y
then 809015 + 577*x*y
else 196500
modInteger : 196500
expInteger : We estimate 32 mults and adds (23373952)
```
## Groth16 verifier
The verifier needs to run the following check:
$$e([A]_1, [B]_2) =? e([\alpha]_1, [\beta]_2) * e\left(\sum_{i=0}^l a_i \left[\frac{\beta * u_i(x) + \alpha * v_i(x) + w_i(x)}{\alpha}\right]_1, [\gamma]_2\right) * e([C]_1, [\delta]_2)$$
Note that the above is a pairing equation, meaning that we are checking if
$e(a,b) = e(c,d) * e(e,f) * e(g,h)$. This check can be translated to:
$$
\begin{aligned}
lhs &\leftarrow ml(a,b) \\
rhs &\leftarrow ml(c,d) * ml(e,f) * ml(g,h) \\
\texttt{accept}&\iff \texttt{final_verify}(lhs, rhs)
\end{aligned}
$$
We can do some ZK engineering to bring the number of l to 1, with some minor effect on the verifier (in the best case, a hash function of some public values). This means that the number of operations required for a Groth16 verifier are:
* 4 `bls12_381_G1_uncompress` (16511372)
* 4 `bls12_381_G2_uncompress` (33114723)
* 1 `bls12_381_G1_mul` (97392939)
* 1 `bls12_381_G1_add` (1046420)
* 4 `bls12_381_GT_millerLoop` (402099373)
* 2 `bls12_381_GT_mul` (2533975)
* 1 `bls12_381_GT_finalVerify` (388656972)
This results in a total of 2,299,066,153. With the limit of 10,000,000,000, this means that a Groth16 proof uses around 23% of the execution budget. Note, however, that the basic Plutus operations add a lot on top of the costs of executing the builtins, so we shouldn't expect to reach the limit.
In terms of size, we are handling:
* 4 G1 elements (48 bytes)
* 4 G2 elements (96 bytes)
* 1 scalar (32 bytes)
resulting in 608 bytes. The current limit for scripts is 16384 bytes, which means a Groth16 verifier uses less than 4% of the data limit per script.
## Plonk verifier
We assume there are no public inputs, so l = 1.
The number of operations required for a vanilla plonk verifier are (the numbering refers to each step of the verifier as in the [original paper](https://eprint.iacr.org/2019/953.pdf) as per Feb-2023):
* (1) 9 `bls12_381_G1_uncompress` (16511372)
* (2) 6 `less than` checks, over `Integer` type (free or failure)
* (3) 1 `less than` check, over `Integer` type (free or failure)
* (4) 6 `blake2b_256` of at most 1KB (521475)
* (5) 1 modular exponantiation (23373952 - super uper bound. depends on the number of gates)
* (6) 2 `multiplyInteger` (641924)
* (6) 1 `divideInteger` (196500)
* (7) 1 `multiplyInteger` (641924)
* (8) 8 `multiplyInteger` (641924)
* (8) 7 `addInteger` (88512)
* (8) 1 `modInteger` (196500)
* (9) 6 `bls12_381_G1_uncompress` (16511372)
* (9) 17 `multiplyInteger` (641924)
* (9) 9 `bls12_381_G1_mul` (97392939)
* (9) 9 `bls12_381_G1_add` (1046420)
* (10) 2 `bls12_381_G1_add` (1046420)
* (10) 4 `multiplyInteger` (641924)
* (10) 5 `bls12_381_G1_mul` (97392939)
* (10) 5 `bls12_381_G1_add` (1046420)
* (11) 1 `bls12_381_G1_uncompress` (16511372)
* (11) 10 `multiplyInteger` (641924)
* (11) 7 `addInteger` (88512)
* (11) 1 `modInteger` (196500)
* (11) 1 `bls12_381_G1_mul` (97392939)
* (12) 2 `bls12_381_G2_uncompress` (33114723)
* (12) 2 `bls12_381_G1_mul` (97392939)
* (12) 4 `bls12_381_G1_add` (1046420)
* (12) 2 `bls12_381_GT_millerLoop` (402099373)
* (12) 1 `bls12_381_GT_finalVerify` (388656972)
In summary, the number of operations are the following:
* 16 x bls12_381_G1_uncompress (16511372)
* 17 x bls12_381_G1_mul (97392939)
* 20 x bls12_381_G1_add (1046420)
* 2 x bls12_381_G2_uncompress (33114723)
* 2 x bls12_381_GT_millerLoop (402099373)
* 1 x bls12_381_GT_finalVerify (388656972)
* 6 x blake2b_256 (521475)
* 1 x modular exponantiation (23373952)
* 42 x multiplyInteger (641924)
* 1 x divideInteger (196500)
* 14 x addInteger (88512)
* 2 x modInteger (196500)
We can see that this is a bit more convoluted than Groth16.. But we have less miller loops. A preliminary check gets us a computation of 3,255,167,757, which is 33% of the execution budget.
For the amount of data that the verifier needs to operate with:
* 18 G1 elements (48 bytes)
* 2 G2 elements (96 bytes)
* 7 scalars (32 bytes)
This results in 1280 bytes, which is around 8% of the allowed budget.