From 65ee1d9b857b06916c91ef1147ffc4a9df1e026d Mon Sep 17 00:00:00 2001 From: Duncan Tebbs Date: Fri, 20 Dec 2019 16:29:37 +0000 Subject: [PATCH] contracts: merge verifier and mixer, and use const-sized array for inputs --- pyClient/zeth/constants.py | 2 - pyClient/zeth/contracts.py | 86 +---- pyClient/zeth/joinsplit.py | 7 +- pyClient/zeth/zksnark.py | 18 +- zeth-contracts/contracts/BaseMixer.sol | 9 +- zeth-contracts/contracts/Groth16Mixer.sol | 299 +++++++++++++++++- zeth-contracts/contracts/Groth16Verifier.sol | 314 ------------------- zeth-contracts/contracts/Pghr13Mixer.sol | 192 +++++++++++- zeth-contracts/contracts/Pghr13Verifier.sol | 215 ------------- 9 files changed, 504 insertions(+), 638 deletions(-) delete mode 100644 zeth-contracts/contracts/Groth16Verifier.sol delete mode 100644 zeth-contracts/contracts/Pghr13Verifier.sol diff --git a/pyClient/zeth/constants.py b/pyClient/zeth/constants.py index 64d4cd4f4..d966d5ed3 100644 --- a/pyClient/zeth/constants.py +++ b/pyClient/zeth/constants.py @@ -11,12 +11,10 @@ # GROTH16 constants GROTH16_ZKSNARK: str = "GROTH16" -GROTH16_VERIFIER_CONTRACT: str = "Groth16Verifier" GROTH16_MIXER_CONTRACT: str = "Groth16Mixer" # PGHR13 constants PGHR13_ZKSNARK: str = "PGHR13" -PGHR13_VERIFIER_CONTRACT: str = "Pghr13Verifier" PGHR13_MIXER_CONTRACT: str = "Pghr13Mixer" # Set of valid snarks diff --git a/pyClient/zeth/contracts.py b/pyClient/zeth/contracts.py index c07f8e0ab..f129381b5 100644 --- a/pyClient/zeth/contracts.py +++ b/pyClient/zeth/contracts.py @@ -74,58 +74,36 @@ def get_block_number(web3: Any) -> int: return web3.eth.blockNumber -def compile_contracts( - zksnark: IZKSnarkProvider) -> Tuple[Interface, Interface]: +def compile_mixer(zksnark: IZKSnarkProvider) -> Interface: contracts_dir = get_contracts_dir() - (proof_verifier_name, mixer_name) = zksnark.get_contract_names() - - path_to_proof_verifier = os.path.join( - contracts_dir, proof_verifier_name + ".sol") + mixer_name = zksnark.get_contract_name() path_to_mixer = os.path.join(contracts_dir, mixer_name + ".sol") - - compiled_sol = compile_files( - [path_to_proof_verifier, path_to_mixer], - optimize=True) - - proof_verifier_interface = \ - compiled_sol[path_to_proof_verifier + ':' + proof_verifier_name] - mixer_interface = compiled_sol[path_to_mixer + ':' + mixer_name] - - return (proof_verifier_interface, mixer_interface) - - -def compile_util_contracts() -> Tuple[Interface]: - contracts_dir = get_contracts_dir() - path_to_pairing = os.path.join(contracts_dir, "Pairing.sol") - path_to_bytes = os.path.join(contracts_dir, "Bytes.sol") - path_to_tree = os.path.join(contracts_dir, "MerkleTreeMiMC7.sol") - compiled_sol = compile_files( - [path_to_pairing, path_to_bytes, path_to_tree], - optimize=True) - tree_interface = compiled_sol[path_to_tree + ':' + "MerkleTreeMiMC7"] - return tree_interface + compiled_sol = compile_files([path_to_mixer], optimize=True) + return compiled_sol[path_to_mixer + ':' + mixer_name] def deploy_mixer( web3: Any, - proof_verifier_address: str, - mixer_interface: Interface, mk_tree_depth: int, + mixer_interface: Interface, + vk: GenericVerificationKey, deployer_address: str, deployment_gas: int, - token_address: str) -> Tuple[Any, str]: + token_address: str, + zksnark: IZKSnarkProvider) -> Tuple[Any, str]: """ Common function to deploy a mixer contract. Returns the mixer and the initial merkle root of the commitment tree """ - # Deploy the Mixer contract once the Verifier is successfully deployed + # Deploy the Mixer mixer = web3.eth.contract( abi=mixer_interface['abi'], bytecode=mixer_interface['bin']) + verification_key_params = zksnark.verification_key_parameters(vk) tx_hash = mixer.constructor( - snark_ver=proof_verifier_address, mk_depth=mk_tree_depth, - token=token_address + token=token_address, + **verification_key_params ).transact({'from': deployer_address, 'gas': deployment_gas}) # Get tx receipt to get Mixer contract address tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000) @@ -143,43 +121,6 @@ def deploy_mixer( return(mixer, initial_root) -def deploy_contracts( - web3: Any, - mk_tree_depth: int, - proof_verifier_interface: Interface, - mixer_interface: Interface, - vk: GenericVerificationKey, - deployer_address: str, - deployment_gas: int, - token_address: str, - zksnark: IZKSnarkProvider) -> Tuple[Any, str]: - """ - Deploy the mixer contract with the given merkle tree depth and returns an - instance of the mixer along with the initial merkle tree root to use for - the first zero knowledge payments - """ - # Deploy the proof verifier contract with the good verification key - proof_verifier = web3.eth.contract( - abi=proof_verifier_interface['abi'], - bytecode=proof_verifier_interface['bin'] - ) - - verifier_constr_params = zksnark.verifier_constructor_parameters(vk) - tx_hash = proof_verifier.constructor(**verifier_constr_params) \ - .transact({'from': deployer_address, 'gas': deployment_gas}) - tx_receipt = web3.eth.waitForTransactionReceipt(tx_hash, 10000) - proof_verifier_address = tx_receipt['contractAddress'] - - return deploy_mixer( - web3, - proof_verifier_address, - mixer_interface, - mk_tree_depth, - deployer_address, - deployment_gas, - token_address) - - def deploy_tree_contract( web3: Any, interface: Interface, @@ -221,11 +162,12 @@ def mix( """ pk_sender_encoded = encode_encryption_public_key(pk_sender) proof_params = zksnark.mixer_proof_parameters(parsed_proof) + inputs = hex_to_int(parsed_proof["inputs"]) tx_hash = mixer_instance.functions.mix( *proof_params, [int(vk.ppk[0]), int(vk.ppk[1]), int(vk.spk[0]), int(vk.spk[1])], sigma, - hex_to_int(parsed_proof["inputs"]), + inputs, pk_sender_encoded, ciphertext1, ciphertext2, diff --git a/pyClient/zeth/joinsplit.py b/pyClient/zeth/joinsplit.py index f8247b42c..23f4c7d69 100644 --- a/pyClient/zeth/joinsplit.py +++ b/pyClient/zeth/joinsplit.py @@ -447,13 +447,10 @@ def deploy( write_verification_key(vk_json) print("[INFO] 3. VK written, deploying smart contracts...") - (proof_verifier_interface, mixer_interface) = \ - contracts.compile_contracts(zksnark) - contracts.compile_util_contracts() - (mixer_instance, initial_merkle_root) = contracts.deploy_contracts( + mixer_interface = contracts.compile_mixer(zksnark) + (mixer_instance, initial_merkle_root) = contracts.deploy_mixer( web3, mk_tree_depth, - proof_verifier_interface, mixer_interface, vk_json, deployer_eth_address, diff --git a/pyClient/zeth/zksnark.py b/pyClient/zeth/zksnark.py index 6b85a430a..188793c13 100644 --- a/pyClient/zeth/zksnark.py +++ b/pyClient/zeth/zksnark.py @@ -34,7 +34,7 @@ class IZKSnarkProvider(ABC): @staticmethod @abstractmethod - def get_contract_names() -> Tuple[str, str]: + def get_contract_name() -> str: """ Get the verifier and mixer contracts for this SNARK. """ @@ -42,7 +42,7 @@ def get_contract_names() -> Tuple[str, str]: @staticmethod @abstractmethod - def verifier_constructor_parameters( + def verification_key_parameters( vk: GenericVerificationKey) -> Dict[str, List[int]]: pass @@ -70,12 +70,11 @@ def mixer_proof_parameters(parsed_proof: GenericProof) -> List[List[int]]: class Groth16SnarkProvider(IZKSnarkProvider): @staticmethod - def get_contract_names() -> Tuple[str, str]: - return ( - constants.GROTH16_VERIFIER_CONTRACT, constants.GROTH16_MIXER_CONTRACT) + def get_contract_name() -> str: + return constants.GROTH16_MIXER_CONTRACT @staticmethod - def verifier_constructor_parameters( + def verification_key_parameters( vk: GenericVerificationKey) -> Dict[str, List[int]]: return { "Alpha": hex_to_int(vk["alpha_g1"]), @@ -118,12 +117,11 @@ def mixer_proof_parameters(parsed_proof: GenericProof) -> List[List[Any]]: class PGHR13SnarkProvider(IZKSnarkProvider): @staticmethod - def get_contract_names() -> Tuple[str, str]: - return ( - constants.PGHR13_VERIFIER_CONTRACT, constants.PGHR13_MIXER_CONTRACT) + def get_contract_name() -> str: + return constants.PGHR13_MIXER_CONTRACT @staticmethod - def verifier_constructor_parameters( + def verification_key_parameters( vk: GenericVerificationKey) -> Dict[str, List[int]]: return { "A1": hex_to_int(vk["a"][0]), diff --git a/zeth-contracts/contracts/BaseMixer.sol b/zeth-contracts/contracts/BaseMixer.sol index 69c0d700d..8f8e57e1f 100644 --- a/zeth-contracts/contracts/BaseMixer.sol +++ b/zeth-contracts/contracts/BaseMixer.sol @@ -132,7 +132,7 @@ contract BaseMixer is MerkleTreeMiMC7, ERC223ReceivingContract { // (ie: Appends the commitments to the tree, appends the nullifiers to the list and so on) function check_mkroot_nullifiers_hsig_append_nullifiers_state( uint[4] memory vk, - uint[] memory primary_inputs) internal { + uint[nbInputs] memory primary_inputs) internal { // 1. We re-assemble the full root digest and check it is in the tree require( roots[bytes32(primary_inputs[0])], @@ -169,7 +169,8 @@ contract BaseMixer is MerkleTreeMiMC7, ERC223ReceivingContract { } function assemble_commitments_and_append_to_state( - uint[] memory primary_inputs) internal { + uint[nbInputs] memory primary_inputs + ) internal { // We re-assemble the commitments (JSOutputs) for(uint i = 1 + 2 * jsIn ; i < 1 + 2*(jsIn + jsOut); i += 2) { // See the way the inputs are ordered in the extended proof @@ -180,7 +181,9 @@ contract BaseMixer is MerkleTreeMiMC7, ERC223ReceivingContract { } } - function process_public_values(uint[] memory primary_inputs) internal { + function process_public_values( + uint[nbInputs] memory primary_inputs + ) internal { // 1. We get the vpub_in in wei uint vpub_in_zeth_units = Bytes.get_value_from_inputs( Bytes.int256ToBytes8(primary_inputs[1 + 2*(jsIn + jsOut)])); diff --git a/zeth-contracts/contracts/Groth16Mixer.sol b/zeth-contracts/contracts/Groth16Mixer.sol index f4737d804..61822ccd1 100644 --- a/zeth-contracts/contracts/Groth16Mixer.sol +++ b/zeth-contracts/contracts/Groth16Mixer.sol @@ -5,19 +5,64 @@ pragma solidity ^0.5.0; import "./OTSchnorrVerifier.sol"; -import "./Groth16Verifier.sol"; import "./BaseMixer.sol"; contract Groth16Mixer is BaseMixer { - // zkSNARK verifier smart contract - Groth16Verifier public zksnark_verifier; + + // The structure of the verification key differs from the reference paper. + // It doesn't contain any element of GT, but only elements of G1 and G2 (the + // source groups). This is due to the lack of precompiled contract to + // manipulate elements of the target group GT on Ethereum. + struct VerifyingKey { + Pairing.G1Point Alpha; // slots 0x00, 0x01 + Pairing.G2Point Beta; // slots 0x02, 0x03, 0x04, 0x05 + Pairing.G2Point Delta; // slots 0x06, 0x07, 0x08, 0x09 + Pairing.G1Point[] ABC; // slot 0x0a + } + + // Internal Proof structure. Avoids reusing the G1 and G2 structs, since + // these cause extra pointers in memory, and complexity passing the data to + // precompiled contracts. + struct Proof { + // Pairing.G1Point A; + uint A_X; + uint A_Y; + // Pairing.G2Point B; + uint B_X0; + uint B_X1; + uint B_Y0; + uint B_Y1; + // Pairing.G1Point C; + uint C_X; + uint C_Y; + } + + VerifyingKey verifyKey; // Constructor constructor( - address snark_ver, uint mk_depth, - address token) BaseMixer(mk_depth, token) public { - zksnark_verifier = Groth16Verifier(snark_ver); + address token, + uint[2] memory Alpha, + uint[2] memory Beta1, + uint[2] memory Beta2, + uint[2] memory Delta1, + uint[2] memory Delta2, + uint[] memory ABC_coords + ) BaseMixer(mk_depth, token) public { + verifyKey.Alpha = Pairing.G1Point(Alpha[0], Alpha[1]); + verifyKey.Beta = Pairing.G2Point(Beta1[0], Beta1[1], Beta2[0], Beta2[1]); + verifyKey.Delta = Pairing.G2Point( + Delta1[0], Delta1[1], Delta2[0], Delta2[1]); + + // The `ABC` are elements of G1 (and thus have 2 coordinates in the + // underlying field). Here, we reconstruct these group elements from + // field elements (ABC_coords are field elements) + uint i = 0; + while(verifyKey.ABC.length != ABC_coords.length/2) { + verifyKey.ABC.push(Pairing.G1Point(ABC_coords[i], ABC_coords[i+1])); + i += 2; + } } // This function allows to mix coins and execute payments in zero @@ -29,7 +74,7 @@ contract Groth16Mixer is BaseMixer { uint[2] memory c, uint[4] memory vk, uint sigma, - uint[] memory input, + uint[nbInputs] memory input, bytes32 pk_sender, bytes memory ciphertext0, bytes memory ciphertext1 @@ -39,7 +84,7 @@ contract Groth16Mixer is BaseMixer { // 2.a Verify the proof require( - zksnark_verifier.verifyTx(a, b, c, input), + verifyTx(a, b, c, input), "Invalid proof: Unable to verify the proof correctly" ); @@ -74,4 +119,242 @@ contract Groth16Mixer is BaseMixer { // respective keys emit_ciphertexts(pk_sender, ciphertext0, ciphertext1); } + + function verify( + uint[] memory input, + Proof memory proof) internal returns (uint) { + + // `input.length` = size of the instance = l (see notations in the + // reference paper). We have coefficients indexed in the range[1..l], + // where l is the instance size, and we define a_0 = 1. This is the + // reason we need to check that: input.length + 1 == vk.ABC.length (the + // +1 accounts for a_0). This equality is a strong consistency check + // (len(givenInputs) needs to equal expectedInputSize (not less)) + require( + input.length + 1 == verifyKey.ABC.length, + "Input length differs from expected"); + + // A memory scratch pad + uint[24] memory pad; + + // 1. Compute the linear combination + // vk_x = \sum_{i=0}^{l} a_i * vk.ABC[i], vk_x in G1. + // + // ORIGINAL CODE: + // Pairing.G1Point memory vk_x = vk.ABC[0]; // a_0 = 1 + // for (uint i = 0; i < input.length; i++) { + // vk_x = Pairing.add(vk_x, Pairing.mul(vk.ABC[i + 1], input[i])); + // } + // + // The linear combination loop was the biggest cost center of the mixer + // contract. The following assembly block removes a lot of unnecessary + // memory usage and data copying, but relies on the structure of storage + // data. + // + // `pad` is layed out as follows, (so that calls to precompiled + // contracts can be done with minimal data copying) + // + // OFFSET USAGE + // 0x20 accum_y + // 0x00 accum_x + + // In each iteration, copy scalar multiplicaation data to 0x40+ + // + // OFFSET USAGE + // 0x80 input_i -- + // 0x60 abc_y | compute abc[i+1] * input[i] in-place + // 0x40 abc_x -- + // 0x20 accum_y + // 0x00 accum_x + // + // ready to call bn256ScalarMul(in: 0x40, out: 0x40). This results in: + // + // OFFSET USAGE + // 0x80 + // 0x60 input_i * abc_y -- + // 0x40 input_i * abc_x | accum = accum + input[i] * abc[i+1] + // 0x20 accum_y | + // 0x00 accum_x -- + // + // ready to call bn256Add(in: 0x00, out: 0x00) to update accum_x, + // accum_y in place. + + bool success = true; + assembly { + + let g := sub(gas, 2000) + + // Compute slot of ABC[0], using pad[0] as a temporary + mstore(pad, add(verifyKey_slot, 10)) + let abc_slot := keccak256(pad, 32) + + // Compute input array bounds (layout: ,elem_0,elem_1...) + let input_i := add(input, 0x20) + let input_end := add(input_i, mul(0x20, mload(input))) + + // Initialize pad[0] with abc[0] + mstore(pad, sload(abc_slot)) + mstore(add(pad, 0x20), sload(add(abc_slot, 1))) + abc_slot := add(abc_slot, 2) + + // Location within pad to do scalar mul operation + let mul_in := add(pad, 0x40) + + // Iterate over all inputs / ABC values + for + { } + lt(input_i, input_end) + { + abc_slot := add(abc_slot, 2) + input_i := add(input_i, 0x20) + } + { + // Copy abc[i+1] into mul_in, incrementing abc + mstore(mul_in, sload(abc_slot)) + mstore(add(mul_in, 0x20), sload(add(abc_slot, 1))) + + // Copy input[i] into mul_in + 0x40, and increment index_i + mstore(add(mul_in, 0x40), mload(input_i)) + + // bn256ScalarMul and bn256Add can be done with no copying + let s1 := call(g, 7, 0, mul_in, 0x60, mul_in, 0x40) + let s2 := call(g, 6, 0, pad, 0x80, pad, 0x40) + success := and(success, and(s1, s2)) + } + } + + require( + success, + "Call to the bn256Add or bn256ScalarMul precompiled failed"); + + // 2. The verification check: + // e(Proof.A, Proof.B) = + // e(vk.Alpha, vk.Beta) * e(vk_x, P2) * e(Proof.C, vk.Delta) + // where: + // - e: G_1 x G_2 -> G_T is a bilinear map + // - `*`: denote the group operation in G_T + + // ORIGINAL CODE: + // bool res = Pairing.pairingProd4( + // Pairing.negate(Pairing.G1Point(proof.A_X, proof.A_Y)), + // Pairing.G2Point(proof.B_X0, proof.B_X1, proof.B_Y0, proof.B_Y1), + // verifyKey.Alpha, verifyKey.Beta, + // vk_x, Pairing.P2(), + // Pairing.G1Point(proof.C_X, proof.C_Y), + // verifyKey.Delta); + // if (!res) { + // return 0; + // } + // return 1; + + // Assembly below fills out pad and calls bn256Pairing, performing a + // check of the form: + // + // e(vk_x, P2) * e(vk.Alpha, vk.Beta) * + // e(negate(Proof.A), Proof.B) * e(Proof.C, vk.Delta) == 1 + // + // See Pairing.pairing(). Note terms have been re-ordered since vk_x is + // already at offset 0x00. Memory is laid out: + // + // 0x0300 + // 0x0280 - verifyKey.Delta in G2 + // 0x0240 - proof.C in G1 + // 0x01c0 - Proof.B in G2 + // 0x0180 - negate(Proof.A) in G1 + // 0x0100 - vk.Beta in G2 + // 0x00c0 - vk.Alpha in G1 + // 0x0040 - P2 in G2 + // 0x0000 - vk_x in G1 (Already present, by the above) + + assembly { + + // Write P2, from offset 0x40. See Pairing for these values. + mstore( + add(pad, 0x040), + 11559732032986387107991004021392285783925812861821192530917403151452391805634) + mstore( + add(pad, 0x060), + 10857046999023057135944570762232829481370756359578518086990519993285655852781) + mstore( + add(pad, 0x080), + 4082367875863433681332203403145435568316851327593401208105741076214120093531) + mstore( + add(pad, 0x0a0), + 8495653923123431417604973247489272438418190587263600148770280649306958101930) + + // Write vk.Alpha, vk.Beta (first 6 uints from verifyKey) from + // offset 0x0c0. + mstore(add(pad, 0x0c0), sload(verifyKey_slot)) + mstore(add(pad, 0x0e0), sload(add(verifyKey_slot, 1))) + mstore(add(pad, 0x100), sload(add(verifyKey_slot, 2))) + mstore(add(pad, 0x120), sload(add(verifyKey_slot, 3))) + mstore(add(pad, 0x140), sload(add(verifyKey_slot, 4))) + mstore(add(pad, 0x160), sload(add(verifyKey_slot, 5))) + + // Write negate(Proof.A) and Proof.B from offset 0x180. + mstore(add(pad, 0x180), mload(proof)) + let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 + let proof_A_y := mload(add(proof, 0x20)) + mstore(add(pad, 0x1a0), sub(q, mod(proof_A_y, q))) + mstore(add(pad, 0x1c0), mload(add(proof, 0x40))) + mstore(add(pad, 0x1e0), mload(add(proof, 0x60))) + mstore(add(pad, 0x200), mload(add(proof, 0x80))) + mstore(add(pad, 0x220), mload(add(proof, 0xa0))) + + // Proof.C and verifyKey.Delta from offset 0x240. + mstore(add(pad, 0x240), mload(add(proof, 0xc0))) + mstore(add(pad, 0x260), mload(add(proof, 0xe0))) + mstore(add(pad, 0x280), sload(add(verifyKey_slot, 6))) + mstore(add(pad, 0x2a0), sload(add(verifyKey_slot, 7))) + mstore(add(pad, 0x2c0), sload(add(verifyKey_slot, 8))) + mstore(add(pad, 0x2e0), sload(add(verifyKey_slot, 9))) + + success := call(sub(gas, 2000), 8, 0, pad, 0x300, pad, 0x20) + } + + require( + success, + "Call to bn256Add, bn256ScalarMul or bn256Pairing failed"); + return pad[0]; + } + + function verifyTx( + uint[2] memory a, + uint[4] memory b, + uint[2] memory c, + uint[nbInputs] memory primaryInputs + ) internal returns (bool) { + // Scalar field characteristic + // solium-disable-next-line + uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + Proof memory proof; + proof.A_X = a[0]; + proof.A_Y = a[1]; + proof.B_X0 = b[0]; + proof.B_X1 = b[1]; + proof.B_Y0 = b[2]; + proof.B_Y1 = b[3]; + proof.C_X = c[0]; + proof.C_Y = c[1]; + + uint[] memory inputValues = new uint[](primaryInputs.length); + for (uint i = 0; i < primaryInputs.length; i++) { + // Make sure that all primary inputs lie in the scalar field + require( + primaryInputs[i] < r, + "Input is not in scalar field" + ); + inputValues[i] = primaryInputs[i]; + } + + uint verification_result = verify(inputValues, proof); + if (verification_result != 1) { + /* emit LogVerifier("Failed to verify the transaction"); */ + return false; + } + + /* emit LogVerifier("Proof verification successfull"); */ + return true; + } } diff --git a/zeth-contracts/contracts/Groth16Verifier.sol b/zeth-contracts/contracts/Groth16Verifier.sol deleted file mode 100644 index 6927ae964..000000000 --- a/zeth-contracts/contracts/Groth16Verifier.sol +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (c) 2015-2019 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -/* - * Reference paper: - * - * \[Gro16]: - * "On the Size of Pairing-based Non-interactive Arguments", - * Jens Groth, - * EUROCRYPT 2016, - * -**/ - -import "./Pairing.sol"; - -// Groth16 Verifier contract -contract Groth16Verifier { - using Pairing for *; - - // The structure of the verification key differs from the reference paper. - // It doesn't contain any element of GT, but only elements of G1 and G2 (the - // source groups). This is due to the lack of precompiled contract to - // manipulate elements of the target group GT on Ethereum. - struct VerifyingKey { - Pairing.G1Point Alpha; // slots 0x00, 0x01 - Pairing.G2Point Beta; // slots 0x02, 0x03, 0x04, 0x05 - Pairing.G2Point Delta; // slots 0x06, 0x07, 0x08, 0x09 - Pairing.G1Point[] ABC; // slot 0x0a - } - - // Internal Proof structure. Avoids reusing the G1 and G2 structs, since - // these cause extra pointers in memory, and complexity passing the data to - // precompiled contracts. - struct Proof { - // Pairing.G1Point A; - uint A_X; - uint A_Y; - // Pairing.G2Point B; - uint B_X0; - uint B_X1; - uint B_Y0; - uint B_Y1; - // Pairing.G1Point C; - uint C_X; - uint C_Y; - } - - VerifyingKey verifyKey; - - event LogVerifier(string); - - constructor( - uint[2] memory Alpha, - uint[2] memory Beta1, - uint[2] memory Beta2, - uint[2] memory Delta1, - uint[2] memory Delta2, - uint[] memory ABC_coords - ) public { - verifyKey.Alpha = Pairing.G1Point(Alpha[0], Alpha[1]); - verifyKey.Beta = Pairing.G2Point(Beta1[0], Beta1[1], Beta2[0], Beta2[1]); - verifyKey.Delta = Pairing.G2Point( - Delta1[0], Delta1[1], Delta2[0], Delta2[1]); - - // The `ABC` are elements of G1 (and thus have 2 coordinates in the - // underlying field). Here, we reconstruct these group elements from - // field elements (ABC_coords are field elements) - uint i = 0; - while(verifyKey.ABC.length != ABC_coords.length/2) { - verifyKey.ABC.push(Pairing.G1Point(ABC_coords[i], ABC_coords[i+1])); - i += 2; - } - } - - function verify( - uint[] memory input, Proof memory proof) internal returns (uint) { - - // `input.length` = size of the instance = l (see notations in the - // reference paper). We have coefficients indexed in the range[1..l], - // where l is the instance size, and we define a_0 = 1. This is the - // reason we need to check that: input.length + 1 == vk.ABC.length (the - // +1 accounts for a_0). This equality is a strong consistency check - // (len(givenInputs) needs to equal expectedInputSize (not less)) - require( - input.length + 1 == verifyKey.ABC.length, - "Input length differs from expected"); - - // A memory scratch pad - uint[24] memory pad; - - // 1. Compute the linear combination - // vk_x = \sum_{i=0}^{l} a_i * vk.ABC[i], vk_x in G1. - // - // ORIGINAL CODE: - // Pairing.G1Point memory vk_x = vk.ABC[0]; // a_0 = 1 - // for (uint i = 0; i < input.length; i++) { - // vk_x = Pairing.add(vk_x, Pairing.mul(vk.ABC[i + 1], input[i])); - // } - // - // The linear combination loop was the biggest cost center of the mixer - // contract. The following assembly block removes a lot of unnecessary - // memory usage and data copying, but relies on the structure of storage - // data. - // - // `pad` is layed out as follows, (so that calls to precompiled - // contracts can be done with minimal data copying) - // - // OFFSET USAGE - // 0x20 accum_y - // 0x00 accum_x - - // In each iteration, copy scalar multiplicaation data to 0x40+ - // - // OFFSET USAGE - // 0x80 input_i -- - // 0x60 abc_y | compute abc[i+1] * input[i] in-place - // 0x40 abc_x -- - // 0x20 accum_y - // 0x00 accum_x - // - // ready to call bn256ScalarMul(in: 0x40, out: 0x40). This results in: - // - // OFFSET USAGE - // 0x80 - // 0x60 input_i * abc_y -- - // 0x40 input_i * abc_x | accum = accum + input[i] * abc[i+1] - // 0x20 accum_y | - // 0x00 accum_x -- - // - // ready to call bn256Add(in: 0x00, out: 0x00) to update accum_x, - // accum_y in place. - - bool success = true; - assembly { - - let g := sub(gas, 2000) - - // Compute slot of ABC[0], using pad[0] as a temporary - mstore(pad, add(verifyKey_slot, 10)) - let abc_slot := keccak256(pad, 32) - - // Compute input array bounds (layout: ,elem_0,elem_1...) - let input_i := add(input, 0x20) - let input_end := add(input_i, mul(0x20, mload(input))) - - // Initialize pad[0] with abc[0] - mstore(pad, sload(abc_slot)) - mstore(add(pad, 0x20), sload(add(abc_slot, 1))) - abc_slot := add(abc_slot, 2) - - // Location within pad to do scalar mul operation - let mul_in := add(pad, 0x40) - - // Iterate over all inputs / ABC values - for - { } - lt(input_i, input_end) - { - abc_slot := add(abc_slot, 2) - input_i := add(input_i, 0x20) - } - { - // Copy abc[i+1] into mul_in, incrementing abc - mstore(mul_in, sload(abc_slot)) - mstore(add(mul_in, 0x20), sload(add(abc_slot, 1))) - - // Copy input[i] into mul_in + 0x40, and increment index_i - mstore(add(mul_in, 0x40), mload(input_i)) - - // bn256ScalarMul and bn256Add can be done with no copying - let s1 := call(g, 7, 0, mul_in, 0x60, mul_in, 0x40) - let s2 := call(g, 6, 0, pad, 0x80, pad, 0x40) - success := and(success, and(s1, s2)) - } - } - - require( - success, - "Call to the bn256Add or bn256ScalarMul precompiled failed"); - - // 2. The verification check: - // e(Proof.A, Proof.B) = - // e(vk.Alpha, vk.Beta) * e(vk_x, P2) * e(Proof.C, vk.Delta) - // where: - // - e: G_1 x G_2 -> G_T is a bilinear map - // - `*`: denote the group operation in G_T - - // ORIGINAL CODE: - // bool res = Pairing.pairingProd4( - // Pairing.negate(Pairing.G1Point(proof.A_X, proof.A_Y)), - // Pairing.G2Point(proof.B_X0, proof.B_X1, proof.B_Y0, proof.B_Y1), - // verifyKey.Alpha, verifyKey.Beta, - // vk_x, Pairing.P2(), - // Pairing.G1Point(proof.C_X, proof.C_Y), - // verifyKey.Delta); - // if (!res) { - // return 0; - // } - // return 1; - - // Assembly below fills out pad and calls bn256Pairing, performing a - // check of the form: - // - // e(vk_x, P2) * e(vk.Alpha, vk.Beta) * - // e(negate(Proof.A), Proof.B) * e(Proof.C, vk.Delta) == 1 - // - // See Pairing.pairing(). Note terms have been re-ordered since vk_x is - // already at offset 0x00. Memory is laid out: - // - // 0x0300 - // 0x0280 - verifyKey.Delta in G2 - // 0x0240 - proof.C in G1 - // 0x01c0 - Proof.B in G2 - // 0x0180 - negate(Proof.A) in G1 - // 0x0100 - vk.Beta in G2 - // 0x00c0 - vk.Alpha in G1 - // 0x0040 - P2 in G2 - // 0x0000 - vk_x in G1 (Already present, by the above) - - assembly { - - // Write P2, from offset 0x40. See Pairing for these values. - mstore( - add(pad, 0x040), - 11559732032986387107991004021392285783925812861821192530917403151452391805634) - mstore( - add(pad, 0x060), - 10857046999023057135944570762232829481370756359578518086990519993285655852781) - mstore( - add(pad, 0x080), - 4082367875863433681332203403145435568316851327593401208105741076214120093531) - mstore( - add(pad, 0x0a0), - 8495653923123431417604973247489272438418190587263600148770280649306958101930) - - // Write vk.Alpha, vk.Beta (first 6 uints from verifyKey) from - // offset 0x0c0. - mstore(add(pad, 0x0c0), sload(verifyKey_slot)) - mstore(add(pad, 0x0e0), sload(add(verifyKey_slot, 1))) - mstore(add(pad, 0x100), sload(add(verifyKey_slot, 2))) - mstore(add(pad, 0x120), sload(add(verifyKey_slot, 3))) - mstore(add(pad, 0x140), sload(add(verifyKey_slot, 4))) - mstore(add(pad, 0x160), sload(add(verifyKey_slot, 5))) - - // Write negate(Proof.A) and Proof.B from offset 0x180. - mstore(add(pad, 0x180), mload(proof)) - let q := 21888242871839275222246405745257275088696311157297823662689037894645226208583 - let proof_A_y := mload(add(proof, 0x20)) - mstore(add(pad, 0x1a0), sub(q, mod(proof_A_y, q))) - mstore(add(pad, 0x1c0), mload(add(proof, 0x40))) - mstore(add(pad, 0x1e0), mload(add(proof, 0x60))) - mstore(add(pad, 0x200), mload(add(proof, 0x80))) - mstore(add(pad, 0x220), mload(add(proof, 0xa0))) - - // Proof.C and verifyKey.Delta from offset 0x240. - mstore(add(pad, 0x240), mload(add(proof, 0xc0))) - mstore(add(pad, 0x260), mload(add(proof, 0xe0))) - mstore(add(pad, 0x280), sload(add(verifyKey_slot, 6))) - mstore(add(pad, 0x2a0), sload(add(verifyKey_slot, 7))) - mstore(add(pad, 0x2c0), sload(add(verifyKey_slot, 8))) - mstore(add(pad, 0x2e0), sload(add(verifyKey_slot, 9))) - - success := call(sub(gas, 2000), 8, 0, pad, 0x300, pad, 0x20) - } - - require( - success, - "Call to bn256Add, bn256ScalarMul or bn256Pairing failed"); - return pad[0]; - } - - function verifyTx( - uint[2] memory a, - uint[4] memory b, - uint[2] memory c, - uint[] memory primaryInputs - ) public returns (bool) { - // Scalar field characteristic - // solium-disable-next-line - uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - - Proof memory proof; - proof.A_X = a[0]; - proof.A_Y = a[1]; - proof.B_X0 = b[0]; - proof.B_X1 = b[1]; - proof.B_Y0 = b[2]; - proof.B_Y1 = b[3]; - proof.C_X = c[0]; - proof.C_Y = c[1]; - - uint[] memory inputValues = new uint[](primaryInputs.length); - for (uint i = 0; i < primaryInputs.length; i++) { - // Make sure that all primary inputs lie in the scalar field - require( - primaryInputs[i] < r, - "Input is not in scalar field" - ); - inputValues[i] = primaryInputs[i]; - } - - uint verification_result = verify(inputValues, proof); - if (verification_result != 1) { - emit LogVerifier("Failed to verify the transaction"); - return false; - } - - emit LogVerifier("Proof verification successfull"); - return true; - } -} diff --git a/zeth-contracts/contracts/Pghr13Mixer.sol b/zeth-contracts/contracts/Pghr13Mixer.sol index ff735617c..2a28e28c7 100644 --- a/zeth-contracts/contracts/Pghr13Mixer.sol +++ b/zeth-contracts/contracts/Pghr13Mixer.sol @@ -5,20 +5,68 @@ pragma solidity ^0.5.0; import "./OTSchnorrVerifier.sol"; -import "./Pghr13Verifier.sol"; import "./BaseMixer.sol"; contract Pghr13Mixer is BaseMixer { - // zkSNARK verifier smart contract - Pghr13Verifier public zksnark_verifier; + + struct VerifyingKey { + Pairing.G2Point A; + Pairing.G1Point B; + Pairing.G2Point C; + Pairing.G2Point gamma; + Pairing.G1Point gammaBeta1; + Pairing.G2Point gammaBeta2; + Pairing.G2Point Z; + Pairing.G1Point[] IC; + } + + struct Proof { + Pairing.G1Point A; + Pairing.G1Point A_p; + Pairing.G2Point B; + Pairing.G1Point B_p; + Pairing.G1Point C; + Pairing.G1Point C_p; + Pairing.G1Point K; + Pairing.G1Point H; + } + + VerifyingKey verifyKey; // Constructor constructor( - address snark_ver, uint mk_depth, - address token) - BaseMixer(mk_depth, token) public { - zksnark_verifier = Pghr13Verifier(snark_ver); + address token, + uint[2] memory A1, + uint[2] memory A2, + uint[2] memory B, + uint[2] memory C1, + uint[2] memory C2, + uint[2] memory gamma1, + uint[2] memory gamma2, + uint[2] memory gammaBeta1, + uint[2] memory gammaBeta2_1, + uint[2] memory gammaBeta2_2, + uint[2] memory Z1, + uint[2] memory Z2, + uint[] memory IC_coefficients + ) BaseMixer(mk_depth, token) public { + verifyKey.A = Pairing.G2Point(A1[0], A1[1], A2[0], A2[1]); + verifyKey.B = Pairing.G1Point(B[0], B[1]); + verifyKey.C = Pairing.G2Point(C1[0], C1[1], C2[0], C2[1]); + verifyKey.gamma = Pairing.G2Point( + gamma1[0], gamma1[1], gamma2[0], gamma1[1]); + verifyKey.gammaBeta1 = Pairing.G1Point(gammaBeta1[0], gammaBeta1[1]); + verifyKey.gammaBeta2 = Pairing.G2Point( + gammaBeta2_1[0], gammaBeta2_1[1], gammaBeta2_2[0], gammaBeta2_2[1]); + verifyKey.Z = Pairing.G2Point(Z1[0], Z1[1], Z2[0], Z2[1]); + + uint i = 0; + while(verifyKey.IC.length != IC_coefficients.length/2) { + verifyKey.IC.push( + Pairing.G1Point(IC_coefficients[i], IC_coefficients[i+1])); + i += 2; + } } // This function allows to mix coins and execute payments in zero knowledge. @@ -34,7 +82,7 @@ contract Pghr13Mixer is BaseMixer { uint[2] memory k, uint[4] memory vk, uint sigma, - uint[] memory input, + uint[nbInputs] memory input, bytes32 pk_sender, bytes memory ciphertext0, bytes memory ciphertext1 @@ -44,7 +92,7 @@ contract Pghr13Mixer is BaseMixer { // 2.a Verify the proof require( - zksnark_verifier.verifyTx(a, a_p, b, b_p, c, c_p, h, k, input), + verifyTx(a, a_p, b, b_p, c, c_p, h, k, input), "Invalid proof: Unable to verify the proof correctly" ); @@ -92,4 +140,130 @@ contract Pghr13Mixer is BaseMixer { // respective keys emit_ciphertexts(pk_sender, ciphertext0, ciphertext1); } + + function getIC(uint i) public view returns (uint) { + return(verifyKey.IC[i].X); + } + + function getICLen() public view returns (uint) { + return(verifyKey.IC.length); + } + + function verify( + uint[nbInputs] memory input, + Proof memory proof + ) internal returns (uint) { + VerifyingKey memory vk = verifyKey; + // |I_{in}| == input.length, and vk.IC also contains A_0(s). Thus + // ||vk.IC| == input.length + 1 + require( + input.length + 1 == vk.IC.length, + "Using strong input consistency, and the input length differs from expected" + ); + + // 1. Compute the linear combination + // vk_x := vk_{IC,0} + \sum_{i=1}^{n} x_i * vk_{IC,i}, vk_x ∈ G1 + // + // E(A_{in}(s)) if the encoding of + // A_{in}(s) = \sum_{k ∈ I_{in}} a_k · A_k(s), + // where I_{in} denotes the indices of the input wires. + // + // |I_{in}| = n here as we assume that we have a vector x of inputs of + // size n. + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + for (uint i = 0; i < input.length; i++) { + vk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[i + 1], input[i])); + } + vk_x = Pairing.add(vk_x, vk.IC[0]); + + // 2. Check the validity of knowledge commitments for A, B, C + // e(π_A, vk_A) = e(π′A, P2), e(vk_B, π_B) + // = e(π′_B, P2), e(vk_C, π_C) + // = e(π′_C, P2), + if (!Pairing.pairingProd2( + proof.A, vk.A, + Pairing.negate(proof.A_p), Pairing.P2()) + ) { + return 1; + } + if (!Pairing.pairingProd2( + vk.B, proof.B, + Pairing.negate(proof.B_p), Pairing.P2()) + ) { + return 2; + } + if (!Pairing.pairingProd2( + proof.C, vk.C, + Pairing.negate(proof.C_p), Pairing.P2()) + ) { + return 3; + } + + // 3. Check same coefficients were used + // e(π_K, vk_γ) = e(vk_x + π_A + π_C, vk_{γβ2}) · e(vk_{γβ1}, π_B) + if (!Pairing.pairingProd3( + proof.K, vk.gamma, + Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, + Pairing.negate(vk.gammaBeta1), proof.B) + ) { + return 4; + } + + // 4. Check QAP divisibility + // e(vk_x + π_A, π_B) = e(π_H, vk_Z) · e(π_C, P2) + if (!Pairing.pairingProd3( + Pairing.add(vk_x, proof.A), proof.B, + Pairing.negate(proof.H), vk.Z, + Pairing.negate(proof.C), Pairing.P2()) + ) { + return 5; + } + + return 0; + } + + function verifyTx( + uint[2] memory a, + uint[2] memory a_p, + uint[2][2] memory b, + uint[2] memory b_p, + uint[2] memory c, + uint[2] memory c_p, + uint[2] memory h, + uint[2] memory k, + uint[nbInputs] memory primaryInputs + ) public returns (bool) { + // Scalar field characteristic + // solium-disable-next-line + uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.A_p = Pairing.G1Point(a_p[0], a_p[1]); + proof.B = Pairing.G2Point(b[0][0], b[0][1], b[1][0], b[1][1]); + proof.B_p = Pairing.G1Point(b_p[0], b_p[1]); + proof.C = Pairing.G1Point(c[0], c[1]); + proof.C_p = Pairing.G1Point(c_p[0], c_p[1]); + proof.H = Pairing.G1Point(h[0], h[1]); + proof.K = Pairing.G1Point(k[0], k[1]); + + /* uint[] memory inputValues = new uint[](primaryInputs.length); */ + for(uint i = 0; i < primaryInputs.length; i++){ + // Make sure that all primary inputs lie in the scalar field + require( + primaryInputs[i] < r, + "Input is not is scalar field" + ); + /* inputValues[i] = primaryInputs[i]; */ + } + + uint verification_result = verify(primaryInputs, proof); + if (verification_result != 0) { + /* emit LogVerifier("Failed to verify the transaction"); */ + return false; + } + + /* emit LogVerifier("Proof verification successfull"); */ + return true; + } } diff --git a/zeth-contracts/contracts/Pghr13Verifier.sol b/zeth-contracts/contracts/Pghr13Verifier.sol deleted file mode 100644 index 1b988e95e..000000000 --- a/zeth-contracts/contracts/Pghr13Verifier.sol +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2015-2019 Clearmatics Technologies Ltd -// -// SPDX-License-Identifier: LGPL-3.0+ - -pragma solidity ^0.5.0; - -/* - * Reference papers: - * \[PGHR13]: - * "Pinocchio: Nearly practical verifiable computation", - * Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova, - * IEEE S&P 2013, - * - * - * [BCTV14]: - * "Succinct Non-Interactive Zero Knowledge for a von Neumann Architecture", - * Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, - * USENIX Security 2014, - * - * - * [Gab19] - * "On the security of the BCTV Pinocchio zk-SNARK variant", - * Ariel Gabizon, - * -**/ - -import "./Pairing.sol"; - -// PGHR13 Verifier contract -contract Pghr13Verifier { - using Pairing for *; - - struct VerifyingKey { - Pairing.G2Point A; - Pairing.G1Point B; - Pairing.G2Point C; - Pairing.G2Point gamma; - Pairing.G1Point gammaBeta1; - Pairing.G2Point gammaBeta2; - Pairing.G2Point Z; - Pairing.G1Point[] IC; - } - - struct Proof { - Pairing.G1Point A; - Pairing.G1Point A_p; - Pairing.G2Point B; - Pairing.G1Point B_p; - Pairing.G1Point C; - Pairing.G1Point C_p; - Pairing.G1Point K; - Pairing.G1Point H; - } - - VerifyingKey verifyKey; - - event LogVerifier(string); - - constructor( - uint[2] memory A1, - uint[2] memory A2, - uint[2] memory B, - uint[2] memory C1, - uint[2] memory C2, - uint[2] memory gamma1, - uint[2] memory gamma2, - uint[2] memory gammaBeta1, - uint[2] memory gammaBeta2_1, - uint[2] memory gammaBeta2_2, - uint[2] memory Z1, - uint[2] memory Z2, - uint[] memory IC_coefficients - ) public { - verifyKey.A = Pairing.G2Point(A1[0], A1[1], A2[0], A2[1]); - verifyKey.B = Pairing.G1Point(B[0], B[1]); - verifyKey.C = Pairing.G2Point(C1[0], C1[1], C2[0], C2[1]); - verifyKey.gamma = Pairing.G2Point( - gamma1[0], gamma1[1], gamma2[0], gamma1[1]); - verifyKey.gammaBeta1 = Pairing.G1Point(gammaBeta1[0], gammaBeta1[1]); - verifyKey.gammaBeta2 = Pairing.G2Point( - gammaBeta2_1[0], gammaBeta2_1[1], gammaBeta2_2[0], gammaBeta2_2[1]); - verifyKey.Z = Pairing.G2Point(Z1[0], Z1[1], Z2[0], Z2[1]); - - uint i = 0; - while(verifyKey.IC.length != IC_coefficients.length/2) { - verifyKey.IC.push( - Pairing.G1Point(IC_coefficients[i], IC_coefficients[i+1])); - i += 2; - } - } - - function getIC(uint i) public view returns (uint) { - return(verifyKey.IC[i].X); - } - - function getICLen() public view returns (uint) { - return(verifyKey.IC.length); - } - - function verify( - uint[] memory input, Proof memory proof) internal returns (uint) { - VerifyingKey memory vk = verifyKey; - // |I_{in}| == input.length, and vk.IC also contains A_0(s). Thus - // ||vk.IC| == input.length + 1 - require( - input.length + 1 == vk.IC.length, - "Using strong input consistency, and the input length differs from expected" - ); - - // 1. Compute the linear combination - // vk_x := vk_{IC,0} + \sum_{i=1}^{n} x_i * vk_{IC,i}, vk_x ∈ G1 - // - // E(A_{in}(s)) if the encoding of - // A_{in}(s) = \sum_{k ∈ I_{in}} a_k · A_k(s), - // where I_{in} denotes the indices of the input wires. - // - // |I_{in}| = n here as we assume that we have a vector x of inputs of - // size n. - Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); - for (uint i = 0; i < input.length; i++) { - vk_x = Pairing.add(vk_x, Pairing.mul(vk.IC[i + 1], input[i])); - } - vk_x = Pairing.add(vk_x, vk.IC[0]); - - // 2. Check the validity of knowledge commitments for A, B, C - // e(π_A, vk_A) = e(π′A, P2), e(vk_B, π_B) - // = e(π′_B, P2), e(vk_C, π_C) - // = e(π′_C, P2), - if (!Pairing.pairingProd2( - proof.A, vk.A, - Pairing.negate(proof.A_p), Pairing.P2()) - ) { - return 1; - } - if (!Pairing.pairingProd2( - vk.B, proof.B, - Pairing.negate(proof.B_p), Pairing.P2()) - ) { - return 2; - } - if (!Pairing.pairingProd2( - proof.C, vk.C, - Pairing.negate(proof.C_p), Pairing.P2()) - ) { - return 3; - } - - // 3. Check same coefficients were used - // e(π_K, vk_γ) = e(vk_x + π_A + π_C, vk_{γβ2}) · e(vk_{γβ1}, π_B) - if (!Pairing.pairingProd3( - proof.K, vk.gamma, - Pairing.negate(Pairing.add(vk_x, Pairing.add(proof.A, proof.C))), vk.gammaBeta2, - Pairing.negate(vk.gammaBeta1), proof.B) - ) { - return 4; - } - - // 4. Check QAP divisibility - // e(vk_x + π_A, π_B) = e(π_H, vk_Z) · e(π_C, P2) - if (!Pairing.pairingProd3( - Pairing.add(vk_x, proof.A), proof.B, - Pairing.negate(proof.H), vk.Z, - Pairing.negate(proof.C), Pairing.P2()) - ) { - return 5; - } - - return 0; - } - - function verifyTx( - uint[2] memory a, - uint[2] memory a_p, - uint[2][2] memory b, - uint[2] memory b_p, - uint[2] memory c, - uint[2] memory c_p, - uint[2] memory h, - uint[2] memory k, - uint[] memory primaryInputs - ) public returns (bool) { - // Scalar field characteristic - // solium-disable-next-line - uint256 r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - - Proof memory proof; - proof.A = Pairing.G1Point(a[0], a[1]); - proof.A_p = Pairing.G1Point(a_p[0], a_p[1]); - proof.B = Pairing.G2Point(b[0][0], b[0][1], b[1][0], b[1][1]); - proof.B_p = Pairing.G1Point(b_p[0], b_p[1]); - proof.C = Pairing.G1Point(c[0], c[1]); - proof.C_p = Pairing.G1Point(c_p[0], c_p[1]); - proof.H = Pairing.G1Point(h[0], h[1]); - proof.K = Pairing.G1Point(k[0], k[1]); - - uint[] memory inputValues = new uint[](primaryInputs.length); - for(uint i = 0; i < primaryInputs.length; i++){ - // Make sure that all primary inputs lie in the scalar field - require( - primaryInputs[i] < r, - "Input is not is scalar field" - ); - inputValues[i] = primaryInputs[i]; - } - - uint verification_result = verify(inputValues, proof); - if (verification_result != 0) { - emit LogVerifier("Failed to verify the transaction"); - return false; - } - - emit LogVerifier("Proof verification successfull"); - return true; - } -}