171 lines
6.9 KiB
Solidity
171 lines
6.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.6.0;
|
|
|
|
library Pairing {
|
|
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
|
|
|
struct G1Point {
|
|
uint256 X;
|
|
uint256 Y;
|
|
}
|
|
|
|
// Encoding of field elements is: X[0] * z + X[1]
|
|
struct G2Point {
|
|
uint256[2] X;
|
|
uint256[2] Y;
|
|
}
|
|
|
|
/*
|
|
* @return The negation of p, i.e. p.plus(p.negate()) should be zero
|
|
*/
|
|
function negate(G1Point memory p) internal pure returns (G1Point memory) {
|
|
// The prime q in the base field F_q for G1
|
|
if (p.X == 0 && p.Y == 0) {
|
|
return G1Point(0, 0);
|
|
} else {
|
|
return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @return r the sum of two points of G1
|
|
*/
|
|
function plus(
|
|
G1Point memory p1,
|
|
G1Point memory p2
|
|
) internal view returns (G1Point memory r) {
|
|
uint256[4] memory input = [
|
|
p1.X, p1.Y,
|
|
p2.X, p2.Y
|
|
];
|
|
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 r the product of a point on G1 and a scalar, i.e.
|
|
* p == p.scalarMul(1) and p.plus(p) == p.scalarMul(2) for all
|
|
* points p.
|
|
*/
|
|
function scalarMul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) {
|
|
uint256[3] memory input = [p.X, p.Y, s];
|
|
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,
|
|
* pairing([P1(), P1().negate()], [P2(), P2()]) should return true.
|
|
*/
|
|
function pairing(
|
|
G1Point memory a1,
|
|
G2Point memory a2,
|
|
G1Point memory b1,
|
|
G2Point memory b2,
|
|
G1Point memory c1,
|
|
G2Point memory c2,
|
|
G1Point memory d1,
|
|
G2Point memory d2
|
|
) internal view returns (bool) {
|
|
uint256[24] memory input = [
|
|
a1.X, a1.Y, a2.X[0], a2.X[1], a2.Y[0], a2.Y[1],
|
|
b1.X, b1.Y, b2.X[0], b2.X[1], b2.Y[0], b2.Y[1],
|
|
c1.X, c1.Y, c2.X[0], c2.X[1], c2.Y[0], c2.Y[1],
|
|
d1.X, d1.Y, d2.X[0], d2.X[1], d2.Y[0], d2.Y[1]
|
|
];
|
|
uint256[1] memory out;
|
|
bool success;
|
|
|
|
// solium-disable-next-line security/no-inline-assembly
|
|
assembly {
|
|
success := staticcall(sub(gas(), 2000), 8, input, mul(24, 0x20), out, 0x20)
|
|
// Use "invalid" to make gas estimation work
|
|
switch success case 0 { invalid() }
|
|
}
|
|
|
|
require(success, "pairing-opcode-failed");
|
|
return out[0] != 0;
|
|
}
|
|
}
|
|
|
|
contract BatchTreeUpdateVerifier {
|
|
uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
|
uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
|
using Pairing for *;
|
|
|
|
struct VerifyingKey {
|
|
Pairing.G1Point alfa1;
|
|
Pairing.G2Point beta2;
|
|
Pairing.G2Point gamma2;
|
|
Pairing.G2Point delta2;
|
|
Pairing.G1Point[2] IC;
|
|
}
|
|
|
|
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
|
|
vk.alfa1 = Pairing.G1Point(uint256(20475789791681002364587166738311620805815985969091106757478379420262430093495), uint256(3034180384279528157431123624668892018871098425968640214767822771352219138078));
|
|
vk.beta2 = Pairing.G2Point([uint256(347992840312110670849483472224503623225781749273259516677464742758581199694), uint256(16853081403278411985324640353650047676779142117029386935051386044282804346484)], [uint256(10461241566647602546027012417757263991485755060136522105605550609788790829933), uint256(16049761706815422591462572571571264938897676292217555774707799384732883004386)]);
|
|
vk.gamma2 = Pairing.G2Point([uint256(5535450215937949788522672716791294482208969162172756729752675877422249461391), uint256(4537903555000997751027892507073556632992848536024556182449526590439971414042)], [uint256(6688278057604431581483695896713912024597719708930089928002132340517626404891), uint256(15745439923152020754042431613052318298038129099865656040309120795605091105487)]);
|
|
vk.delta2 = Pairing.G2Point([uint256(10712491908603553476637447918495381165104059355722416702328240143919146641319), uint256(15855442659923189569787773688895011287546687523233653745264460947047886121140)], [uint256(18278088599243830423965796542892879791365910862597475788753708589843343437901), uint256(10765606859348375283724614934374540130725132299795942405716724739350245709734)]);
|
|
vk.IC[0] = Pairing.G1Point(uint256(18147360875100520747353841225428915644191762631193821400291387675910597374366), uint256(17222433096548585553756828362569506045947134360392537102794184064340219776032));
|
|
vk.IC[1] = Pairing.G1Point(uint256(3514632146136652297064638325657684436433185732623721288055192259268961814948), uint256(8363257337389338977321440370428118205387545635573906956020792115766452976369));
|
|
|
|
}
|
|
|
|
/*
|
|
* @returns Whether the proof is valid given the hardcoded verifying key
|
|
* above and the public inputs
|
|
*/
|
|
function verifyProof(
|
|
bytes memory proof,
|
|
uint256[1] memory input
|
|
) public view returns (bool) {
|
|
uint256[8] memory p = abi.decode(proof, (uint256[8]));
|
|
for (uint8 i = 0; i < p.length; i++) {
|
|
// Make sure that each element in the proof is less than the prime q
|
|
require(p[i] < PRIME_Q, "verifier-proof-element-gte-prime-q");
|
|
}
|
|
Pairing.G1Point memory proofA = Pairing.G1Point(p[0], p[1]);
|
|
Pairing.G2Point memory proofB = Pairing.G2Point([p[2], p[3]], [p[4], p[5]]);
|
|
Pairing.G1Point memory proofC = Pairing.G1Point(p[6], p[7]);
|
|
|
|
VerifyingKey memory vk = verifyingKey();
|
|
// Compute the linear combination vkX
|
|
Pairing.G1Point memory vkX = vk.IC[0];
|
|
for (uint256 i = 0; i < input.length; i++) {
|
|
// Make sure that every input is less than the snark scalar field
|
|
require(input[i] < SNARK_SCALAR_FIELD, "verifier-input-gte-snark-scalar-field");
|
|
vkX = Pairing.plus(vkX, Pairing.scalarMul(vk.IC[i + 1], input[i]));
|
|
}
|
|
|
|
return Pairing.pairing(
|
|
Pairing.negate(proofA),
|
|
proofB,
|
|
vk.alfa1,
|
|
vk.beta2,
|
|
vkX,
|
|
vk.gamma2,
|
|
proofC,
|
|
vk.delta2
|
|
);
|
|
}
|
|
}
|
|
|