Beispiel #1
0
    def generate_proof(self, project_dir: str, contract: str, function: str, priv_values: List, in_vals: List, out_vals: List[Union[int, CipherValue]]) -> List[int]:
        """
        Generate a NIZK-proof using the provided circuit for the given arguments.

        Note: circuit arguments must be in the same order as they are declared inside the circuit. (i.e. in execution order)

        :param project_dir: directory where the manifest and the prover keys are located
        :param contract: contract of which the function which requires verification is part of
        :param function: the contract member function for which a proof needs to be generated
        :param priv_values: private/auxiliary circuit inputs in correct order
        :param in_vals: public circuit inputs in correct order
        :param out_vals: public circuit outputs in correct order
        :raise ProofGenerationError: if proof generation fails
        :return: the proof, serialized into an uint256 array
        """
        for i in range(len(priv_values)):
            arg = priv_values[i]
            assert not isinstance(arg, Value) or isinstance(arg, (RandomnessValue, AddressValue))
            if isinstance(arg, AddressValue):
                priv_values[i] = int.from_bytes(arg.val, byteorder='big')

        zk_print(f'Generating proof for {contract}.{function}')
        zk_print(f'[priv: {Value.collection_to_string(priv_values)}] '
                 f'[in: {Value.collection_to_string(in_vals)}] [out: {Value.collection_to_string(out_vals)}]', verbosity_level=2)

        priv_values, in_vals, out_vals = Value.unwrap_values(Value.flatten(priv_values)), Value.unwrap_values(in_vals), Value.unwrap_values(out_vals)

        # Check for overflows
        for arg in priv_values + in_vals + out_vals:
            assert int(arg) < bn128_scalar_field, 'argument overflow'

        with time_measure(f'generate_proof', True):
            verify_dir = cfg.get_circuit_output_dir_name(cfg.get_verification_contract_name(contract, function))
            return self._generate_proof(os.path.join(project_dir, verify_dir), priv_values, in_vals, out_vals)
Beispiel #2
0
def _collect_package_contents(contract_dir: str, check_all_files: bool) -> List[str]:
    """
    Return list of relative paths of all files which should be part of the package for the contract in contract_dir.

    Raises an exception if contract.zkay, manifest.json or any of the files required by contract.zkay is missing.

    :param contract_dir: path to directory containing manifest and zkay file
    :param check_all_files: if true, checks whether all expected files are present
    :raise FileNotFoundError: if any of the expected files is not present
    :return: list of relative paths (relative to contract_dir)
    """

    zkay_filename = os.path.join(contract_dir, 'contract.zkay')
    if not os.path.exists(zkay_filename):
        raise FileNotFoundError('contract.zkay not found in package')

    manifest_filename = os.path.join(contract_dir, 'manifest.json')
    if not os.path.exists(manifest_filename):
        raise FileNotFoundError('manifest.json not found in package')
    manifest = Manifest.load(contract_dir)

    files = ['contract.zkay', 'manifest.json']
    with open(zkay_filename) as f:
        verifier_names = get_verification_contract_names(f.read())
    with Manifest.with_manifest_config(manifest):
        gen_cls = generator_classes[cfg.snark_backend]
        files += [os.path.join(cfg.get_circuit_output_dir_name(v), k)
                  for k in gen_cls.get_vk_and_pk_filenames() for v in verifier_names]

    if check_all_files:
        for f in files:
            path = os.path.join(contract_dir, f)
            if not os.path.exists(path) or not os.path.isfile(path):
                raise FileNotFoundError(f)
    return files
Beispiel #3
0
 def _get_circuit_output_dir(self, circuit: CircuitHelper):
     """Return the output directory for an individual circuit"""
     return os.path.join(self.output_dir, cfg.get_circuit_output_dir_name(circuit.get_verification_contract_name()))
