Skip to content

Commit

Permalink
Optimize bn254/groth16 solidity verifier. (#376)
Browse files Browse the repository at this point in the history
* fix: misspelled ("decsribes" ->  "describes") (#339)

* fix  misspelled ("decsribes" ->  "describes")

* fix  misspelled ("decsribes" ->  "describes") from tmpl file and regenerate

Co-authored-by: tinywell <tinywell@tinywell.com>

* Fix: minor typo (#360)

Fix: minor typo

* Use shared array for input

* Fixes in the template

Co-authored-by: tinywell <zfh-zyw@163.com>
Co-authored-by: tinywell <tinywell@tinywell.com>
Co-authored-by: omahs <73983677+omahs@users.noreply.github.com>
  • Loading branch information
4 people committed Oct 6, 2022
1 parent b33f88c commit 1a93ec8
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 15 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ happens and under which conditions it normally happens.
suggested. If it has, add a comment to the existing issue instead of opening a new one.

#### How Do I Submit A (Good) Enhancement Suggestion?
Enhancement suggestions are tracked as GitHub issues. Create an issue on and provide
Enhancement suggestions are tracked as GitHub issues. Create an issue and provide
the following information:

* Use a **clear and descriptive title** for the issue to identify the suggestion.
Expand All @@ -65,4 +65,4 @@ If these checks pass, pull requests will be reviewed by the project team against
* changes that are solely formatting are likely to be rejected

Always write a clear log message for your commits. One-line messages are fine for small changes, but
bigger changes should contain more detail.
bigger changes should contain more detail.
104 changes: 91 additions & 13 deletions internal/backend/bn254/groth16/solidity.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package groth16

// solidityTemplate uses an audited template https://github.com/appliedzkp/semaphore/blob/master/contracts/sol/verifier.sol
// solidityTemplate based on an audited template https://github.com/appliedzkp/semaphore/blob/master/contracts/sol/verifier.sol
// audit report https://github.com/appliedzkp/semaphore/blob/master/audit/Audit%20Report%20Summary%20for%20Semaphore%20and%20MicroMix.pdf
// But some gas cost optimizations have been made.
// this is an experimental feature and gnark solidity generator as not been thoroughly tested
const solidityTemplate = `
{{- $lenK := len .G1.K }}
// SPDX-License-Identifier: AML
//
//
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
Expand Down Expand Up @@ -44,7 +45,7 @@ library Pairing {
}
/*
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
* @return The negation of p, i.e. p.plus(p.negate()) should be zero.
*/
function negate(G1Point memory p) internal pure returns (G1Point memory) {
Expand Down Expand Up @@ -81,6 +82,24 @@ library Pairing {
require(success,"pairing-add-failed");
}
/*
* Same as plus but accepts raw input instead of struct
* @return The sum of two points of G1, one is represented as array
*/
function plus_raw(uint256[4] memory input, G1Point memory r) internal view {
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 {invalid()}
}
require(success, "pairing-add-failed");
}
/*
* @return The product of a point on G1 and a scalar, i.e.
* p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all
Expand All @@ -102,6 +121,23 @@ library Pairing {
require (success,"pairing-mul-failed");
}
/*
* Same as scalar_mul but accepts raw input instead of struct,
* Which avoid extra allocation. provided input can be allocated outside and re-used multiple times
*/
function scalar_mul_raw(uint256[3] memory input, G1Point memory r) internal view {
bool success;
// solium-disable-next-line security/no-inline-assembly
assembly {
success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 {invalid()}
}
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,
Expand Down Expand Up @@ -175,11 +211,26 @@ contract Verifier {
vk.beta2 = Pairing.G2Point([uint256({{.G2.Beta.X.A1.String}}), uint256({{.G2.Beta.X.A0.String}})], [uint256({{.G2.Beta.Y.A1.String}}), uint256({{.G2.Beta.Y.A0.String}})]);
vk.gamma2 = Pairing.G2Point([uint256({{.G2.Gamma.X.A1.String}}), uint256({{.G2.Gamma.X.A0.String}})], [uint256({{.G2.Gamma.Y.A1.String}}), uint256({{.G2.Gamma.Y.A0.String}})]);
vk.delta2 = Pairing.G2Point([uint256({{.G2.Delta.X.A1.String}}), uint256({{.G2.Delta.X.A0.String}})], [uint256({{.G2.Delta.Y.A1.String}}), uint256({{.G2.Delta.Y.A0.String}})]);
{{- range $i, $ki := .G1.K }}
{{- range $i, $ki := .G1.K }}
vk.IC[{{$i}}] = Pairing.G1Point(uint256({{$ki.X.String}}), uint256({{$ki.Y.String}}));
{{- end}}
}
function iteration(
uint256[3] memory mul_input,
Pairing.G1Point memory mul_return,
uint256[4] memory add_input,
Pairing.G1Point memory add_return
) internal view {
Pairing.scalar_mul_raw(mul_input, mul_return);
add_input[0] = add_return.X;
add_input[1] = add_return.Y;
add_input[2] = mul_return.X;
add_input[3] = mul_return.Y;
Pairing.plus_raw(add_input, add_return);
}
/*
* @returns Whether the proof is valid given the hardcoded verifying key
* above and the public inputs
Expand All @@ -188,19 +239,14 @@ contract Verifier {
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[{{sub $lenK 1}}] memory input
uint256[{{sub $lenK 1}}] calldata input
) public view returns (bool r) {
Proof memory proof;
proof.A = Pairing.G1Point(a[0], a[1]);
proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
proof.C = Pairing.G1Point(c[0], c[1]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
// Make sure that proof.A, B, and C are each less than the prime q
require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q");
require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q");
Expand All @@ -217,10 +263,42 @@ contract Verifier {
// Make sure that every input is less than the snark scalar field
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field");
vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
vk_x = Pairing.plus(vk_x, vk.IC[0]);
VerifyingKey memory vk = verifyingKey();
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
// Buffer reused for addition. Avoids memory allocations
uint256[4] memory add_input;
Pairing.G1Point memory add_return = Pairing.G1Point(0, 0);
// Buffer reused for multiplication
uint256[3] memory mul_input;
Pairing.G1Point memory mul_return = Pairing.G1Point(0, 0);
{{- range $i, $ki := .G1.K }}
{{- if gt $i 0 -}}
{{- $pos := sub $i 1 }}
mul_input[0] = uint256({{$ki.X.String}});
mul_input[1] = uint256({{$ki.Y.String}});
mul_input[2] = input[{{$pos}}];
iteration(mul_input, mul_return, add_input, add_return);
{{- end -}}
{{- end }}
{{- range $i, $ki := .G1.K -}}
{{- if lt $i 1}}
// last
add_input[0] = add_return.X;
add_input[1] = add_return.Y;
add_input[2] = uint256({{$ki.X.String}});
add_input[3] = uint256({{$ki.Y.String}});
Pairing.plus_raw(add_input, add_return);
{{- end -}}
{{- end }}
return Pairing.pairing(
Pairing.negate(proof.A),
Expand Down

0 comments on commit 1a93ec8

Please sign in to comment.