├── BatchedGroth16.md ├── BatchedSnarkVerifier ├── .gitignore ├── contracts │ ├── BatchVerifier.sol │ ├── BatchVerifierContract.sol │ ├── Migrations.sol │ ├── Pairing.sol │ ├── Verifier.sol │ └── Wrapper.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test │ ├── threeInputs.js │ └── verifier.js └── truffle-config.js ├── LICENSE └── README.md /BatchedGroth16.md: -------------------------------------------------------------------------------- 1 | # Batched Groth16 SNARK verification for the same circuit 2 | 3 | Short explainer of how SNARK for transaction aggregation can be verified more efficiently on-chain. It allows the trade-off "100 mil constraints + one SNARK verification <=> few proofs of 10 mil constraints + batched verification". 4 | 5 | Here additional transaction data size is neglected for cost estimation purposes 6 | 7 | In our specific case where all SNARKs in the batch are effectively the same (same verifying key) the structure of the verifier is the following: 8 | 9 | **Original equation** 10 | 11 | ``` 12 | e(proof.A, proof.B)*e(-vk.alpha, vk.beta)*e(-vk_x, vk.gamma)*e(-proof.C, vk.delta) == 1 13 | ``` 14 | 15 | where `alpha` and `beta` from are from the original Groth16 `GT = e(alpha, beta)` and nothing more than a special form to allow on-chain verification where `GT` operations are not allowed. Ethereum pairing function implicitly verifies the equation above and takes gas per each `e(...)` operation. 16 | 17 | Structure of `vk_x` is an aggregation of public inputs in a form 18 | 19 | ``` 20 | vk_x = vkABC[0] + sum(vkABC[i]*input[i]) 21 | ``` 22 | 23 | where `vkABC` is a part of verifying key that's used for inclusion of public inputs into equation. 24 | 25 | In such naive setting verification of `N` SNARKs requires `4*N` pairing operations and `N*num_pub_inputs` scalar multiplications on-chain. Cost of point additions can be neglected, so final gas cost is roughly 26 | 27 | ``` 28 | 100 000 + 80 000 * 4 * N + 40 000 * N * num_pub_inputs = 29 | 100 000 + (320 000 + 40 000 * num_pub_inputs) * N 30 | 31 | ``` 32 | 33 | **Batching** 34 | 35 | For batching purposes one can raise every of `N` equations like on above to some random power `r[i]` (where `r[0] = 1` can be used as in degenerate case of batch of one), multiply all of them together and restructure. 36 | 37 | Important points to remember when restructuring: 38 | 39 | - `e(a*x, y)*e(b*x, y) = e((a+b)*x, y)` 40 | - `e(x, y)*e(z, y) = e(x + z, y)` 41 | - all `vkABC` are common, so accumulation for the public inputs can be further exploited 42 | 43 | ``` 44 | r[0] = 1 in this example 45 | 46 | e(vkABC[0] + sum(vkABC[i]*input[i]), vk.gamma)* 47 | e(r*(vkABC[0] + sum(vkABC[i]*input'[i])), vk.gamma) = 48 | e((1+r)*vkABC[0] + sum(vkABC[i]*(input[i] + r * input'[i]), vk.gamma) 49 | ``` 50 | 51 | - part in a form `e(proof.A, proof.B)` can not be exploited much due to different `proof.A` and `proof.B` elements in each proof (with unknown discrete logs), so here one has to pay additional scalar multiplication cost and keep elements in a form of 52 | 53 | ``` 54 | e(proof.A[0],proof.B[0])*e(r[1]*proof.A[1], proof.B[1])*.... 55 | ``` 56 | 57 | Inclusion of such pairing is legitimate because one can restructure everything backwards and recombine, so each equation in an orignal form above will be present with a coefficient `r[i]` in front of each G1 element in pairing 58 | 59 | As a result each proof verification requires one pairing due to `e(proof.A,proof.B)` form plus three pairings per full batch in forms of 60 | 61 | ``` 62 | e(-sum(r[i])*vk.alpha, vk.beta) // 1 scalar multiplication 63 | 64 | e(-vk_x_aggregated, vk.gamma) // num_pub_inputs + 1 scalar multiplications 65 | 66 | e(-sum(r[i]*proof.C[i]), vk.delta) // N scalar multiplications 67 | ``` 68 | 69 | where additional trick for accumulation in public inputs inclusion allows to postpone scalar multiplication as much as possible and do only `num_pub_inputs + 1` scalar multiplications. 70 | 71 | Final cost is 72 | 73 | ``` 74 | 100 000 + 80 000 * 3 + 80 000 * N + 40 000 * (num_pub_inputs + 1 + 1 + 2 * N) = 75 | 420 000 + 160 000 * N + 40 000 * num_pub_inputs 76 | ``` 77 | 78 | For our case of three public inputs (old state, new state, transactions commitment): 79 | 80 | - Naive verification ```100 000 + 460 000 * N``` 81 | - Batch verification ```540 000 + 160 000 * N``` 82 | 83 | Carefull usage of `r[0] = 1` will also bring costs down. 84 | 85 | ## Implementation results 86 | 87 | These tests are for **three** public inputs and uses `r[0] = 1` optimization. 88 | 89 | - Verification of 1 proof using batching verifier requires gas: 737684 90 | - Verification of 2 proofs using batching verifier requires gas: 924385 91 | - Verification of 3 proofs using batching verifier requires gas: 1111151 92 | - Verification of 4 proofs using batching verifier requires gas: 1297855 93 | - Verification of 5 proofs using batching verifier requires gas: 1484433 94 | 95 | ## Author 96 | 97 | Alex Vlasov, [@shamatar](https://github.com/shamatar), alex.m.vlasov@gmail.com -------------------------------------------------------------------------------- /BatchedSnarkVerifier/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | .vscode/* 4 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/BatchVerifier.sol: -------------------------------------------------------------------------------- 1 | // Thanks Harry from ETHSNARKS for base code 2 | 3 | pragma solidity 0.4.25; 4 | 5 | // import "./Pairing.sol"; 6 | 7 | library BatchVerifier { 8 | function GroupOrder () 9 | public pure returns (uint256) 10 | { 11 | return 21888242871839275222246405745257275088548364400416034343698204186575808495617; 12 | } 13 | 14 | function NegateY( uint256 Y ) 15 | internal pure returns (uint256) 16 | { 17 | uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 18 | return q - (Y % q); 19 | } 20 | 21 | function accumulate( 22 | uint256[] in_proof, 23 | uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 24 | uint256 num_proofs 25 | ) internal view returns ( 26 | uint256[] proofsAandC, 27 | uint256[] inputAccumulators 28 | ) { 29 | uint256 q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 30 | uint256 numPublicInputs = proof_inputs.length / num_proofs; 31 | uint256[] memory entropy = new uint256[](num_proofs); 32 | inputAccumulators = new uint256[](numPublicInputs + 1); 33 | 34 | for (uint256 proofNumber = 0; proofNumber < num_proofs; proofNumber++) { 35 | if (proofNumber == 0) { 36 | entropy[proofNumber] = 1; 37 | } else { 38 | entropy[proofNumber] = uint256(blockhash(block.number - proofNumber)) % q; 39 | } 40 | require(entropy[proofNumber] != 0, "Entropy should not be zero"); 41 | // here multiplication by 1 is implied 42 | inputAccumulators[0] = addmod(inputAccumulators[0], entropy[proofNumber], q); 43 | for (uint256 i = 0; i < numPublicInputs; i++) { 44 | // accumulate the exponent with extra entropy mod q 45 | inputAccumulators[i+1] = addmod(inputAccumulators[i+1], mulmod(entropy[proofNumber], proof_inputs[proofNumber * numPublicInputs + i], q), q); 46 | } 47 | // coefficient for +vk.alpha (mind +) is the same as inputAccumulator[0] 48 | } 49 | 50 | // inputs for scalar multiplication 51 | uint256[3] memory mul_input; 52 | bool success; 53 | 54 | // use scalar multiplications to get proof.A[i] * entropy[i] 55 | 56 | proofsAandC = new uint256[](num_proofs*2 + 2); 57 | 58 | proofsAandC[0] = in_proof[0]; 59 | proofsAandC[1] = in_proof[1]; 60 | 61 | for (proofNumber = 1; proofNumber < num_proofs; proofNumber++) { 62 | mul_input[0] = in_proof[proofNumber*8]; 63 | mul_input[1] = in_proof[proofNumber*8 + 1]; 64 | mul_input[2] = entropy[proofNumber]; 65 | assembly { 66 | // ECMUL, output proofsA[i] 67 | // success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add(proofsAandC, 0x20), mul(proofNumber, 0x40)), 0x40) 68 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 69 | } 70 | proofsAandC[proofNumber*2] = mul_input[0]; 71 | proofsAandC[proofNumber*2 + 1] = mul_input[1]; 72 | require(success, "Failed to call a precompile"); 73 | } 74 | 75 | // use scalar multiplication and addition to get sum(proof.C[i] * entropy[i]) 76 | 77 | uint256[4] memory add_input; 78 | 79 | add_input[0] = in_proof[6]; 80 | add_input[1] = in_proof[7]; 81 | 82 | for (proofNumber = 1; proofNumber < num_proofs; proofNumber++) { 83 | mul_input[0] = in_proof[proofNumber*8 + 6]; 84 | mul_input[1] = in_proof[proofNumber*8 + 7]; 85 | mul_input[2] = entropy[proofNumber]; 86 | assembly { 87 | // ECMUL, output proofsA 88 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 89 | } 90 | require(success, "Failed to call a precompile for G1 multiplication for Proof C"); 91 | 92 | assembly { 93 | // ECADD from two elements that are in add_input and output into first two elements of add_input 94 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 95 | } 96 | require(success, "Failed to call a precompile for G1 addition for Proof C"); 97 | } 98 | 99 | proofsAandC[num_proofs*2] = add_input[0]; 100 | proofsAandC[num_proofs*2 + 1] = add_input[1]; 101 | } 102 | 103 | function prepareBatches( 104 | uint256[14] in_vk, 105 | uint256[] vk_gammaABC, 106 | uint256[] inputAccumulators 107 | ) internal view returns ( 108 | uint256[4] finalVksAlphaX 109 | ) { 110 | // Compute the linear combination vk_x using accumulator 111 | // First two fields are used as the sum and are initially zero 112 | uint256[4] memory add_input; 113 | uint256[3] memory mul_input; 114 | bool success; 115 | 116 | // Performs a sum(gammaABC[i] * inputAccumulator[i]) 117 | for (uint256 i = 0; i < inputAccumulators.length; i++) { 118 | mul_input[0] = vk_gammaABC[2*i]; 119 | mul_input[1] = vk_gammaABC[2*i + 1]; 120 | mul_input[2] = inputAccumulators[i]; 121 | 122 | assembly { 123 | // ECMUL, output to the last 2 elements of `add_input` 124 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 125 | } 126 | require(success, "Failed to call a precompile for G1 multiplication for input accumulator"); 127 | 128 | assembly { 129 | // ECADD from four elements that are in add_input and output into first two elements of add_input 130 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 131 | } 132 | require(success, "Failed to call a precompile for G1 addition for input accumulator"); 133 | } 134 | 135 | finalVksAlphaX[2] = add_input[0]; 136 | finalVksAlphaX[3] = add_input[1]; 137 | 138 | // add one extra memory slot for scalar for multiplication usage 139 | uint256[3] memory finalVKalpha; 140 | finalVKalpha[0] = in_vk[0]; 141 | finalVKalpha[1] = in_vk[1]; 142 | finalVKalpha[2] = inputAccumulators[0]; 143 | 144 | assembly { 145 | // ECMUL, output to first 2 elements of finalVKalpha 146 | success := staticcall(sub(gas, 2000), 7, finalVKalpha, 0x60, finalVKalpha, 0x40) 147 | } 148 | require(success, "Failed to call a precompile for G1 multiplication"); 149 | finalVksAlphaX[0] = finalVKalpha[0]; 150 | finalVksAlphaX[1] = finalVKalpha[1]; 151 | } 152 | 153 | // original equation 154 | // e(proof.A, proof.B)*e(-vk.alpha, vk.beta)*e(-vk_x, vk.gamma)*e(-proof.C, vk.delta) == 1 155 | // accumulation of inputs 156 | // gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 157 | 158 | function BatchVerify ( 159 | uint256[14] in_vk, // verifying key is always constant number of elements 160 | uint256[] vk_gammaABC, // variable length, depends on number of inputs 161 | uint256[] in_proof, // proof itself, length is 8 * num_proofs 162 | uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 163 | uint256 num_proofs 164 | ) 165 | internal 166 | view 167 | returns (bool success) 168 | { 169 | require(in_proof.length == num_proofs * 8, "Invalid proofs length for a batch"); 170 | require(proof_inputs.length % num_proofs == 0, "Invalid inputs length for a batch"); 171 | require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length / num_proofs ); 172 | 173 | // strategy is to accumulate entropy separately for some proof elements 174 | // (accumulate only for G1, can't in G2) of the pairing equation, as well as input verification key, 175 | // postpone scalar multiplication as much as possible and check only one equation 176 | // by using 3 + num_proofs pairings only plus 2*num_proofs + (num_inputs+1) + 1 scalar multiplications compared to naive 177 | // 4*num_proofs pairings and num_proofs*(num_inputs+1) scalar multiplications 178 | 179 | uint256[] memory proofsAandC; 180 | uint256[] memory inputAccumulators; 181 | (proofsAandC, inputAccumulators) = accumulate(in_proof, proof_inputs, num_proofs); 182 | 183 | uint256[4] memory finalVksAlphaX = prepareBatches(in_vk, vk_gammaABC, inputAccumulators); 184 | 185 | uint256[] memory inputs = new uint256[](6*num_proofs + 18); 186 | // first num_proofs pairings e(ProofA, ProofB) 187 | for (uint256 proofNumber = 0; proofNumber < num_proofs; proofNumber++) { 188 | inputs[proofNumber*6] = proofsAandC[proofNumber*2]; 189 | inputs[proofNumber*6 + 1] = proofsAandC[proofNumber*2 + 1]; 190 | inputs[proofNumber*6 + 2] = in_proof[proofNumber*8 + 2]; 191 | inputs[proofNumber*6 + 3] = in_proof[proofNumber*8 + 3]; 192 | inputs[proofNumber*6 + 4] = in_proof[proofNumber*8 + 4]; 193 | inputs[proofNumber*6 + 5] = in_proof[proofNumber*8 + 5]; 194 | } 195 | 196 | // second pairing e(-finalVKaplha, vk.beta) 197 | inputs[num_proofs*6] = finalVksAlphaX[0]; 198 | inputs[num_proofs*6 + 1] = NegateY(finalVksAlphaX[1]); 199 | inputs[num_proofs*6 + 2] = in_vk[2]; 200 | inputs[num_proofs*6 + 3] = in_vk[3]; 201 | inputs[num_proofs*6 + 4] = in_vk[4]; 202 | inputs[num_proofs*6 + 5] = in_vk[5]; 203 | 204 | // third pairing e(-finalVKx, vk.gamma) 205 | inputs[num_proofs*6 + 6] = finalVksAlphaX[2]; 206 | inputs[num_proofs*6 + 7] = NegateY(finalVksAlphaX[3]); 207 | inputs[num_proofs*6 + 8] = in_vk[6]; 208 | inputs[num_proofs*6 + 9] = in_vk[7]; 209 | inputs[num_proofs*6 + 10] = in_vk[8]; 210 | inputs[num_proofs*6 + 11] = in_vk[9]; 211 | 212 | // fourth pairing e(-proof.C, finalVKdelta) 213 | inputs[num_proofs*6 + 12] = proofsAandC[num_proofs*2]; 214 | inputs[num_proofs*6 + 13] = NegateY(proofsAandC[num_proofs*2 + 1]); 215 | inputs[num_proofs*6 + 14] = in_vk[10]; 216 | inputs[num_proofs*6 + 15] = in_vk[11]; 217 | inputs[num_proofs*6 + 16] = in_vk[12]; 218 | inputs[num_proofs*6 + 17] = in_vk[13]; 219 | 220 | uint256 inputsLength = inputs.length * 32; 221 | uint[1] memory out; 222 | require(inputsLength % 192 == 0, "Inputs length should be multiple of 192 bytes"); 223 | 224 | assembly { 225 | success := staticcall(sub(gas, 2000), 8, add(inputs, 0x20), inputsLength, out, 0x20) 226 | } 227 | require(success, "Failed to call pairings functions"); 228 | return out[0] == 1; 229 | } 230 | } -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/BatchVerifierContract.sol: -------------------------------------------------------------------------------- 1 | // Thanks Harry from ETHSNARKS for base code 2 | 3 | pragma solidity 0.4.25; 4 | 5 | import "./Pairing.sol"; 6 | 7 | contract BatchVerifierContract { 8 | event DebugEvent(uint256 indexed idx, uint256 indexed data); 9 | 10 | constructor () public {} 11 | 12 | using Pairing for Pairing.G1Point; 13 | using Pairing for Pairing.G2Point; 14 | 15 | function ScalarField () 16 | public pure returns (uint256) 17 | { 18 | return 21888242871839275222246405745257275088548364400416034343698204186575808495617; 19 | } 20 | 21 | function NegateY( uint256 Y ) 22 | internal pure returns (uint256) 23 | { 24 | uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 25 | return q - (Y % q); 26 | } 27 | 28 | function accumulate( 29 | uint256[] in_proof, 30 | uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 31 | uint256 num_proofs 32 | ) internal view returns ( 33 | uint256[] proofsAandC, 34 | uint256[] inputAccumulators 35 | ) { 36 | uint256 q = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 37 | uint256 numPublicInputs = proof_inputs.length / num_proofs; 38 | uint256[] memory entropy = new uint256[](num_proofs); 39 | inputAccumulators = new uint256[](numPublicInputs + 1); 40 | 41 | for (uint256 proofNumber = 0; proofNumber < num_proofs; proofNumber++) { 42 | if (proofNumber == 0) { 43 | entropy[proofNumber] = 1; 44 | // entropy[proofNumber] = uint256(blockhash(block.number - 10)) % q; 45 | } else { 46 | // entropy[proofNumber] = 7; 47 | entropy[proofNumber] = uint256(blockhash(block.number - proofNumber)) % q; 48 | } 49 | require(entropy[proofNumber] != 0, "Entropy should not be zero"); 50 | // here multiplication by 1 is for a sake of clarity only 51 | inputAccumulators[0] = addmod(inputAccumulators[0], mulmod(1, entropy[proofNumber], q), q); 52 | for (uint256 i = 0; i < numPublicInputs; i++) { 53 | // accumulate the exponent with extra entropy mod q 54 | inputAccumulators[i+1] = addmod(inputAccumulators[i+1], mulmod(entropy[proofNumber], proof_inputs[proofNumber * numPublicInputs + i], q), q); 55 | } 56 | // coefficient for +vk.alpha (mind +) 57 | // accumulators[0] = addmod(accumulators[0], entropy[proofNumber], q); // that's the same as inputAccumulators[0] 58 | } 59 | 60 | // inputs for scalar multiplication 61 | uint256[3] memory mul_input; 62 | bool success; 63 | 64 | // use scalar multiplications to get proof.A[i] * entropy[i] 65 | 66 | proofsAandC = new uint256[](num_proofs*2 + 2); 67 | 68 | proofsAandC[0] = in_proof[0]; 69 | proofsAandC[1] = in_proof[1]; 70 | 71 | for (proofNumber = 1; proofNumber < num_proofs; proofNumber++) { 72 | mul_input[0] = in_proof[proofNumber*8]; 73 | mul_input[1] = in_proof[proofNumber*8 + 1]; 74 | mul_input[2] = entropy[proofNumber]; 75 | assembly { 76 | // ECMUL, output proofsA[i] 77 | // success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add(proofsAandC, 0x20), mul(proofNumber, 0x40)), 0x40) 78 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 79 | } 80 | proofsAandC[proofNumber*2] = mul_input[0]; 81 | proofsAandC[proofNumber*2 + 1] = mul_input[1]; 82 | require(success, "Failed to call a precompile"); 83 | } 84 | 85 | // use scalar multiplication and addition to get sum(proof.C[i] * entropy[i]) 86 | 87 | uint256[4] memory add_input; 88 | 89 | add_input[0] = in_proof[6]; 90 | add_input[1] = in_proof[7]; 91 | 92 | for (proofNumber = 1; proofNumber < num_proofs; proofNumber++) { 93 | mul_input[0] = in_proof[proofNumber*8 + 6]; 94 | mul_input[1] = in_proof[proofNumber*8 + 7]; 95 | mul_input[2] = entropy[proofNumber]; 96 | assembly { 97 | // ECMUL, output proofsA 98 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 99 | } 100 | require(success, "Failed to call a precompile for G1 multiplication for Proof C"); 101 | 102 | assembly { 103 | // ECADD from two elements that are in add_input and output into first two elements of add_input 104 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 105 | } 106 | require(success, "Failed to call a precompile for G1 addition for Proof C"); 107 | } 108 | 109 | proofsAandC[num_proofs*2] = add_input[0]; 110 | proofsAandC[num_proofs*2 + 1] = add_input[1]; 111 | } 112 | 113 | function prepareBatches( 114 | uint256[14] in_vk, 115 | uint256[] vk_gammaABC, 116 | uint256[] inputAccumulators 117 | ) internal view returns ( 118 | uint256[4] finalVksAlphaX 119 | ) { 120 | // Compute the linear combination vk_x using accumulator 121 | // First two fields are used as the sum and are initially zero 122 | uint256[4] memory add_input; 123 | uint256[3] memory mul_input; 124 | bool success; 125 | 126 | // Performs a sum(gammaABC[i] * inputAccumulator[i]) 127 | for (uint256 i = 0; i < inputAccumulators.length; i++) { 128 | mul_input[0] = vk_gammaABC[2*i]; 129 | mul_input[1] = vk_gammaABC[2*i + 1]; 130 | mul_input[2] = inputAccumulators[i]; 131 | 132 | assembly { 133 | // ECMUL, output to the last 2 elements of `add_input` 134 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 135 | } 136 | require(success, "Failed to call a precompile for G1 multiplication for input accumulator"); 137 | 138 | assembly { 139 | // ECADD from four elements that are in add_input and output into first two elements of add_input 140 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 141 | } 142 | require(success, "Failed to call a precompile for G1 addition for input accumulator"); 143 | } 144 | 145 | finalVksAlphaX[2] = add_input[0]; 146 | finalVksAlphaX[3] = add_input[1]; 147 | 148 | // add one extra memory slot for scalar for multiplication usage 149 | uint256[3] memory finalVKalpha; 150 | finalVKalpha[0] = in_vk[0]; 151 | finalVKalpha[1] = in_vk[1]; 152 | finalVKalpha[2] = inputAccumulators[0]; 153 | 154 | assembly { 155 | // ECMUL, output to first 2 elements of finalVKalpha 156 | success := staticcall(sub(gas, 2000), 7, finalVKalpha, 0x60, finalVKalpha, 0x40) 157 | } 158 | require(success, "Failed to call a precompile for G1 multiplication"); 159 | finalVksAlphaX[0] = finalVKalpha[0]; 160 | finalVksAlphaX[1] = finalVKalpha[1]; 161 | } 162 | 163 | // original equation 164 | // e(proof.A, proof.B)*e(-vk.alpha, vk.beta)*e(-vk_x, vk.gamma)*e(-proof.C, vk.delta) == 1 165 | // accumulation of inputs 166 | // gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 167 | 168 | function verifyBatch ( 169 | uint256[14] in_vk, // verifying key is always constant number of elements 170 | uint256[] vk_gammaABC, // variable length, depends on number of inputs 171 | uint256[] in_proof, // proof itself, length is 8 * num_proofs 172 | uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 173 | uint256 num_proofs 174 | ) 175 | public 176 | // view 177 | returns (bool success) 178 | { 179 | require(in_proof.length == 8 * num_proofs, "Invalid proofs length for a batch"); 180 | require(proof_inputs.length % num_proofs == 0, "Invalid inputs length for a batch"); 181 | require(((vk_gammaABC.length / 2) - 1) == proof_inputs.length / num_proofs, "Mismatching number of inputs for verifying key"); 182 | 183 | // strategy is to accumulate entropy separately for all the "constant" elements 184 | // (accumulate only for G1, can't in G2) of the pairing equation, as well as input verification key, 185 | // postpone scalar multiplication as much as possible and check only one equation 186 | // by using 3+num_proofs pairings only 187 | 188 | uint256[] memory proofsAandC; 189 | uint256[] memory inputAccumulators; 190 | (proofsAandC, inputAccumulators) = accumulate(in_proof, proof_inputs, num_proofs); 191 | 192 | uint256[4] memory finalVksAlphaX = prepareBatches(in_vk, vk_gammaABC, inputAccumulators); 193 | 194 | uint256[] memory inputs = new uint256[](6*num_proofs + 18); 195 | // first num_proofs pairings e(ProofA, ProofB) 196 | for (uint256 proofNumber = 0; proofNumber < num_proofs; proofNumber++) { 197 | inputs[proofNumber*6] = proofsAandC[proofNumber*2]; 198 | inputs[proofNumber*6 + 1] = proofsAandC[proofNumber*2 + 1]; 199 | inputs[proofNumber*6 + 2] = in_proof[proofNumber*8 + 2]; 200 | inputs[proofNumber*6 + 3] = in_proof[proofNumber*8 + 3]; 201 | inputs[proofNumber*6 + 4] = in_proof[proofNumber*8 + 4]; 202 | inputs[proofNumber*6 + 5] = in_proof[proofNumber*8 + 5]; 203 | } 204 | 205 | // second pairing e(-finalVKaplha, vk.beta) 206 | inputs[num_proofs*6] = finalVksAlphaX[0]; 207 | inputs[num_proofs*6 + 1] = NegateY(finalVksAlphaX[1]); 208 | inputs[num_proofs*6 + 2] = in_vk[2]; 209 | inputs[num_proofs*6 + 3] = in_vk[3]; 210 | inputs[num_proofs*6 + 4] = in_vk[4]; 211 | inputs[num_proofs*6 + 5] = in_vk[5]; 212 | 213 | // third pairing e(-finalVKx, vk.gamma) 214 | inputs[num_proofs*6 + 6] = finalVksAlphaX[2]; 215 | inputs[num_proofs*6 + 7] = NegateY(finalVksAlphaX[3]); 216 | inputs[num_proofs*6 + 8] = in_vk[6]; 217 | inputs[num_proofs*6 + 9] = in_vk[7]; 218 | inputs[num_proofs*6 + 10] = in_vk[8]; 219 | inputs[num_proofs*6 + 11] = in_vk[9]; 220 | 221 | // fourth pairing e(-proof.C, finalVKdelta) 222 | inputs[num_proofs*6 + 12] = proofsAandC[num_proofs*2]; 223 | inputs[num_proofs*6 + 13] = NegateY(proofsAandC[num_proofs*2 + 1]); 224 | inputs[num_proofs*6 + 14] = in_vk[10]; 225 | inputs[num_proofs*6 + 15] = in_vk[11]; 226 | inputs[num_proofs*6 + 16] = in_vk[12]; 227 | inputs[num_proofs*6 + 17] = in_vk[13]; 228 | 229 | uint256 inputsLength = inputs.length * 32; 230 | uint[1] memory out; 231 | require(inputsLength % 192 == 0, "Inputs length should be multiple of 192 bytes"); 232 | 233 | for (uint256 i = 0; i < inputs.length; i++) { 234 | emit DebugEvent(i, inputs[i]); 235 | } 236 | // return true; 237 | assembly { 238 | success := staticcall(sub(gas, 2000), 8, add(inputs, 0x20), inputsLength, out, 0x20) 239 | } 240 | require(success, "Failed to call pairings functions"); 241 | emit DebugEvent(1024, out[0]); 242 | return out[0] == 1; 243 | } 244 | 245 | function verify ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) 246 | public 247 | // view 248 | returns (bool) 249 | { 250 | require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length ); 251 | 252 | // Compute the linear combination vk_x 253 | uint256[3] memory mul_input; 254 | uint256[4] memory add_input; 255 | bool success; 256 | uint m = 2; 257 | 258 | // First two fields are used as the sum 259 | add_input[0] = vk_gammaABC[0]; 260 | add_input[1] = vk_gammaABC[1]; 261 | 262 | // Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 263 | for (uint i = 0; i < proof_inputs.length; i++) { 264 | mul_input[0] = vk_gammaABC[m++]; 265 | mul_input[1] = vk_gammaABC[m++]; 266 | mul_input[2] = proof_inputs[i]; 267 | 268 | assembly { 269 | // ECMUL, output to last 2 elements of `add_input` 270 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x80, add(add_input, 0x40), 0x60) 271 | } 272 | require( success ); 273 | 274 | assembly { 275 | // ECADD 276 | success := staticcall(sub(gas, 2000), 6, add_input, 0xc0, add_input, 0x60) 277 | } 278 | require( success ); 279 | } 280 | 281 | uint[24] memory input = [ 282 | // (proof.A, proof.B) 283 | in_proof[0], in_proof[1], // proof.A (G1) 284 | in_proof[2], in_proof[3], in_proof[4], in_proof[5], // proof.B (G2) 285 | 286 | // (-vk.alpha, vk.beta) 287 | in_vk[0], NegateY(in_vk[1]), // -vk.alpha (G1) 288 | in_vk[2], in_vk[3], in_vk[4], in_vk[5], // vk.beta (G2) 289 | 290 | // (-vk_x, vk.gamma) 291 | add_input[0], NegateY(add_input[1]), // -vk_x (G1) 292 | in_vk[6], in_vk[7], in_vk[8], in_vk[9], // vk.gamma (G2) 293 | 294 | // (-proof.C, vk.delta) 295 | in_proof[6], NegateY(in_proof[7]), // -proof.C (G1) 296 | in_vk[10], in_vk[11], in_vk[12], in_vk[13] // vk.delta (G2) 297 | ]; 298 | 299 | for (i = 0; i < 24; i++) { 300 | emit DebugEvent(i, input[i]); 301 | } 302 | 303 | uint[1] memory out; 304 | assembly { 305 | success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20) 306 | } 307 | require(success); 308 | return out[0] == 1; 309 | } 310 | 311 | function verifyInPower ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) 312 | public 313 | // view 314 | returns (bool) 315 | { 316 | require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length ); 317 | // uint256 r = uint256(blockhash(block.number - 1)) % 21888242871839275222246405745257275088548364400416034343698204186575808495617; 318 | uint256 r = 7; 319 | require(r != 0, "Entropy should be non-zero"); 320 | // Compute the linear combination vk_x 321 | uint256[3] memory mul_input; 322 | uint256[4] memory add_input; 323 | bool success; 324 | uint m = 2; 325 | 326 | // First two fields are used as the sum 327 | add_input[0] = vk_gammaABC[0]; 328 | add_input[1] = vk_gammaABC[1]; 329 | 330 | // Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 331 | for (uint i = 0; i < proof_inputs.length; i++) { 332 | mul_input[0] = vk_gammaABC[m++]; 333 | mul_input[1] = vk_gammaABC[m++]; 334 | mul_input[2] = proof_inputs[i]; 335 | 336 | assembly { 337 | // ECMUL, output to last 2 elements of `add_input` 338 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 339 | } 340 | require( success ); 341 | 342 | assembly { 343 | // ECADD 344 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 345 | } 346 | require( success ); 347 | } 348 | 349 | uint[24] memory input; 350 | 351 | // (r*proof.A, proof.B) 352 | mul_input[0] = in_proof[0]; 353 | mul_input[1] = in_proof[1]; 354 | mul_input[2] = r; 355 | assembly { 356 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 357 | } 358 | require( success ); 359 | 360 | input[0] = mul_input[0]; 361 | input[1] = mul_input[1]; 362 | input[2] = in_proof[2]; 363 | input[3] = in_proof[3]; 364 | input[4] = in_proof[4]; 365 | input[5] = in_proof[5]; 366 | 367 | // (-r*vk.alpha, vk.beta) 368 | mul_input[0] = in_vk[0]; 369 | mul_input[1] = in_vk[1]; 370 | mul_input[2] = r; 371 | assembly { 372 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 373 | } 374 | require( success ); 375 | 376 | input[6] = mul_input[0]; 377 | input[7] = NegateY(mul_input[1]); 378 | input[8] = in_vk[2]; 379 | input[9] = in_vk[3]; 380 | input[10] = in_vk[4]; 381 | input[11] = in_vk[5]; 382 | 383 | // (-r*vk_x, vk.gamma) 384 | mul_input[0] = add_input[0]; 385 | mul_input[1] = add_input[1]; 386 | mul_input[2] = r; 387 | assembly { 388 | // ECMUL, output to last 2 elements of `add_input` 389 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 390 | } 391 | require( success ); 392 | 393 | input[12] = mul_input[0]; 394 | input[13] = NegateY(mul_input[1]); 395 | input[14] = in_vk[6]; 396 | input[15] = in_vk[7]; 397 | input[16] = in_vk[8]; 398 | input[17] = in_vk[9]; 399 | 400 | // (-r*proof.C, vk.delta) 401 | mul_input[0] = in_proof[6]; 402 | mul_input[1] = in_proof[7]; 403 | mul_input[2] = r; 404 | assembly { 405 | // ECMUL, output to last 2 elements of `add_input` 406 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 407 | } 408 | require( success ); 409 | 410 | input[18] = mul_input[0]; 411 | input[19] = NegateY(mul_input[1]); 412 | input[20] = in_vk[10]; 413 | input[21] = in_vk[11]; 414 | input[22] = in_vk[12]; 415 | input[23] = in_vk[13]; 416 | 417 | for (i = 0; i < 24; i++) { 418 | emit DebugEvent(i, input[i]); 419 | } 420 | uint[1] memory out; 421 | assembly { 422 | success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20) 423 | } 424 | require(success, "Pairing precompile call failed"); 425 | return out[0] == 1; 426 | } 427 | } -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.25; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/Pairing.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.25; 2 | 3 | library Pairing { 4 | struct G1Point { 5 | uint X; 6 | uint Y; 7 | } 8 | 9 | // Encoding of field elements is: X[0] * z + X[1] 10 | struct G2Point { 11 | uint[2] X; 12 | uint[2] Y; 13 | } 14 | 15 | /// @return the generator of G1 16 | function P1() internal pure returns (G1Point) { 17 | return G1Point(1, 2); 18 | } 19 | 20 | /// @return the generator of G2 21 | function P2() internal pure returns (G2Point) { 22 | return G2Point( 23 | [11559732032986387107991004021392285783925812861821192530917403151452391805634, 24 | 10857046999023057135944570762232829481370756359578518086990519993285655852781], 25 | [4082367875863433681332203403145435568316851327593401208105741076214120093531, 26 | 8495653923123431417604973247489272438418190587263600148770280649306958101930] 27 | ); 28 | } 29 | 30 | /// @return the negation of p, i.e. p.add(p.negate()) should be zero. 31 | function negate(G1Point p) internal pure returns (G1Point) { 32 | // The prime q in the base field F_q for G1 33 | uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 34 | if (p.X == 0 && p.Y == 0) 35 | return G1Point(0, 0); 36 | return G1Point(p.X, q - (p.Y % q)); 37 | } 38 | 39 | /// @return the sum of two points of G1 40 | function pointAdd(G1Point p1, G1Point p2) 41 | internal view returns (G1Point r) 42 | { 43 | uint[4] memory input; 44 | input[0] = p1.X; 45 | input[1] = p1.Y; 46 | input[2] = p2.X; 47 | input[3] = p2.Y; 48 | bool success; 49 | assembly { 50 | success := staticcall(sub(gas, 2000), 6, input, 0xc0, r, 0x60) 51 | } 52 | require(success); 53 | } 54 | 55 | /// @return the product of a point on G1 and a scalar, i.e. 56 | /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. 57 | function pointMul(G1Point p, uint s) 58 | internal view returns (G1Point r) 59 | { 60 | uint[3] memory input; 61 | input[0] = p.X; 62 | input[1] = p.Y; 63 | input[2] = s; 64 | bool success; 65 | assembly { 66 | success := staticcall(sub(gas, 2000), 7, input, 0x80, r, 0x60) 67 | } 68 | require (success); 69 | } 70 | 71 | /// @return the result of computing the pairing check 72 | /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 73 | /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should 74 | /// return true. 75 | function pairing(G1Point[] p1, G2Point[] p2) 76 | internal view returns (bool) 77 | { 78 | require(p1.length == p2.length); 79 | uint elements = p1.length; 80 | uint inputSize = elements * 6; 81 | uint[] memory input = new uint[](inputSize); 82 | for (uint i = 0; i < elements; i++) 83 | { 84 | input[i * 6 + 0] = p1[i].X; 85 | input[i * 6 + 1] = p1[i].Y; 86 | input[i * 6 + 2] = p2[i].X[0]; 87 | input[i * 6 + 3] = p2[i].X[1]; 88 | input[i * 6 + 4] = p2[i].Y[0]; 89 | input[i * 6 + 5] = p2[i].Y[1]; 90 | } 91 | uint[1] memory out; 92 | bool success; 93 | assembly { 94 | success := staticcall(sub(gas, 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) 95 | } 96 | require(success); 97 | return out[0] != 0; 98 | } 99 | 100 | /// Convenience method for a pairing check for two pairs. 101 | function pairingProd2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) 102 | internal view returns (bool) 103 | { 104 | G1Point[] memory p1 = new G1Point[](2); 105 | G2Point[] memory p2 = new G2Point[](2); 106 | p1[0] = a1; 107 | p1[1] = b1; 108 | p2[0] = a2; 109 | p2[1] = b2; 110 | return pairing(p1, p2); 111 | } 112 | /// Convenience method for a pairing check for three pairs. 113 | function pairingProd3( 114 | G1Point a1, G2Point a2, 115 | G1Point b1, G2Point b2, 116 | G1Point c1, G2Point c2 117 | ) 118 | internal view returns (bool) 119 | { 120 | G1Point[] memory p1 = new G1Point[](3); 121 | G2Point[] memory p2 = new G2Point[](3); 122 | p1[0] = a1; 123 | p1[1] = b1; 124 | p1[2] = c1; 125 | p2[0] = a2; 126 | p2[1] = b2; 127 | p2[2] = c2; 128 | return pairing(p1, p2); 129 | } 130 | 131 | /// Convenience method for a pairing check for four pairs. 132 | function pairingProd4( 133 | G1Point a1, G2Point a2, 134 | G1Point b1, G2Point b2, 135 | G1Point c1, G2Point c2, 136 | G1Point d1, G2Point d2 137 | ) 138 | internal view returns (bool) 139 | { 140 | G1Point[] memory p1 = new G1Point[](4); 141 | G2Point[] memory p2 = new G2Point[](4); 142 | p1[0] = a1; 143 | p1[1] = b1; 144 | p1[2] = c1; 145 | p1[3] = d1; 146 | p2[0] = a2; 147 | p2[1] = b2; 148 | p2[2] = c2; 149 | p2[3] = d2; 150 | return pairing(p1, p2); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/Verifier.sol: -------------------------------------------------------------------------------- 1 | // this code is taken from https://github.com/JacobEberhardt/ZoKrates 2 | 3 | pragma solidity 0.4.25; 4 | 5 | import "./Pairing.sol"; 6 | 7 | library Verifier 8 | { 9 | using Pairing for Pairing.G1Point; 10 | using Pairing for Pairing.G2Point; 11 | 12 | function ScalarField () 13 | public pure returns (uint256) 14 | { 15 | return 21888242871839275222246405745257275088548364400416034343698204186575808495617; 16 | } 17 | 18 | struct VerifyingKey 19 | { 20 | Pairing.G1Point alpha; 21 | Pairing.G2Point beta; 22 | Pairing.G2Point gamma; 23 | Pairing.G2Point delta; 24 | Pairing.G1Point[] gammaABC; 25 | } 26 | 27 | struct Proof 28 | { 29 | Pairing.G1Point A; 30 | Pairing.G2Point B; 31 | Pairing.G1Point C; 32 | } 33 | 34 | struct ProofWithInput 35 | { 36 | Proof proof; 37 | uint256[] input; 38 | } 39 | 40 | 41 | function NegateY( uint256 Y ) 42 | internal pure returns (uint256) 43 | { 44 | uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 45 | return q - (Y % q); 46 | } 47 | 48 | 49 | /* 50 | * This implements the Solidity equivalent of the following Python code: 51 | 52 | from py_ecc.bn128 import * 53 | 54 | data = # ... arguments to function [in_vk, vk_gammaABC, in_proof, proof_inputs] 55 | 56 | vk = [int(_, 16) for _ in data[0]] 57 | ic = [FQ(int(_, 16)) for _ in data[1]] 58 | proof = [int(_, 16) for _ in data[2]] 59 | inputs = [int(_, 16) for _ in data[3]] 60 | 61 | it = iter(ic) 62 | ic = [(_, next(it)) for _ in it] 63 | vk_alpha = [FQ(_) for _ in vk[:2]] 64 | vk_beta = (FQ2(vk[2:4][::-1]), FQ2(vk[4:6][::-1])) 65 | vk_gamma = (FQ2(vk[6:8][::-1]), FQ2(vk[8:10][::-1])) 66 | vk_delta = (FQ2(vk[10:12][::-1]), FQ2(vk[12:14][::-1])) 67 | 68 | assert is_on_curve(vk_alpha, b) 69 | assert is_on_curve(vk_beta, b2) 70 | assert is_on_curve(vk_gamma, b2) 71 | assert is_on_curve(vk_delta, b2) 72 | 73 | proof_A = [FQ(_) for _ in proof[:2]] 74 | proof_B = (FQ2(proof[2:4][::-1]), FQ2(proof[4:-2][::-1])) 75 | proof_C = [FQ(_) for _ in proof[-2:]] 76 | 77 | assert is_on_curve(proof_A, b) 78 | assert is_on_curve(proof_B, b2) 79 | assert is_on_curve(proof_C, b) 80 | 81 | vk_x = ic[0] 82 | for i, s in enumerate(inputs): 83 | vk_x = add(vk_x, multiply(ic[i + 1], s)) 84 | 85 | check_1 = pairing(proof_B, proof_A) 86 | check_2 = pairing(vk_beta, neg(vk_alpha)) 87 | check_3 = pairing(vk_gamma, neg(vk_x)) 88 | check_4 = pairing(vk_delta, neg(proof_C)) 89 | 90 | ok = check_1 * check_2 * check_3 * check_4 91 | assert ok == FQ12.one() 92 | */ 93 | function Verify ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) 94 | internal 95 | view 96 | returns (bool) 97 | { 98 | require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length, "Invalid number of public inputs" ); 99 | 100 | // Compute the linear combination vk_x 101 | uint256[3] memory mul_input; 102 | uint256[4] memory add_input; 103 | bool success; 104 | uint m = 2; 105 | 106 | // First two fields are used as the sum 107 | add_input[0] = vk_gammaABC[0]; 108 | add_input[1] = vk_gammaABC[1]; 109 | 110 | // Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 111 | for (uint i = 0; i < proof_inputs.length; i++) { 112 | mul_input[0] = vk_gammaABC[m++]; 113 | mul_input[1] = vk_gammaABC[m++]; 114 | mul_input[2] = proof_inputs[i]; 115 | 116 | assembly { 117 | // ECMUL, output to last 2 elements of `add_input` 118 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 119 | } 120 | require( success, "Failed to call ECMUL precompile" ); 121 | 122 | assembly { 123 | // ECADD 124 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 125 | } 126 | require( success, "Failed to call ECADD precompile" ); 127 | } 128 | 129 | uint[24] memory input = [ 130 | // (proof.A, proof.B) 131 | in_proof[0], in_proof[1], // proof.A (G1) 132 | in_proof[2], in_proof[3], in_proof[4], in_proof[5], // proof.B (G2) 133 | 134 | // (-vk.alpha, vk.beta) 135 | in_vk[0], NegateY(in_vk[1]), // -vk.alpha (G1) 136 | in_vk[2], in_vk[3], in_vk[4], in_vk[5], // vk.beta (G2) 137 | 138 | // (-vk_x, vk.gamma) 139 | add_input[0], NegateY(add_input[1]), // -vk_x (G1) 140 | in_vk[6], in_vk[7], in_vk[8], in_vk[9], // vk.gamma (G2) 141 | 142 | // (-proof.C, vk.delta) 143 | in_proof[6], NegateY(in_proof[7]), // -proof.C (G1) 144 | in_vk[10], in_vk[11], in_vk[12], in_vk[13] // vk.delta (G2) 145 | ]; 146 | 147 | uint[1] memory out; 148 | assembly { 149 | success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20) 150 | } 151 | require(success, "Failed to call pairing precompile"); 152 | return out[0] == 1; 153 | } 154 | 155 | function VerifyInPower ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) 156 | internal 157 | view 158 | returns (bool) 159 | { 160 | require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length, "Invalid number of public inputs" ); 161 | uint256 r = uint256(blockhash(block.number - 1)) % 21888242871839275222246405745257275088696311157297823662689037894645226208583; 162 | require(r != 0, "Entropy should be non-zero"); 163 | // Compute the linear combination vk_x 164 | uint256[3] memory mul_input; 165 | uint256[4] memory add_input; 166 | bool success; 167 | uint m = 2; 168 | 169 | // First two fields are used as the sum 170 | add_input[0] = vk_gammaABC[0]; 171 | add_input[1] = vk_gammaABC[1]; 172 | 173 | // Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ] 174 | for (uint i = 0; i < proof_inputs.length; i++) { 175 | mul_input[0] = vk_gammaABC[m++]; 176 | mul_input[1] = vk_gammaABC[m++]; 177 | mul_input[2] = proof_inputs[i]; 178 | 179 | assembly { 180 | // ECMUL, output to last 2 elements of `add_input` 181 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, add(add_input, 0x40), 0x40) 182 | } 183 | require( success, "Failed to call ECMUL precompile" ); 184 | 185 | assembly { 186 | // ECADD 187 | success := staticcall(sub(gas, 2000), 6, add_input, 0x80, add_input, 0x40) 188 | } 189 | require( success, "Failed to call ECADD precompile" ); 190 | } 191 | 192 | uint[24] memory input; 193 | 194 | // (r*proof.A, proof.B) 195 | mul_input[0] = in_proof[0]; 196 | mul_input[1] = in_proof[1]; 197 | mul_input[2] = r; 198 | assembly { 199 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 200 | } 201 | require( success, "Failed to call ECMUL precompile" ); 202 | 203 | input[0] = mul_input[0]; 204 | input[1] = mul_input[1]; 205 | input[2] = in_proof[2]; 206 | input[3] = in_proof[3]; 207 | input[4] = in_proof[4]; 208 | input[5] = in_proof[5]; 209 | 210 | // (-r*vk.alpha, vk.beta) 211 | mul_input[0] = in_vk[0]; 212 | mul_input[1] = in_vk[1]; 213 | mul_input[2] = r; 214 | assembly { 215 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 216 | } 217 | require( success, "Failed to call ECMUL precompile" ); 218 | 219 | input[6] = mul_input[0]; 220 | input[7] = NegateY(mul_input[1]); 221 | input[8] = in_vk[2]; 222 | input[9] = in_vk[3]; 223 | input[10] = in_vk[4]; 224 | input[11] = in_vk[5]; 225 | 226 | // (-r*vk_x, vk.gamma) 227 | mul_input[0] = add_input[0]; 228 | mul_input[1] = add_input[1]; 229 | mul_input[2] = r; 230 | assembly { 231 | // ECMUL, output to last 2 elements of `add_input` 232 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 233 | } 234 | require( success, "Failed to call ECMUL precompile" ); 235 | 236 | input[12] = mul_input[0]; 237 | input[13] = NegateY(mul_input[1]); 238 | input[14] = in_vk[6]; 239 | input[15] = in_vk[7]; 240 | input[16] = in_vk[8]; 241 | input[17] = in_vk[9]; 242 | 243 | // (-r*proof.C, vk.delta) 244 | mul_input[0] = in_proof[6]; 245 | mul_input[1] = in_proof[7]; 246 | mul_input[2] = r; 247 | assembly { 248 | // ECMUL, output to last 2 elements of `add_input` 249 | success := staticcall(sub(gas, 2000), 7, mul_input, 0x60, mul_input, 0x40) 250 | } 251 | require( success, "Failed to call ECMUL precompile" ); 252 | 253 | input[18] = mul_input[0]; 254 | input[19] = NegateY(mul_input[1]); 255 | input[20] = in_vk[10]; 256 | input[21] = in_vk[11]; 257 | input[22] = in_vk[12]; 258 | input[23] = in_vk[13]; 259 | 260 | uint[1] memory out; 261 | assembly { 262 | success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20) 263 | } 264 | require(success, "Pairing precompile call failed"); 265 | return out[0] == 1; 266 | } 267 | 268 | 269 | function Verify (VerifyingKey memory vk, ProofWithInput memory pwi) 270 | internal view returns (bool) 271 | { 272 | return Verify(vk, pwi.proof, pwi.input); 273 | } 274 | 275 | 276 | function Verify (VerifyingKey memory vk, Proof memory proof, uint256[] memory input) 277 | internal view returns (bool) 278 | { 279 | require(input.length + 1 == vk.gammaABC.length); 280 | 281 | // Compute the linear combination vk_x 282 | Pairing.G1Point memory vk_x = vk.gammaABC[0]; 283 | for (uint i = 0; i < input.length; i++) 284 | vk_x = Pairing.pointAdd(vk_x, Pairing.pointMul(vk.gammaABC[i + 1], input[i])); 285 | 286 | // Verify proof 287 | return Pairing.pairingProd4( 288 | proof.A, proof.B, 289 | vk_x.negate(), vk.gamma, 290 | proof.C.negate(), vk.delta, 291 | vk.alpha.negate(), vk.beta); 292 | } 293 | } -------------------------------------------------------------------------------- /BatchedSnarkVerifier/contracts/Wrapper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.25; 2 | 3 | import "./BatchVerifier.sol"; 4 | import "./Verifier.sol"; 5 | 6 | contract Wrapper { 7 | constructor() public { 8 | 9 | } 10 | 11 | function verifyBatch( 12 | uint256[14] in_vk, // verifying key is always constant number of elements 13 | uint256[] vk_gammaABC, // variable length, depends on number of inputs 14 | uint256[] in_proof, // proof itself, length is 8 * num_proofs 15 | uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 16 | uint256 num_proofs 17 | ) 18 | public 19 | view 20 | returns (bool success) { 21 | return BatchVerifier.BatchVerify(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs); 22 | } 23 | 24 | function verify( 25 | uint256[14] in_vk, // verifying key is always constant number of elements 26 | uint256[] vk_gammaABC, // variable length, depends on number of inputs 27 | uint256[8] in_proof, // proof itself, length is 8 * num_proofs 28 | uint256[] proof_inputs // public inputs, length is num_inputs * num_proofs 29 | ) 30 | public 31 | view 32 | returns (bool success) { 33 | return Verifier.Verify(in_vk, vk_gammaABC, in_proof, proof_inputs); 34 | } 35 | 36 | function verifyInPower( 37 | uint256[14] in_vk, // verifying key is always constant number of elements 38 | uint256[] vk_gammaABC, // variable length, depends on number of inputs 39 | uint256[8] in_proof, // proof itself, length is 8 * num_proofs 40 | uint256[] proof_inputs // public inputs, length is num_inputs * num_proofs 41 | ) 42 | public 43 | view 44 | returns (bool success) { 45 | return Verifier.VerifyInPower(in_vk, vk_gammaABC, in_proof, proof_inputs); 46 | } 47 | } -------------------------------------------------------------------------------- /BatchedSnarkVerifier/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batchedsnarkverifier", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-regex": { 8 | "version": "2.1.1", 9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 10 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 11 | }, 12 | "balanced-match": { 13 | "version": "1.0.0", 14 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 15 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 16 | }, 17 | "bn.js": { 18 | "version": "4.11.8", 19 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", 20 | "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" 21 | }, 22 | "brace-expansion": { 23 | "version": "1.1.11", 24 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 25 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 26 | "requires": { 27 | "balanced-match": "^1.0.0", 28 | "concat-map": "0.0.1" 29 | } 30 | }, 31 | "browser-stdout": { 32 | "version": "1.3.0", 33 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 34 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" 35 | }, 36 | "builtin-modules": { 37 | "version": "1.1.1", 38 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 39 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" 40 | }, 41 | "camelcase": { 42 | "version": "3.0.0", 43 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", 44 | "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" 45 | }, 46 | "cliui": { 47 | "version": "3.2.0", 48 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 49 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 50 | "requires": { 51 | "string-width": "^1.0.1", 52 | "strip-ansi": "^3.0.1", 53 | "wrap-ansi": "^2.0.0" 54 | } 55 | }, 56 | "code-point-at": { 57 | "version": "1.1.0", 58 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 59 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 60 | }, 61 | "commander": { 62 | "version": "2.11.0", 63 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 64 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==" 65 | }, 66 | "concat-map": { 67 | "version": "0.0.1", 68 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 69 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 70 | }, 71 | "debug": { 72 | "version": "3.1.0", 73 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 74 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 75 | "requires": { 76 | "ms": "2.0.0" 77 | } 78 | }, 79 | "decamelize": { 80 | "version": "1.2.0", 81 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 82 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 83 | }, 84 | "diff": { 85 | "version": "3.3.1", 86 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 87 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==" 88 | }, 89 | "error-ex": { 90 | "version": "1.3.2", 91 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 92 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 93 | "requires": { 94 | "is-arrayish": "^0.2.1" 95 | } 96 | }, 97 | "escape-string-regexp": { 98 | "version": "1.0.5", 99 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 100 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 101 | }, 102 | "find-up": { 103 | "version": "1.1.2", 104 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", 105 | "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", 106 | "requires": { 107 | "path-exists": "^2.0.0", 108 | "pinkie-promise": "^2.0.0" 109 | } 110 | }, 111 | "fs-extra": { 112 | "version": "0.30.0", 113 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", 114 | "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", 115 | "requires": { 116 | "graceful-fs": "^4.1.2", 117 | "jsonfile": "^2.1.0", 118 | "klaw": "^1.0.0", 119 | "path-is-absolute": "^1.0.0", 120 | "rimraf": "^2.2.8" 121 | } 122 | }, 123 | "fs.realpath": { 124 | "version": "1.0.0", 125 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 126 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 127 | }, 128 | "get-caller-file": { 129 | "version": "1.0.3", 130 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", 131 | "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" 132 | }, 133 | "glob": { 134 | "version": "7.1.2", 135 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 136 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 137 | "requires": { 138 | "fs.realpath": "^1.0.0", 139 | "inflight": "^1.0.4", 140 | "inherits": "2", 141 | "minimatch": "^3.0.4", 142 | "once": "^1.3.0", 143 | "path-is-absolute": "^1.0.0" 144 | } 145 | }, 146 | "graceful-fs": { 147 | "version": "4.1.15", 148 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", 149 | "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" 150 | }, 151 | "growl": { 152 | "version": "1.10.3", 153 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 154 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==" 155 | }, 156 | "has-flag": { 157 | "version": "2.0.0", 158 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 159 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" 160 | }, 161 | "he": { 162 | "version": "1.1.1", 163 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 164 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" 165 | }, 166 | "hosted-git-info": { 167 | "version": "2.7.1", 168 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", 169 | "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" 170 | }, 171 | "inflight": { 172 | "version": "1.0.6", 173 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 174 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 175 | "requires": { 176 | "once": "^1.3.0", 177 | "wrappy": "1" 178 | } 179 | }, 180 | "inherits": { 181 | "version": "2.0.3", 182 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 183 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 184 | }, 185 | "invert-kv": { 186 | "version": "1.0.0", 187 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 188 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 189 | }, 190 | "is-arrayish": { 191 | "version": "0.2.1", 192 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 193 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 194 | }, 195 | "is-builtin-module": { 196 | "version": "1.0.0", 197 | "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", 198 | "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", 199 | "requires": { 200 | "builtin-modules": "^1.0.0" 201 | } 202 | }, 203 | "is-fullwidth-code-point": { 204 | "version": "1.0.0", 205 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 206 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 207 | "requires": { 208 | "number-is-nan": "^1.0.0" 209 | } 210 | }, 211 | "is-utf8": { 212 | "version": "0.2.1", 213 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 214 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 215 | }, 216 | "jsonfile": { 217 | "version": "2.4.0", 218 | "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", 219 | "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", 220 | "requires": { 221 | "graceful-fs": "^4.1.6" 222 | } 223 | }, 224 | "klaw": { 225 | "version": "1.3.1", 226 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", 227 | "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", 228 | "requires": { 229 | "graceful-fs": "^4.1.9" 230 | } 231 | }, 232 | "lcid": { 233 | "version": "1.0.0", 234 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 235 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 236 | "requires": { 237 | "invert-kv": "^1.0.0" 238 | } 239 | }, 240 | "load-json-file": { 241 | "version": "1.1.0", 242 | "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", 243 | "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", 244 | "requires": { 245 | "graceful-fs": "^4.1.2", 246 | "parse-json": "^2.2.0", 247 | "pify": "^2.0.0", 248 | "pinkie-promise": "^2.0.0", 249 | "strip-bom": "^2.0.0" 250 | } 251 | }, 252 | "lodash.assign": { 253 | "version": "4.2.0", 254 | "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", 255 | "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" 256 | }, 257 | "memorystream": { 258 | "version": "0.3.1", 259 | "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", 260 | "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" 261 | }, 262 | "minimatch": { 263 | "version": "3.0.4", 264 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 265 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 266 | "requires": { 267 | "brace-expansion": "^1.1.7" 268 | } 269 | }, 270 | "minimist": { 271 | "version": "0.0.8", 272 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 273 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 274 | }, 275 | "mkdirp": { 276 | "version": "0.5.1", 277 | "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 278 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 279 | "requires": { 280 | "minimist": "0.0.8" 281 | } 282 | }, 283 | "mocha": { 284 | "version": "4.1.0", 285 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", 286 | "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", 287 | "requires": { 288 | "browser-stdout": "1.3.0", 289 | "commander": "2.11.0", 290 | "debug": "3.1.0", 291 | "diff": "3.3.1", 292 | "escape-string-regexp": "1.0.5", 293 | "glob": "7.1.2", 294 | "growl": "1.10.3", 295 | "he": "1.1.1", 296 | "mkdirp": "0.5.1", 297 | "supports-color": "4.4.0" 298 | } 299 | }, 300 | "ms": { 301 | "version": "2.0.0", 302 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 303 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 304 | }, 305 | "normalize-package-data": { 306 | "version": "2.4.0", 307 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", 308 | "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", 309 | "requires": { 310 | "hosted-git-info": "^2.1.4", 311 | "is-builtin-module": "^1.0.0", 312 | "semver": "2 || 3 || 4 || 5", 313 | "validate-npm-package-license": "^3.0.1" 314 | } 315 | }, 316 | "number-is-nan": { 317 | "version": "1.0.1", 318 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 319 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 320 | }, 321 | "once": { 322 | "version": "1.4.0", 323 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 324 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 325 | "requires": { 326 | "wrappy": "1" 327 | } 328 | }, 329 | "original-require": { 330 | "version": "1.0.1", 331 | "resolved": "https://registry.npmjs.org/original-require/-/original-require-1.0.1.tgz", 332 | "integrity": "sha1-DxMEcVhM0zURxew4yNWSE/msXiA=" 333 | }, 334 | "os-locale": { 335 | "version": "1.4.0", 336 | "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 337 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 338 | "requires": { 339 | "lcid": "^1.0.0" 340 | } 341 | }, 342 | "parse-json": { 343 | "version": "2.2.0", 344 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 345 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 346 | "requires": { 347 | "error-ex": "^1.2.0" 348 | } 349 | }, 350 | "path-exists": { 351 | "version": "2.1.0", 352 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", 353 | "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", 354 | "requires": { 355 | "pinkie-promise": "^2.0.0" 356 | } 357 | }, 358 | "path-is-absolute": { 359 | "version": "1.0.1", 360 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 361 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 362 | }, 363 | "path-type": { 364 | "version": "1.1.0", 365 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", 366 | "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", 367 | "requires": { 368 | "graceful-fs": "^4.1.2", 369 | "pify": "^2.0.0", 370 | "pinkie-promise": "^2.0.0" 371 | } 372 | }, 373 | "pify": { 374 | "version": "2.3.0", 375 | "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", 376 | "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" 377 | }, 378 | "pinkie": { 379 | "version": "2.0.4", 380 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 381 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 382 | }, 383 | "pinkie-promise": { 384 | "version": "2.0.1", 385 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 386 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 387 | "requires": { 388 | "pinkie": "^2.0.0" 389 | } 390 | }, 391 | "read-pkg": { 392 | "version": "1.1.0", 393 | "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", 394 | "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", 395 | "requires": { 396 | "load-json-file": "^1.0.0", 397 | "normalize-package-data": "^2.3.2", 398 | "path-type": "^1.0.0" 399 | } 400 | }, 401 | "read-pkg-up": { 402 | "version": "1.0.1", 403 | "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", 404 | "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", 405 | "requires": { 406 | "find-up": "^1.0.0", 407 | "read-pkg": "^1.0.0" 408 | } 409 | }, 410 | "require-directory": { 411 | "version": "2.1.1", 412 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 413 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 414 | }, 415 | "require-from-string": { 416 | "version": "1.2.1", 417 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", 418 | "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" 419 | }, 420 | "require-main-filename": { 421 | "version": "1.0.1", 422 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", 423 | "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" 424 | }, 425 | "rimraf": { 426 | "version": "2.6.2", 427 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", 428 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", 429 | "requires": { 430 | "glob": "^7.0.5" 431 | } 432 | }, 433 | "semver": { 434 | "version": "5.6.0", 435 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", 436 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" 437 | }, 438 | "set-blocking": { 439 | "version": "2.0.0", 440 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 441 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 442 | }, 443 | "solc": { 444 | "version": "0.4.25", 445 | "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.25.tgz", 446 | "integrity": "sha512-jU1YygRVy6zatgXrLY2rRm7HW1d7a8CkkEgNJwvH2VLpWhMFsMdWcJn6kUqZwcSz/Vm+w89dy7Z/aB5p6AFTrg==", 447 | "requires": { 448 | "fs-extra": "^0.30.0", 449 | "memorystream": "^0.3.1", 450 | "require-from-string": "^1.1.0", 451 | "semver": "^5.3.0", 452 | "yargs": "^4.7.1" 453 | } 454 | }, 455 | "spdx-correct": { 456 | "version": "3.0.2", 457 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", 458 | "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", 459 | "requires": { 460 | "spdx-expression-parse": "^3.0.0", 461 | "spdx-license-ids": "^3.0.0" 462 | } 463 | }, 464 | "spdx-exceptions": { 465 | "version": "2.2.0", 466 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", 467 | "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" 468 | }, 469 | "spdx-expression-parse": { 470 | "version": "3.0.0", 471 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", 472 | "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", 473 | "requires": { 474 | "spdx-exceptions": "^2.1.0", 475 | "spdx-license-ids": "^3.0.0" 476 | } 477 | }, 478 | "spdx-license-ids": { 479 | "version": "3.0.2", 480 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", 481 | "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==" 482 | }, 483 | "string-width": { 484 | "version": "1.0.2", 485 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 486 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 487 | "requires": { 488 | "code-point-at": "^1.0.0", 489 | "is-fullwidth-code-point": "^1.0.0", 490 | "strip-ansi": "^3.0.0" 491 | } 492 | }, 493 | "strip-ansi": { 494 | "version": "3.0.1", 495 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 496 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 497 | "requires": { 498 | "ansi-regex": "^2.0.0" 499 | } 500 | }, 501 | "strip-bom": { 502 | "version": "2.0.0", 503 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 504 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 505 | "requires": { 506 | "is-utf8": "^0.2.0" 507 | } 508 | }, 509 | "supports-color": { 510 | "version": "4.4.0", 511 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 512 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 513 | "requires": { 514 | "has-flag": "^2.0.0" 515 | } 516 | }, 517 | "truffle": { 518 | "version": "5.0.0-beta.1", 519 | "resolved": "https://registry.npmjs.org/truffle/-/truffle-5.0.0-beta.1.tgz", 520 | "integrity": "sha512-U9vNwUAX0kb+pgpUWsWDFQeU2XeCHYm4swVxQdRi37mDQCOedEVgiE+Lr87LNQMAIuAY+8sG/9KF7HwVks0ncQ==", 521 | "requires": { 522 | "mocha": "^4.1.0", 523 | "original-require": "1.0.1", 524 | "solc": "0.4.25" 525 | } 526 | }, 527 | "validate-npm-package-license": { 528 | "version": "3.0.4", 529 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 530 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 531 | "requires": { 532 | "spdx-correct": "^3.0.0", 533 | "spdx-expression-parse": "^3.0.0" 534 | } 535 | }, 536 | "which-module": { 537 | "version": "1.0.0", 538 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", 539 | "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" 540 | }, 541 | "window-size": { 542 | "version": "0.2.0", 543 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", 544 | "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" 545 | }, 546 | "wrap-ansi": { 547 | "version": "2.1.0", 548 | "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 549 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 550 | "requires": { 551 | "string-width": "^1.0.1", 552 | "strip-ansi": "^3.0.1" 553 | } 554 | }, 555 | "wrappy": { 556 | "version": "1.0.2", 557 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 558 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 559 | }, 560 | "y18n": { 561 | "version": "3.2.1", 562 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 563 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 564 | }, 565 | "yargs": { 566 | "version": "4.8.1", 567 | "resolved": "http://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", 568 | "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", 569 | "requires": { 570 | "cliui": "^3.2.0", 571 | "decamelize": "^1.1.1", 572 | "get-caller-file": "^1.0.1", 573 | "lodash.assign": "^4.0.3", 574 | "os-locale": "^1.4.0", 575 | "read-pkg-up": "^1.0.1", 576 | "require-directory": "^2.1.1", 577 | "require-main-filename": "^1.0.1", 578 | "set-blocking": "^2.0.0", 579 | "string-width": "^1.0.1", 580 | "which-module": "^1.0.0", 581 | "window-size": "^0.2.0", 582 | "y18n": "^3.2.1", 583 | "yargs-parser": "^2.4.1" 584 | } 585 | }, 586 | "yargs-parser": { 587 | "version": "2.4.1", 588 | "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", 589 | "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", 590 | "requires": { 591 | "camelcase": "^3.0.0", 592 | "lodash.assign": "^4.0.6" 593 | } 594 | } 595 | } 596 | } 597 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "batchedsnarkverifier", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bn.js": "^4.11.8", 13 | "truffle": "^5.0.0-beta.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/test/threeInputs.js: -------------------------------------------------------------------------------- 1 | const Wrapper = artifacts.require('Wrapper'); 2 | const BatchVerifierContract = artifacts.require("BatchVerifierContract") 3 | 4 | contract('Batch verifier with three inputs', async (accounts) => { 5 | const BN = require("bn.js"); 6 | 7 | 8 | const P = new BN("21888242871839275222246405745257275088696311157297823662689037894645226208583") 9 | 10 | 11 | const account = accounts[0]; 12 | let wrapper; 13 | let verifier; 14 | 15 | beforeEach(async () => { 16 | wrapper = await Wrapper.new({from: account}) 17 | verifier = await BatchVerifierContract.new({from: account}) 18 | verifier = wrapper 19 | }) 20 | 21 | // const VK_STATIC = {'alpha': [ 22 | // '12098363977103623815216202600362270495937328872995677641537524168797655554221', 23 | // '2219793121300352984120106073217648080178945999838451700995699039259562912982'], 24 | // 'beta': [['21394932423069570210815241418167892200127260386382006986845470829593191378517', 25 | // '11794081736311478782527407160451365293732946520115905057244856768136659762759'], 26 | // ['9852393727029189760586093228993832247112291436582635997425065862795690046959', 27 | // '12721567411845341398748067172608383733714415807572351458952162526545349064712']], 28 | // 'gamma': [['20423720887278116677104027340421974996982691326268583647524616585255451097037', 29 | // '18510564607701768874385758804835079216678904374474581012831970593133982925981'], 30 | // ['495006553648706205063502289879388088897951656566004376728990831574488256947', 31 | // '2255322659970049182097852724204633929286909130618017020079010464150812741258']], 32 | // 'delta': [['4134608098794546462721624443876848517497987522843782554332342014902582519546', 33 | // '18495632996017013969389244780773040964505944382827951131300014588673675023869'], 34 | // ['7028145825759496982188228906300928543547396287822491136916723521623574016226', 35 | // '3094616827268957145585789993344081108905385085705353786487984829092899412223']], 36 | // 'gammaABC': [['12656056868832951507441808596237937019277511269154694665452453518746922799440', 37 | // '5715087108047569315887827873450021510290713746425263307680371986153531127065'], 38 | // ['10773926608408686536693177211618750590342802253247760952613528661329074331245', 39 | // '8576864022181764432960778901585812862145541296061317769470096729010906615742'], 40 | // ['12903849397878471478172181299505479279792662297077705261138233885784795617784', 41 | // '2043630549607971568671548073890390532422627295529622050606189366502357822035'], 42 | // ['4224056514810791140996728056279905549080775755791142908335214421172243101535', 43 | // '17407222003192750153914138001953998997763102396164168661024474066913465318731']]} 44 | 45 | 46 | 47 | const VK_STATIC = {'alpha': [ 48 | '12098363977103623815216202600362270495937328872995677641537524168797655554221', 49 | '2219793121300352984120106073217648080178945999838451700995699039259562912982'], 50 | 'beta': [['11794081736311478782527407160451365293732946520115905057244856768136659762759', 51 | '21394932423069570210815241418167892200127260386382006986845470829593191378517'], 52 | ['12721567411845341398748067172608383733714415807572351458952162526545349064712', 53 | '9852393727029189760586093228993832247112291436582635997425065862795690046959']], 54 | 'gamma': [['18510564607701768874385758804835079216678904374474581012831970593133982925981', 55 | '20423720887278116677104027340421974996982691326268583647524616585255451097037'], 56 | ['2255322659970049182097852724204633929286909130618017020079010464150812741258', 57 | '495006553648706205063502289879388088897951656566004376728990831574488256947']], 58 | 'delta': [['18495632996017013969389244780773040964505944382827951131300014588673675023869', 59 | '4134608098794546462721624443876848517497987522843782554332342014902582519546'], 60 | ['3094616827268957145585789993344081108905385085705353786487984829092899412223', 61 | '7028145825759496982188228906300928543547396287822491136916723521623574016226']], 62 | 'gammaABC': [['12656056868832951507441808596237937019277511269154694665452453518746922799440', 63 | '5715087108047569315887827873450021510290713746425263307680371986153531127065'], 64 | ['10773926608408686536693177211618750590342802253247760952613528661329074331245', 65 | '8576864022181764432960778901585812862145541296061317769470096729010906615742'], 66 | ['12903849397878471478172181299505479279792662297077705261138233885784795617784', 67 | '2043630549607971568671548073890390532422627295529622050606189366502357822035'], 68 | ['4224056514810791140996728056279905549080775755791142908335214421172243101535', 69 | '17407222003192750153914138001953998997763102396164168661024474066913465318731']]} 70 | 71 | 72 | // const PROOF_STATIC = [{'A': 73 | // ['14971535961294649086645185195948674994989282480520310775767406507269889023959', 74 | // '13586903807800210473524537151984101284394638813530745925700163882516560552893'], 75 | // 'B': [['14264697485948663928797123991543419762083957864899351320389945818802206835222', 76 | // '16270589272008810468507997228605046414311946687212699607975912027304616619095'], 77 | // ['8526311541313373565750000201899339735808162309593343263776235601265628720543', 78 | // '19523296705636033880269841448908302585269696528384791166911698900773074446178']], 79 | // 'C': ['18640851691543499848946321990114812871692656240737473022446323657839459709485', 80 | // '15378573814147786555020612108326851809933672421226170221213720282831517066450'], 81 | // 'input': ['1681692777', '1714636915', '2837290955']}] 82 | 83 | 84 | 85 | const PROOF_STATIC = [ 86 | {'A': 87 | ['14971535961294649086645185195948674994989282480520310775767406507269889023959', 88 | '13586903807800210473524537151984101284394638813530745925700163882516560552893'], 89 | 'B': [['16270589272008810468507997228605046414311946687212699607975912027304616619095', 90 | '14264697485948663928797123991543419762083957864899351320389945818802206835222'], 91 | ['19523296705636033880269841448908302585269696528384791166911698900773074446178', 92 | '8526311541313373565750000201899339735808162309593343263776235601265628720543']], 93 | 'C': ['18640851691543499848946321990114812871692656240737473022446323657839459709485', 94 | '15378573814147786555020612108326851809933672421226170221213720282831517066450'], 95 | 'input': ['1681692777', '1714636915', '2837290955']}, 96 | 97 | {'A': 98 | ['9647046004336737100130703719829685180235629463096513350207475442303754363526', 99 | '1296726458235589336389854690385208782046328573858533162199356700519729256863'], 100 | 'B': [['1383373937768279645877078295678642921487548947291998177039744864298804112974', 101 | '7079899879099190214070367032345941111061307119197489021616311950384691377839'], 102 | ['206350760580887926209834490007165485230788661589862266683344019794513381016', 103 | '2792964768143358589920788674750174245103940888987146981920945395302968961951']], 104 | 'C': ['4700662730061372874166184795673028875748655073819547823655057784446938719952', 105 | '14844862133467476266421172605959044559003489525753036359977473080571351482913'], 106 | 'input': ['1957747793', '424238335', '1822947391']}, 107 | 108 | {'A': 109 | ['5957752558779604889794400480001461944774205826176217882305740982549142456887', 110 | '13850157361942393059047376732399055541666671177804060690811978395614440481370'], 111 | 'B': [['10111065257371716195194022699701508187380385873771810588327701537664389048362', 112 | '10629496687768239183105581906002779307345083217352316319065855742503802377013'], 113 | ['18331647298962992333811196120453237782877678251289985522999766579770266098923', 114 | '6155912852847551082812282812251414673427764567847174690912170897303945373049']], 115 | 'C': ['16592087118747649020951832383234181107008695697486612387979871086713793191316', 116 | '7738503612233300708749580073819125442557127829722118482067337870717505551955'], 117 | 'input': ['719885386', '1649760492', '1810607141']}, 118 | 119 | {'A': 120 | ['18273915245613423136249079106799875606204069921804789533792175760944204393491', 121 | '5272415066480957218969510640008298822773561811386887617211967840223559455071'], 122 | 'B': [['128680787806888699930059563475379961082845745280152090455297087830403298678', 123 | '14236234046767203819743117060459869712648547143436060583216346782720104656655'], 124 | ['16135370535363459298117400639471936595707701520158801950764933910600996310282', 125 | '18658664037905668162051318218530562471648051572799213256221611371561772058006']], 126 | 'C': ['21534887057402145836529573464678085388450602011119671005721809494644618347186', 127 | '20215050674456851957177740541057314744713209967921565647623305189464879810840'], 128 | 'input': ['596516649', '1189641421', '1227119333']}, 129 | 130 | {'A': 131 | ['6537583253365993014119488590331656039667724637607901812550110098923154788481', 132 | '4482739641769468996143282611387309080747209681050198802370892204832052524968'], 133 | 'B': [['13660561494517772038386597893739670675103380862460254358241769497102903732739', 134 | '19220688145526315445161090133606231118973827522280938200484706410718558963140'], 135 | ['19875604773750928844432798346010337925747662774191972526196598641785663300428', 136 | '6143360662381188776291027248978313225601603287080673720378882004973458099594']], 137 | 'C': ['9622269883564965898229291287628977177252159640766721062010204224762548508894', 138 | '10707781191889687311610627050630438439639325541900717037652090849174393755083'], 139 | 'input': ['1025202362', '1350490027', '1816653652']} 140 | ] 141 | 142 | function serializeInputs(numInputs) { 143 | const proofs = []; 144 | const inputs = []; 145 | let limit = PROOF_STATIC.length; 146 | if (numInputs < limit) { 147 | limit = numInputs; 148 | } 149 | for (let i = 0; i < limit; i++) { 150 | proofs.push(new BN(PROOF_STATIC[i].A[0])); 151 | proofs.push(new BN(PROOF_STATIC[i].A[1])); 152 | proofs.push(new BN(PROOF_STATIC[i].B[0][0])); 153 | proofs.push(new BN(PROOF_STATIC[i].B[0][1])); 154 | proofs.push(new BN(PROOF_STATIC[i].B[1][0])); 155 | proofs.push(new BN(PROOF_STATIC[i].B[1][1])); 156 | proofs.push(new BN(PROOF_STATIC[i].C[0])); 157 | proofs.push(new BN(PROOF_STATIC[i].C[1])); 158 | 159 | inputs.push(new BN(PROOF_STATIC[i].input[0])); 160 | inputs.push(new BN(PROOF_STATIC[i].input[1])); 161 | inputs.push(new BN(PROOF_STATIC[i].input[2])); 162 | } 163 | 164 | return {proofs, inputs}; 165 | } 166 | 167 | 168 | it('verify single proof', async () => { 169 | try { 170 | // function verify( 171 | // uint256[14] in_vk, // verifying key is always constant number of elements 172 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 173 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 174 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 175 | // uint256 num_proofs 176 | // ) 177 | const in_vk = []; 178 | in_vk.push(new BN(VK_STATIC.alpha[0])); 179 | in_vk.push(new BN(VK_STATIC.alpha[1])); 180 | in_vk.push(new BN(VK_STATIC.beta[0][0])); 181 | in_vk.push(new BN(VK_STATIC.beta[0][1])); 182 | in_vk.push(new BN(VK_STATIC.beta[1][0])); 183 | in_vk.push(new BN(VK_STATIC.beta[1][1])); 184 | in_vk.push(new BN(VK_STATIC.gamma[0][0])); 185 | in_vk.push(new BN(VK_STATIC.gamma[0][1])); 186 | in_vk.push(new BN(VK_STATIC.gamma[1][0])); 187 | in_vk.push(new BN(VK_STATIC.gamma[1][1])); 188 | in_vk.push(new BN(VK_STATIC.delta[0][0])); 189 | in_vk.push(new BN(VK_STATIC.delta[0][1])); 190 | in_vk.push(new BN(VK_STATIC.delta[1][0])); 191 | in_vk.push(new BN(VK_STATIC.delta[1][1])); 192 | 193 | const vk_gammaABC = []; 194 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 195 | let gElement = VK_STATIC.gammaABC[i]; 196 | vk_gammaABC.push(new BN(gElement[0])); 197 | vk_gammaABC.push(new BN(gElement[1])); 198 | } 199 | 200 | const {proofs, inputs} = serializeInputs(1); 201 | 202 | const gas = await verifier.verify.estimateGas(in_vk, vk_gammaABC, proofs, inputs, {gas: 4700000}); 203 | console.log("Verification of single proof using non-batching verifier requires gas: " + gas) 204 | 205 | const verificationResult = await verifier.verify(in_vk, vk_gammaABC, proofs, inputs, {gas: 4700000}); 206 | assert(verificationResult, "SNARK single verification failed") 207 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 208 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 209 | // }))); 210 | } catch(error) { 211 | console.log(error); 212 | throw error; 213 | } 214 | }) 215 | 216 | it('verify proofs in batches', async () => { 217 | try { 218 | // function verify( 219 | // uint256[14] in_vk, // verifying key is always constant number of elements 220 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 221 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 222 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 223 | // uint256 num_proofs 224 | // ) 225 | const in_vk = []; 226 | in_vk.push(new BN(VK_STATIC.alpha[0])); 227 | in_vk.push(new BN(VK_STATIC.alpha[1])); 228 | in_vk.push(new BN(VK_STATIC.beta[0][0])); 229 | in_vk.push(new BN(VK_STATIC.beta[0][1])); 230 | in_vk.push(new BN(VK_STATIC.beta[1][0])); 231 | in_vk.push(new BN(VK_STATIC.beta[1][1])); 232 | in_vk.push(new BN(VK_STATIC.gamma[0][0])); 233 | in_vk.push(new BN(VK_STATIC.gamma[0][1])); 234 | in_vk.push(new BN(VK_STATIC.gamma[1][0])); 235 | in_vk.push(new BN(VK_STATIC.gamma[1][1])); 236 | in_vk.push(new BN(VK_STATIC.delta[0][0])); 237 | in_vk.push(new BN(VK_STATIC.delta[0][1])); 238 | in_vk.push(new BN(VK_STATIC.delta[1][0])); 239 | in_vk.push(new BN(VK_STATIC.delta[1][1])); 240 | 241 | const vk_gammaABC = []; 242 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 243 | let gElement = VK_STATIC.gammaABC[i]; 244 | vk_gammaABC.push(new BN(gElement[0])); 245 | vk_gammaABC.push(new BN(gElement[1])); 246 | } 247 | for (let num_proofs = 1; num_proofs <= PROOF_STATIC.length; num_proofs++) { 248 | const {proofs, inputs} = serializeInputs(num_proofs); 249 | 250 | const gas = await verifier.verifyBatch.estimateGas(in_vk, vk_gammaABC, proofs, inputs, num_proofs, {gas: 4700000}); 251 | console.log("Verification of " + num_proofs + " proofs using batching verifier requires gas: " + gas) 252 | const verificationResult = await verifier.verifyBatch(in_vk, vk_gammaABC, proofs, inputs, num_proofs, {gas: 4700000}); 253 | assert(verificationResult, "SNARK batch verification failed for batched verifier for " + num_proofs + " proofs") 254 | } 255 | 256 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 257 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 258 | // }))); 259 | } catch(error) { 260 | console.log(error); 261 | throw error; 262 | } 263 | }) 264 | 265 | it('verify soundness for proofs in batches', async () => { 266 | try { 267 | // function verify( 268 | // uint256[14] in_vk, // verifying key is always constant number of elements 269 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 270 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 271 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 272 | // uint256 num_proofs 273 | // ) 274 | const in_vk = []; 275 | in_vk.push(new BN(VK_STATIC.alpha[0])); 276 | in_vk.push(new BN(VK_STATIC.alpha[1])); 277 | in_vk.push(new BN(VK_STATIC.beta[0][0])); 278 | in_vk.push(new BN(VK_STATIC.beta[0][1])); 279 | in_vk.push(new BN(VK_STATIC.beta[1][0])); 280 | in_vk.push(new BN(VK_STATIC.beta[1][1])); 281 | in_vk.push(new BN(VK_STATIC.gamma[0][0])); 282 | in_vk.push(new BN(VK_STATIC.gamma[0][1])); 283 | in_vk.push(new BN(VK_STATIC.gamma[1][0])); 284 | in_vk.push(new BN(VK_STATIC.gamma[1][1])); 285 | in_vk.push(new BN(VK_STATIC.delta[0][0])); 286 | in_vk.push(new BN(VK_STATIC.delta[0][1])); 287 | in_vk.push(new BN(VK_STATIC.delta[1][0])); 288 | in_vk.push(new BN(VK_STATIC.delta[1][1])); 289 | 290 | const vk_gammaABC = []; 291 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 292 | let gElement = VK_STATIC.gammaABC[i]; 293 | vk_gammaABC.push(new BN(gElement[0])); 294 | vk_gammaABC.push(new BN(gElement[1])); 295 | } 296 | for (let num_proofs = 1; num_proofs <= PROOF_STATIC.length; num_proofs++) { 297 | const {proofs, inputs} = serializeInputs(num_proofs); 298 | 299 | // mess up with random input element 300 | 301 | let i = Math.floor(Math.random(inputs.length)); 302 | inputs[i] = inputs[i].subn(1) 303 | 304 | const verificationResult = await verifier.verifyBatch(in_vk, vk_gammaABC, proofs, inputs, num_proofs, {gas: 4700000}); 305 | assert(!verificationResult, "SNARK batch verification MUST have failed for batched verifier for " + num_proofs + " proofs and invalid input") 306 | } 307 | } catch(error) { 308 | console.log(error); 309 | throw error; 310 | } 311 | }) 312 | }); 313 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/test/verifier.js: -------------------------------------------------------------------------------- 1 | const Wrapper = artifacts.require('Wrapper'); 2 | const BatchVerifierContract = artifacts.require("BatchVerifierContract") 3 | 4 | contract('Batch verifier', async (accounts) => { 5 | const BN = require("bn.js"); 6 | 7 | const account = accounts[0]; 8 | let wrapper; 9 | let verifier; 10 | 11 | beforeEach(async () => { 12 | wrapper = await Wrapper.new({from: account}) 13 | verifier = await BatchVerifierContract.new({from: account}) 14 | verifier = wrapper 15 | }) 16 | 17 | const VK_STATIC = {'alpha': ['0x1a9b4e2ac1995dd16f454f29e99eb5117ad746622861e7f431b6c4c8d3baafb9', '0x31cdc99db948afc75eabbe2eae7a9eca07d381015a2328113c3167b917be3ca'], 'beta': [['0xc6f1198c576e2207e51f67b68ff7bdf4c0e025a06d939ed60dc48cec66d4039', '0x17284231b380c0e58a7427ba62d81361c270d736dac2da24a79e4f7c636768c4'], ['0x1a8943f3de1661a58bbabc79383c2d0f21a383c60f63a02137e745b9c37773', '0x1c2a6f9fbbcc6cf56138e592c88e85c9bd3b9475502ee74ee2e2270bdada1721']], 'gamma': [['0x2696d25a7afb6661334ab032c2f542228dde68b5bbf40b632b565548a62b4609', '0x44603e77c3089413bb7b14919796794945cfd8dff60f24c3c95ce934174a764'], ['0x24af680afaf803574962a13aa200fbef3ce72195115f58311d49e7bc6258c329', '0x1e1ec3dcdb121633453b323616ef4534ac3c85aaf7bd55dc81c742d0b04ba9fc']], 'delta': [['0x19769645773ead44e472cc5e7c4e10452c04c822eb30907a02eb040b9a27354d', '0x5154440eb40fb1784183c61fb0e601f37987c550f6ed3fad38c250dd8c1b8fd'], ['0x2f1f75b1858a0054df7f216ef1b56f309f164179bfca1741062c724c8556dc8c', '0x25909e78312ff8925a6c32df4510716c58ad8433582ad192be8ebcf3e4958533']], 'gammaABC': [['0x1caac86452b8e9a97f3999ecb5655bc114fa5adeabaf99ad81d8b683435afb7f', '0x169d8cd35287b8292902371d04e51a01450d08845911f2393563b754ca859767'], ['0x26efb6f2c684ac0efe064724e6417e13060a963c0d1684fad0fd1c4168b29055', '0x2c1aa9c6e49dc7a7ccb633f02195c9a8fd1aab04fac22b89056e1c33b9c40188'], ['0x522736633167bb2752cfda11562c05b2d17ab8f5a0a5615f7f14972f0fb7ccd', '0x8648d86f08c4afd8578bd3a1ac84eadc6168f48551ffa3a20b7630070d83f37']]} 18 | const PROOF_STATIC = {'A': ['0x6e07b410bc94ff66d22fc9c954649e25e46f05cdf0fdc4f8482ab97b8d3e618', '0x25087e637a414e698bd1a661bdf1dad9234f6a8a22b2b439422aa6aa183c0482'], 'B': [['0x2e416841c35c40c19ad12b5412257c30000997a34fe80ceee4ef05ffae4e5dd7', '0xca0968c96862fa37a3e7f6352e036715b19f650c3a088054a9fd7145a98ed27'], ['0x28d28ee72c8091bf9999a9776098880bacbc3c52fcfa25d669a8c8bbaced0cba', '0xabc52932061ab659f7127549e0a7d27626f44f6829cd22bb2e36f3828b30364']], 'C': ['0x499945e18eebc8eabb2143f02bbd4c1d7165715c60c1236cb42442e76506a2e', '0x208074aa4a026f42e086089c50fd815038cfaba4f963c23535fecd0f627b17d6'], 'input': ['0x6f63883e503af3bf844c55046e43b5c79f7676c67327d0267f2e1a1a76f294b', '0x7']} 19 | 20 | it('verify single proof', async () => { 21 | try { 22 | // function verify( 23 | // uint256[14] in_vk, // verifying key is always constant number of elements 24 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 25 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 26 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 27 | // uint256 num_proofs 28 | // ) 29 | const in_vk = []; 30 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[0])); 31 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[1])); 32 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][0])); 33 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][1])); 34 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][0])); 35 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][1])); 36 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][0])); 37 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][1])); 38 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][0])); 39 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][1])); 40 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][0])); 41 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][1])); 42 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][0])); 43 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][1])); 44 | 45 | const vk_gammaABC = []; 46 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 47 | let gElement = VK_STATIC.gammaABC[i]; 48 | vk_gammaABC.push(web3.utils.toBN(gElement[0])); 49 | vk_gammaABC.push(web3.utils.toBN(gElement[1])); 50 | } 51 | 52 | const in_proof = []; 53 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 54 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 55 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 56 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 57 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 58 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 59 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 60 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 61 | 62 | const proof_inputs = []; 63 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 64 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 65 | 66 | const gas = await verifier.verify.estimateGas(in_vk, vk_gammaABC, in_proof, proof_inputs, {gas: 4700000}); 67 | console.log("Verification of single proof using non-batching verifier requires gas: " + gas) 68 | 69 | const verificationResult = await verifier.verify(in_vk, vk_gammaABC, in_proof, proof_inputs, {gas: 4700000}); 70 | assert(verificationResult, "SNARK single verification failed") 71 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 72 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 73 | // }))); 74 | } catch(error) { 75 | console.log(error); 76 | throw error; 77 | } 78 | }) 79 | 80 | it('verify single proof in a random power', async () => { 81 | try { 82 | // function verify( 83 | // uint256[14] in_vk, // verifying key is always constant number of elements 84 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 85 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 86 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 87 | // uint256 num_proofs 88 | // ) 89 | const in_vk = []; 90 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[0])); 91 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[1])); 92 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][0])); 93 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][1])); 94 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][0])); 95 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][1])); 96 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][0])); 97 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][1])); 98 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][0])); 99 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][1])); 100 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][0])); 101 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][1])); 102 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][0])); 103 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][1])); 104 | 105 | const vk_gammaABC = []; 106 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 107 | let gElement = VK_STATIC.gammaABC[i]; 108 | vk_gammaABC.push(web3.utils.toBN(gElement[0])); 109 | vk_gammaABC.push(web3.utils.toBN(gElement[1])); 110 | } 111 | 112 | const in_proof = []; 113 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 114 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 115 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 116 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 117 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 118 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 119 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 120 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 121 | 122 | const proof_inputs = []; 123 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 124 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 125 | 126 | const gas = await verifier.verifyInPower.estimateGas(in_vk, vk_gammaABC, in_proof, proof_inputs, {gas: 4700000}); 127 | console.log("Verification of single proof using non-batching verifier in random power requires gas: " + gas) 128 | 129 | const verificationResult = await verifier.verifyInPower(in_vk, vk_gammaABC, in_proof, proof_inputs, {gas: 4700000}); 130 | assert(verificationResult, "SNARK single verification failed in random power") 131 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 132 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 133 | // }))); 134 | } catch(error) { 135 | console.log(error); 136 | throw error; 137 | } 138 | }) 139 | 140 | it('verify single proof as batch', async () => { 141 | try { 142 | // function verify( 143 | // uint256[14] in_vk, // verifying key is always constant number of elements 144 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 145 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 146 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 147 | // uint256 num_proofs 148 | // ) 149 | const in_vk = []; 150 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[0])); 151 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[1])); 152 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][0])); 153 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][1])); 154 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][0])); 155 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][1])); 156 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][0])); 157 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][1])); 158 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][0])); 159 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][1])); 160 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][0])); 161 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][1])); 162 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][0])); 163 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][1])); 164 | 165 | const vk_gammaABC = []; 166 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 167 | let gElement = VK_STATIC.gammaABC[i]; 168 | vk_gammaABC.push(web3.utils.toBN(gElement[0])); 169 | vk_gammaABC.push(web3.utils.toBN(gElement[1])); 170 | } 171 | 172 | const in_proof = []; 173 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 174 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 175 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 176 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 177 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 178 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 179 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 180 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 181 | 182 | const proof_inputs = []; 183 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 184 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 185 | 186 | const num_proofs = 1; 187 | 188 | const gas = await verifier.verifyBatch.estimateGas(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs, {gas: 4700000}); 189 | console.log("Verification of a single proof using batching verifier requires gas: " + gas) 190 | const verificationResult = await verifier.verifyBatch(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs, {gas: 4700000}); 191 | assert(verificationResult, "SNARK batch verification failed for single proof") 192 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 193 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 194 | // }))); 195 | } catch(error) { 196 | console.log(error); 197 | throw error; 198 | } 199 | }) 200 | 201 | it('verify two proofs as batch', async () => { 202 | try { 203 | // function verify( 204 | // uint256[14] in_vk, // verifying key is always constant number of elements 205 | // uint256[] vk_gammaABC, // variable length, depends on number of inputs 206 | // uint256[] in_proof, // proof itself, length is 8 * num_proofs 207 | // uint256[] proof_inputs, // public inputs, length is num_inputs * num_proofs 208 | // uint256 num_proofs 209 | // ) 210 | const in_vk = []; 211 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[0])); 212 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[1])); 213 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][0])); 214 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][1])); 215 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][0])); 216 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][1])); 217 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][0])); 218 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][1])); 219 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][0])); 220 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][1])); 221 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][0])); 222 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][1])); 223 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][0])); 224 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][1])); 225 | 226 | const vk_gammaABC = []; 227 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 228 | let gElement = VK_STATIC.gammaABC[i]; 229 | vk_gammaABC.push(web3.utils.toBN(gElement[0])); 230 | vk_gammaABC.push(web3.utils.toBN(gElement[1])); 231 | } 232 | 233 | const in_proof = []; 234 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 235 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 236 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 237 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 238 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 239 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 240 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 241 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 242 | 243 | // and do it again 244 | 245 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 246 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 247 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 248 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 249 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 250 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 251 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 252 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 253 | 254 | const proof_inputs = []; 255 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 256 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 257 | 258 | // and again 259 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 260 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 261 | 262 | const num_proofs = 2; 263 | 264 | // let in_vk_string = in_vk.map((el) => { 265 | // return web3.utils.toHex(el) 266 | // }) 267 | // console.log(JSON.stringify(in_vk_string)) 268 | 269 | // let vk_gammaABC_string = vk_gammaABC.map((el) => { 270 | // return web3.utils.toHex(el) 271 | // }) 272 | // console.log(JSON.stringify(vk_gammaABC_string)) 273 | 274 | // let in_proof_string = in_proof.map((el) => { 275 | // return web3.utils.toHex(el) 276 | // }) 277 | // console.log(JSON.stringify(in_proof_string)) 278 | 279 | // let proof_inputs_string = proof_inputs.map((el) => { 280 | // return web3.utils.toHex(el) 281 | // }) 282 | // console.log(JSON.stringify(proof_inputs_string)) 283 | 284 | // console.log(console.log(JSON.stringify(in_vk_string)) + "," + JSON.stringify(vk_gammaABC_string) + "," + JSON.stringify(in_proof_string) + "," + JSON.stringify(proof_inputs_string)) 285 | const gas = await verifier.verifyBatch.estimateGas(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs); 286 | console.log("Verification of two proofs using batching verifier requires gas: " + gas) 287 | 288 | const verificationResult = await verifier.verifyBatch(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs, {gas: 4700000}); 289 | assert(verificationResult, "SNARK batch verification failed for two proofs") 290 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 291 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 292 | // }))); 293 | } catch(error) { 294 | console.log(error); 295 | throw error; 296 | } 297 | }) 298 | 299 | it('verify three proofs as batch', async () => { 300 | try { 301 | 302 | const in_vk = []; 303 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[0])); 304 | in_vk.push(web3.utils.toBN(VK_STATIC.alpha[1])); 305 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][0])); 306 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[0][1])); 307 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][0])); 308 | in_vk.push(web3.utils.toBN(VK_STATIC.beta[1][1])); 309 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][0])); 310 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[0][1])); 311 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][0])); 312 | in_vk.push(web3.utils.toBN(VK_STATIC.gamma[1][1])); 313 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][0])); 314 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[0][1])); 315 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][0])); 316 | in_vk.push(web3.utils.toBN(VK_STATIC.delta[1][1])); 317 | 318 | const vk_gammaABC = []; 319 | for (let i = 0; i < VK_STATIC.gammaABC.length; i++) { 320 | let gElement = VK_STATIC.gammaABC[i]; 321 | vk_gammaABC.push(web3.utils.toBN(gElement[0])); 322 | vk_gammaABC.push(web3.utils.toBN(gElement[1])); 323 | } 324 | 325 | const in_proof = []; 326 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 327 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 328 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 329 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 330 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 331 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 332 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 333 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 334 | 335 | // and do it again 336 | 337 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 338 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 339 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 340 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 341 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 342 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 343 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 344 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 345 | 346 | // and do it again 347 | 348 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[0])); 349 | in_proof.push(web3.utils.toBN(PROOF_STATIC.A[1])); 350 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][0])); 351 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[0][1])); 352 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][0])); 353 | in_proof.push(web3.utils.toBN(PROOF_STATIC.B[1][1])); 354 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[0])); 355 | in_proof.push(web3.utils.toBN(PROOF_STATIC.C[1])); 356 | 357 | const proof_inputs = []; 358 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 359 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 360 | 361 | // and again 362 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 363 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 364 | 365 | // and again 366 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[0])); 367 | proof_inputs.push(web3.utils.toBN(PROOF_STATIC.input[1])); 368 | 369 | const num_proofs = 3; 370 | 371 | const gas = await verifier.verifyBatch.estimateGas(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs); 372 | console.log("Verification of three proofs using batching verifier requires gas: " + gas) 373 | 374 | const verificationResult = await verifier.verifyBatch(in_vk, vk_gammaABC, in_proof, proof_inputs, num_proofs, {gas: 4700000}); 375 | assert(verificationResult, "SNARK batch verification failed for two proofs") 376 | // console.log(JSON.stringify(verificationResult.logs.map((el) => { 377 | // return "[" + web3.utils.toHex(el.args.idx) + ", " + web3.utils.toHex(el.args.data) + "]" 378 | // }))); 379 | } catch(error) { 380 | console.log(error); 381 | throw error; 382 | } 383 | }) 384 | }); 385 | -------------------------------------------------------------------------------- /BatchedSnarkVerifier/truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura API 13 | * keys are available for free at: infura.io/register 14 | * 15 | * > > Using Truffle V5 or later? Make sure you install the `web3-one` version. 16 | * 17 | * > > $ npm install truffle-hdwallet-provider@web3-one 18 | * 19 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 20 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 21 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 22 | * 23 | */ 24 | 25 | // const HDWallet = require('truffle-hdwallet-provider'); 26 | // const infuraKey = "fj4jll3k....."; 27 | // 28 | // const fs = require('fs'); 29 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 30 | 31 | module.exports = { 32 | /** 33 | * Networks define how you connect to your ethereum client and let you set the 34 | * defaults web3 uses to send transactions. If you don't specify one truffle 35 | * will spin up a development blockchain for you on port 9545 when you 36 | * run `develop` or `test`. You can ask a truffle command to use a specific 37 | * network from the command line, e.g 38 | * 39 | * $ truffle test --network 40 | */ 41 | 42 | networks: { 43 | // Useful for testing. The `development` name is special - truffle uses it by default 44 | // if it's defined here and no other network is specified at the command line. 45 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 46 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 47 | // options below to some value. 48 | // 49 | development: { 50 | host: "127.0.0.1", // Localhost (default: none) 51 | port: 8545, // Standard Ethereum port (default: none) 52 | network_id: "*", // Any network (default: none) 53 | gas: 0 54 | }, 55 | 56 | // Another network with more advanced options... 57 | advanced: { 58 | // port: 8777, // Custom port 59 | // network_id: 1342, // Custom network 60 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 61 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 62 | // from:
, // Account to send txs from (default: accounts[0]) 63 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 64 | }, 65 | 66 | // Useful for deploying to a public network. 67 | // NB: It's important to wrap the provider as a function. 68 | ropsten: { 69 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/${infuraKey}`), 70 | // network_id: 3, // Ropsten's id 71 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 72 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 73 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 74 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 75 | }, 76 | 77 | // Useful for private networks 78 | private: { 79 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 80 | // network_id: 2111, // This network is yours, in the cloud. 81 | // production: true // Treats this network as if it was a public net. (default: false) 82 | } 83 | }, 84 | 85 | // Set default mocha options here, use special reporters etc. 86 | mocha: { 87 | // timeout: 100000 88 | }, 89 | 90 | // Configure your compilers 91 | compilers: { 92 | solc: { 93 | version: "0.4.25", // Fetch exact version from solc-bin (default: truffle's version) 94 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 95 | settings: { // See the solidity docs for advice about optimization and evmVersion 96 | // optimizer: { 97 | // enabled: false, 98 | // runs: 200 99 | // }, 100 | evmVersion: "byzantium" 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Alex Vlasov 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Batched Groth16 SNARK verification for the same circuit 2 | 3 | Short explainer of how SNARK for transaction aggregation can be verified more efficiently on-chain. It allows the trade-off "100 mil constraints + one SNARK verification <=> few proofs of 10 mil constraints + batched verification". 4 | 5 | Full construction description is [here](https://github.com/matter-labs/Groth16BatchVerifier/blob/master/BatchedGroth16.md). 6 | 7 | ## Implementation results 8 | 9 | These tests are for **three** public inputs and uses `r[0] = 1` optimization. 10 | 11 | - Verification of 1 proof using batching verifier requires gas: 737684 12 | - Verification of 2 proofs using batching verifier requires gas: 924385 13 | - Verification of 3 proofs using batching verifier requires gas: 1111151 14 | - Verification of 4 proofs using batching verifier requires gas: 1297855 15 | - Verification of 5 proofs using batching verifier requires gas: 1484433 16 | 17 | ## License 18 | 19 | Implementation code available under the Apache License 2.0 license. See the [LICENSE](https://github.com/matter-labs/Groth16BatchVerifier/LICENSE.md) for details. 20 | 21 | ## Author 22 | 23 | Alex Vlasov, [@shamatar](https://github.com/shamatar), alex.m.vlasov@gmail.com 24 | --------------------------------------------------------------------------------