示例#1
0
文件: offchain.py 项目: nibau/zkay
    def _function_ctx(self,
                      trans_sec_size=-1,
                      *,
                      wei_amount: int = 0,
                      name: str = '?'):
        with self.api.api_function_ctx(trans_sec_size,
                                       wei_amount) as is_external:
            if is_external:
                zk_print_banner(f'Calling {name}')
                assert self.locals is None
                self.state.clear()
                t_idx = self.tidx.get(name, 0)
                self.tidx[name] = t_idx + 1

            with nullcontext() if not is_external else log_context(
                    'transaction', f'{name}_{t_idx}'):
                prev_locals = self.locals
                self.locals = LocalsDict()

                try:
                    yield is_external
                except (ValueError, BlockChainError, RequireException) as e:
                    if is_external and not cfg.is_unit_test:
                        # uncomment to raise errors instead of just printing message (for debugging)
                        # raise e
                        with fail_print():
                            print(f'ERROR: {e}')
                    else:
                        raise e
                finally:
                    self.locals = prev_locals
                    if is_external:
                        self.state.clear()
示例#2
0
    def _connect_libraries(self):
        zk_print_banner(f'Deploying Libraries')

        sender = self.w3.eth.accounts[0]
        # Since eth-tester is not persistent -> always automatically deploy libraries
        with cfg.library_compilation_environment():
            with tempfile.TemporaryDirectory() as tmpdir:
                with log_context('transaction', 'deploy_pki'):
                    pki_sol = save_to_file(
                        tmpdir, f'{cfg.pki_contract_name}.sol',
                        library_contracts.get_pki_contract())
                    self._pki_contract = self._deploy_contract(
                        sender,
                        self.compile_contract(pki_sol, cfg.pki_contract_name))
                    zk_print(
                        f'Deployed pki contract at address "{self.pki_contract.address}"'
                    )

                with log_context('transaction', 'deploy_verify_libs'):
                    verify_sol = save_to_file(
                        tmpdir, 'verify_libs.sol',
                        library_contracts.get_verify_libs_code())
                    self._lib_addresses = {}
                    for lib in cfg.external_crypto_lib_names:
                        out = self._deploy_contract(
                            sender, self.compile_contract(verify_sol, lib))
                        self._lib_addresses[lib] = out.address
                        zk_print(
                            f'Deployed crypto lib {lib} at address "{out.address}"'
                        )
示例#3
0
    def deploy(self, project_dir: str, sender: AddressValue, contract: str, actual_args: List, should_encrypt: List[bool], wei_amount: Optional[int] = None) -> Any:
        """
        Issue a deployment transaction which constructs the specified contract with the provided constructor arguments on the chain.

        **WARNING: THIS ISSUES A CRYPTO CURRENCY TRANSACTION (GAS COST)**

        :param project_dir: directory where the zkay file, manifest and snark keys reside
        :param sender: creator address, its eth private key must be hosted in the eth node to which the backend connects.
        :param contract: name of the contract to instantiate
        :param actual_args: the constructor argument values
        :param should_encrypt: a list which contains a boolean value for each argument, which should be true if the corresponding
                               parameter expects an encrypted/private value (this is only used for a last sanity-check)
        :param wei_amount: how much money to send along with the constructor transaction (only for payable constructors)
        :raise BlockChainError: if there is an error in the backend
        :raise TransactionFailedException: if the deployment transaction failed
        :return: handle for the newly created 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'Deploy {contract}')

        self.__check_args(actual_args, should_encrypt)
        zk_print(f'Deploying contract {contract}{Value.collection_to_string(actual_args)}')
        ret = self._deploy(project_dir, sender.val, contract, *Value.unwrap_values(actual_args), wei_amount=wei_amount)
        zk_print()
        return ret
示例#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