-
Notifications
You must be signed in to change notification settings - Fork 34
/
Verifiers.ts
179 lines (148 loc) · 7.14 KB
/
Verifiers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import { expect } from "chai";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { ethers } from "hardhat";
import { Verifier as SnarkVerifier, InclusionVerifier, GrandSumVerifier, Halo2VerifyingKey } from "../typechain-types";
import { BigNumber } from "ethers";
import { BytesLike } from "ethers/lib/utils";
import * as fs from "fs";
import * as path from "path";
describe("Verifier Contracts", () => {
async function deployVerifyingFixture() {
// Contracts are deployed using the first signer/account by default
const verifyingKey = await ethers.deployContract(
"src/VerifyingKey.sol:Halo2VerifyingKey",
) as Halo2VerifyingKey;
const commitmentJson = fs.readFileSync(path.resolve(__dirname, "../../prover/bin/commitment_solidity_calldata.json"), "utf-8");
const commitmentCalldata = JSON.parse(commitmentJson);
return {
verifyingKey,
commitmentCalldata,
};
}
describe("Snark Proof Verifier", () => {
let snarkVerifier: SnarkVerifier;
let verifyingKey: Halo2VerifyingKey;
let commitmentCalldata: {
range_check_snark_proof: BytesLike;
grand_sums_batch_proof: BytesLike;
total_balances: BigNumber[];
};
beforeEach(async () => {
const deploymentInfo = await loadFixture(deployVerifyingFixture);
verifyingKey = deploymentInfo.verifyingKey;
commitmentCalldata = deploymentInfo.commitmentCalldata;
// Deploy SnarkVerifier contract
snarkVerifier = await ethers.deployContract(
"src/SnarkVerifier.sol:Verifier"
) as SnarkVerifier;
await snarkVerifier.deployed();
});
it("should verify snark proof", async () => {
// The verifier contract checks the number of instances in the VerifyingKey contract at 0x00c0 with the given 'instances' input
expect(await snarkVerifier.verifyProof(verifyingKey.address, commitmentCalldata.range_check_snark_proof, [0])).to.be.true;
});
it("should revert with invalid proof", async () => {
await expect(snarkVerifier.verifyProof(verifyingKey.address, commitmentCalldata.grand_sums_batch_proof, [1])).to.be.reverted;
});
});
describe("GrandSum Proof Verifier", () => {
let grandSumVerifier: GrandSumVerifier;
let verifyingKey: Halo2VerifyingKey;
let commitmentCalldata: {
range_check_snark_proof: BytesLike;
grand_sums_batch_proof: BytesLike;
total_balances: BigNumber[];
};
beforeEach(async () => {
const deploymentInfo = await loadFixture(deployVerifyingFixture);
verifyingKey = deploymentInfo.verifyingKey;
commitmentCalldata = deploymentInfo.commitmentCalldata;
// Deploy GrandSumVerifier contract
grandSumVerifier = await ethers.deployContract(
"src/GrandSumVerifier.sol:GrandSumVerifier"
) as GrandSumVerifier;
});
it("should verify grand sum proof", async () => {
// Concatenates the snark proof and the grand sum proof
let snarkProofArray = ethers.utils.arrayify(commitmentCalldata.range_check_snark_proof);
let grandSumProofArray = ethers.utils.arrayify(commitmentCalldata.grand_sums_batch_proof);
let totalBalances = commitmentCalldata.total_balances;
// The first 64 bytes of the snark proof represent a commitment to the corresponding to the user identity
// Starting from the next 64 bytes, each set of 64 bytes represents commitments corresponding to the total sum of balances
let grandSumCommitments = snarkProofArray.slice(64, (64 + grandSumProofArray.length));
// The verifier iterates over points in the proofs while verifying them.
// The proofs look like:
// i = 0 1 N
// [grand_sum_proof_p1_x, grand_sum_proof_p1_y, grand_sum_proof_p2_x, grand_sum_proof_p2_y, ... grand_sum_proof_pN_x, grand_sum_proof_pN_y, ...]
// [ snark_proof_p1_x, snark_proof_p1_y, snark_proof_p2_x, snark_proof_p2_y, ... snark_proof_pN_x, snark_proof_pN_y, ...]
// Where `N` is the number of currencies
let proofs = ethers.utils.concat([grandSumProofArray, grandSumCommitments]);
expect(await grandSumVerifier.verifyProof(verifyingKey.address, proofs, totalBalances)).to.be.not.reverted;
});
});
describe("Inclusion Proof Verifier", () => {
let inclusionVerifier: InclusionVerifier;
let verifyingKey: Halo2VerifyingKey;
let inclusionProof: BytesLike;
let commitmentCalldata: {
range_check_snark_proof: BytesLike;
grand_sums_batch_proof: BytesLike;
total_balances: BigNumber[];
};
let challenges: [BigNumber, BigNumber, BigNumber, BigNumber];
let userIdBigUint: BigNumber;
let balance1: BigNumber;
let balance2: BigNumber;
beforeEach(async () => {
const deploymentInfo = await loadFixture(deployVerifyingFixture);
verifyingKey = deploymentInfo.verifyingKey;
commitmentCalldata = deploymentInfo.commitmentCalldata;
inclusionVerifier = await ethers.deployContract(
"src/InclusionVerifier.sol:InclusionVerifier"
) as InclusionVerifier;
await inclusionVerifier.deployed();
verifyingKey = deploymentInfo.verifyingKey;
const inclusionJson = fs.readFileSync(
path.resolve(
__dirname,
"../../prover/bin/inclusion_proof_solidity_calldata.json"
),
"utf-8"
);
const inclusionCalldata: any = JSON.parse(inclusionJson);
inclusionProof = inclusionCalldata.proof;
challenges = inclusionCalldata.challenges;
userIdBigUint = inclusionCalldata.user_values[0];
balance1 = inclusionCalldata.user_values[1];
balance2 = inclusionCalldata.user_values[2];
});
it("should verify inclusion proof", async () => {
// Generating proof with concatenated snark proof and inclusion proof
let snarkProof = commitmentCalldata.range_check_snark_proof;
// Slice the snarkProof to match the length of inclusionProof
let proofArray = ethers.utils.arrayify(inclusionProof);
let snarkProofarray = ethers.utils.arrayify(snarkProof).slice(0, proofArray.length);
let proofs = ethers.utils.concat([proofArray, snarkProofarray]);
expect(await inclusionVerifier.verifyProof(
verifyingKey.address,
proofs,
[challenges[0], challenges[1], challenges[2], challenges[3]],
[userIdBigUint, balance1, balance2]
)).to.be.true;
});
it("should revert invalid inclusion proof", async () => {
// Generating proof with concatenated snark proof and inclusion proof
let snarkProof = commitmentCalldata.range_check_snark_proof;
// Slice the snarkProof to match the length of inclusionProof
let proofArray = ethers.utils.arrayify(inclusionProof);
let snarkProofarray = ethers.utils.arrayify(snarkProof).slice(0, proofArray.length);
let wrongProofs = ethers.utils.concat([snarkProofarray, snarkProofarray]);
await expect(inclusionVerifier.verifyProof(
verifyingKey.address,
wrongProofs,
[challenges[0], challenges[1], challenges[2], challenges[3]],
[userIdBigUint, balance1, balance2]
)).to.be.reverted;
});
});
});