Beispiel #4
0
    def connect(self, project_dir: str, contract: str, contract_address: AddressValue, user_address: AddressValue) -> Any:
        """
        Create a handle which can be used to interact with an existing contract on the chain after verifying its integrity.

        Project dir must contain a .zkay file, a manifest.json file as well as a \
        subdirectory *verification_contract_name*\\ _out containing 'proving.key' and 'verification.key' for each verification contract.
        These files are referred to as 'local' files in the following explanation.

        If this function succeeds, it is guaranteed, that:

        * the remote main contract at contract_address, matches the solidity contract obtained by running zkay on the local zkay file
          using the configuration stored in the local manifest
        * the pki contract referenced in the remote main contract matches the correct zkay pki contract
        * the verification contracts referenced in the remote solidity contract were generated by running zkay on a zkay file
          equivalent to local zkay file, with zk-snark keys which match the local keys.
        * the library contract referenced in the verification contracts matches the correct zkay library contract

        This reduces the required trust to the zk-snark setup phase (i.e. you must trust that prover/verification keys
        were generated for the correct circuit), since you can inspect the source code of the local zkay file and check it
        for malicious behavior yourself (and the zkay implementation, which performs the transformation, is open source as well).

        Example Scenarios:

        a) the remote zkay contract is benign (generated by and deployed using zkay):
           -> you will only be able to connect if the local files are equivalent -> correctness is guaranteed
        b) the remote zkay contract was tampered with (any of the .sol files was modified was modified before deployment)
           -> connection will fail, because local zkay compilation will not produce matching evm bytecode
        c) the prover/verification keys were tampered with (they were generated for a different circuit than the one produced by zkay)

           * local keys are genuine -> connection will be refused because the keys don't match what is baked into the remote verification contract
           * same tampered keys locally -> NO GUARANTEES, since the trust assumption is violated

        :param project_dir: directory where the zkay file, manifest and snark keys reside
        :param contract: name of the contract to connect to
        :param contract_address: address of the deployed contract
        :param user_address: account which connects to the contract
        :raise IntegrityError: if the integrity check fails (mismatch between local code and remote contract)
        :return: contract handle for the specified contract
        """
        if not self.is_debug_backend() and cfg.crypto_backend == 'dummy':
            raise BlockChainError('SECURITY ERROR: Dummy encryption can only be used with debug blockchain backends (w3-eth-tester or w3-ganache).')

        zk_print_banner(f'Connect to {contract}@{contract_address}')

        # If not already done, compile zkay file to generate main and verification contracts (but don't generate new prover/verification keys and manifest)
        zk_file = os.path.join(project_dir, 'contract.zkay')
        if not os.path.exists(zk_file):
            raise IntegrityError('No zkay contract found in specified directory')
        verifier_names = []
        if not os.path.exists(os.path.join(project_dir, 'contract.sol')):
            compile_zkay_file(zk_file, project_dir, import_keys=True, verifier_names=verifier_names)
        else:
            with open(zk_file) as f:
                verifier_names = get_verification_contract_names(f.read())

        zk_print(f'Connecting to contract {contract}@{contract_address}')
        contract_on_chain = self._connect(project_dir, contract, contract_address.val)

        pki_verifier_addresses = {}

        # Check integrity of all pki contracts
        self._pki_contract = {}
        for crypto_params in cfg.all_crypto_params():
            contract_name = cfg.get_pki_contract_name(crypto_params)
            pki_address = self._req_state_var(contract_on_chain, f'{contract_name}_inst')
            pki_verifier_addresses[contract_name] = AddressValue(pki_address)
            with cfg.library_compilation_environment():
                contract = self._verify_contract_integrity(pki_address, os.path.join(project_dir, f'{contract_name}.sol'))
                self._pki_contract[crypto_params.crypto_name] = contract

        # Check verifier contract and library integrity
        if verifier_names:
            some_vname = verifier_names[0]

            libraries = [(lib_name, os.path.join(project_dir, ProvingScheme.verify_libs_contract_filename)) for lib_name in cfg.external_crypto_lib_names]
            some_vcontract = self._req_state_var(contract_on_chain, f'{some_vname}_inst')
            libs = self._verify_library_integrity(libraries, some_vcontract, os.path.join(project_dir, f'{some_vname}.sol'))
            self._lib_addresses = libs

            for verifier in verifier_names:
                v_address = self._req_state_var(contract_on_chain, f'{verifier}_inst')
                pki_verifier_addresses[verifier] = AddressValue(v_address)
                vcontract = self._verify_contract_integrity(v_address, os.path.join(project_dir, f'{verifier}.sol'), libraries=libs)

                # Verify prover key
                expected_hash = self._req_state_var(vcontract, cfg.prover_key_hash_name)
                from zkay.transaction.runtime import Runtime
                actual_hash = Runtime.prover().get_prover_key_hash(os.path.join(project_dir, cfg.get_circuit_output_dir_name(verifier)))
                if expected_hash != actual_hash:
                    raise IntegrityError(f'Prover key hash in deployed verification contract does not match local prover key file for "{verifier}"')

        # Check zkay contract integrity
        self._verify_zkay_contract_integrity(contract_on_chain.address, project_dir, pki_verifier_addresses)

        with success_print():
            zk_print(f'OK: Bytecode on blockchain matches local zkay contract')
        zk_print(f'Connection from account 0x{user_address} established\n')

        return contract_on_chain