def visitIdentifierExpr(self, ast: IdentifierExpr): # Special identifiers pki_inst_names = { f'{cfg.get_pki_contract_name(params)}_inst': params for params in cfg.all_crypto_params() } if ast.idf.name in pki_inst_names and not ast.is_lvalue(): crypto_params = pki_inst_names[ast.idf.name] return f'{api("get_keystore")}("{crypto_params.crypto_name}")' elif ast.idf.name == cfg.field_prime_var_name: assert ast.is_rvalue() return f'{SCALAR_FIELD_NAME}' if self.current_index: # This identifier is the beginning of an Index expression e.g. idf[1][2] or idf[me] indices, t = list(reversed( self.current_index)), self.current_index_t self.current_index, self.current_index_t = [], None indices = [self.visit(idx) for idx in indices] elif self.inside_circuit and isinstance( ast.idf, HybridArgumentIdf ) and ast.idf.corresponding_priv_expression is not None and self.flatten_hybrid_args: return self.visit(ast.idf.corresponding_priv_expression) else: indices, t = [], ast.target.annotated_type if isinstance( ast.target, StateVariableDeclaration) else None return self.get_value(ast, indices)
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('deploy_pki'): self._pki_contract = {} for crypto_params in cfg.all_crypto_params(): with log_context(crypto_params.crypto_name): pki_contract_code = library_contracts.get_pki_contract(crypto_params) pki_contract_name = cfg.get_pki_contract_name(crypto_params) pki_sol = save_to_file(tmpdir, f'{pki_contract_name}.sol', pki_contract_code) contract = self._deploy_contract(sender, self.compile_contract(pki_sol, pki_contract_name)) backend_name = crypto_params.crypto_name self._pki_contract[backend_name] = contract zk_print(f'Deployed pki contract for crypto back-end {backend_name} at address "{contract.address}"') with log_context('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}"')
def initialize_keys_for(address: Union[bytes, str]): """Generate/Load keys for the given address.""" account = AddressValue(address) for crypto_params in cfg.all_crypto_params(): if not Runtime.keystore(crypto_params).has_initialized_keys_for( AddressValue(address)): Runtime.crypto(crypto_params).generate_or_load_key_pair( account)
def _deploy_dependencies(self, sender: Union[bytes, str], project_dir: str, verifier_names: List[str]) -> Dict[str, AddressValue]: # Deploy verification contracts if not already done vf = {} for verifier_name in verifier_names: with log_context('constructor'): with log_context(f'{verifier_name}'): filename = os.path.join(project_dir, f'{verifier_name}.sol') cout = self.compile_contract(filename, verifier_name, self.lib_addresses) with time_measure("transaction_full"): vf[verifier_name] = AddressValue(self._deploy_contract(sender, cout).address) for crypto_params in cfg.all_crypto_params(): pki_contract_name = cfg.get_pki_contract_name(crypto_params) pki_contract_address = self.pki_contract(crypto_params.crypto_name).address vf[pki_contract_name] = AddressValue(pki_contract_address) return vf
def __init__(self, project_dir, contract_name, user_addr) -> None: super().__init__() self.__conn = Runtime.blockchain() self.__keystore = {} self.__crypto = {} self.__prover = Runtime.prover() for crypto_params in cfg.all_crypto_params(): self.__keystore[crypto_params.crypto_name] = Runtime.keystore( crypto_params) self.__crypto[crypto_params.crypto_name] = Runtime.crypto( crypto_params) self.__project_dir = project_dir self.__contract_name = contract_name self.__contract_handle = None """Handle which refers to the deployed contract, this is passed to the blockchain interface when e.g. issuing transactions.""" self.__user_addr = user_addr """From address for all transactions which are issued by this ContractSimulator""" self.__current_msg: Optional[MsgStruct] = None self.__current_block: Optional[BlockStruct] = None self.__current_tx: Optional[TxStruct] = None """ Builtin variable (msg, block, tx) values for the current transaction """ self.current_priv_values: Dict[str, Union[int, bool, RandomnessValue]] = {} """Dictionary which stores the private circuit values (secret inputs) for the current function (no transitivity)""" self.all_priv_values: Optional[List[Union[int, bool, RandomnessValue]]] = None """List which stores all secret circuit inputs for the current transaction in correct order (order of use)""" self.current_all_index: Optional[int] = None """ Index which designates where in all_priv_values the secret circuit inputs of the current function should be inserted. This is basically private analogue of the start_index parameters which are passed to functions which require verification to designate where in the public IO arrays the functions should store/retrieve public circuit inputs/outputs. """ self.is_external: Optional[bool] = None """
def add_function_circuit_arguments(circuit: CircuitHelper): """Generate java code which adds circuit IO as described by circuit""" input_init_stmts = [] for sec_input in circuit.sec_idfs: input_init_stmts.append( f'addS("{sec_input.name}", {sec_input.t.size_in_uints}, {_get_t(sec_input.t)});' ) for pub_input in circuit.input_idfs: if pub_input.t.is_key(): backend = pub_input.t.crypto_params.crypto_name input_init_stmts.append( f'addK("{backend}", "{pub_input.name}", {pub_input.t.size_in_uints});' ) else: input_init_stmts.append( f'addIn("{pub_input.name}", {pub_input.t.size_in_uints}, {_get_t(pub_input.t)});' ) for pub_output in circuit.output_idfs: input_init_stmts.append( f'addOut("{pub_output.name}", {pub_output.t.size_in_uints}, {_get_t(pub_output.t)});' ) sec_input_names = [sec_input.name for sec_input in circuit.sec_idfs] for crypto_params in cfg.all_crypto_params(): pk_name = circuit.get_glob_key_name(MeExpr(), crypto_params) sk_name = circuit.get_own_secret_key_name(crypto_params) if crypto_params.is_symmetric_cipher() and sk_name in sec_input_names: assert pk_name in [ pub_input.name for pub_input in circuit.input_idfs ] input_init_stmts.append( f'setKeyPair("{crypto_params.crypto_name}", "{pk_name}", "{sk_name}");' ) return input_init_stmts
def _connect_libraries(self): if not cfg.blockchain_pki_address: raise BlockChainError('Must specify pki address in config.') lib_addresses = [] if cfg.external_crypto_lib_names: lib_addresses = [addr.strip() for addr in cfg.blockchain_crypto_lib_addresses.split(',')] if cfg.blockchain_crypto_lib_addresses else [] if len(lib_addresses) != len(cfg.external_crypto_lib_names): raise BlockChainError('Must specify all crypto library addresses in config\n' f'Expected {len(cfg.external_crypto_lib_names)} was {len(lib_addresses)}') with cfg.library_compilation_environment(): with tempfile.TemporaryDirectory() as tmpdir: for crypto_params in cfg.all_crypto_params(): contract_name = cfg.get_pki_contract_name(crypto_params.crypto_name) pki_sol = save_to_file(tmpdir, f'{contract_name}.sol', library_contracts.get_pki_contract(crypto_params)) self._pki_contract = self._verify_contract_integrity(cfg.blockchain_pki_address, pki_sol, contract_name=contract_name) verify_sol = save_to_file(tmpdir, 'verify_libs.sol', library_contracts.get_verify_libs_code()) self._lib_addresses = {} for lib, addr in zip(cfg.external_crypto_lib_names, lib_addresses): out = self._verify_contract_integrity(addr, verify_sol, contract_name=lib, is_library=True) self._lib_addresses[lib] = out.address
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
def create_external_wrapper_body(int_fct: ConstructorOrFunctionDefinition, ext_circuit: CircuitHelper, original_params: List[Parameter], requires_proof: bool) -> Block: """ Return Block with external wrapper function body. :param int_fct: corresponding internal function :param ext_circuit: [SIDE EFFECT] circuit helper of the external wrapper function :param original_params: list of transformed function parameters without additional parameters added due to transformation :return: body with wrapper code """ priv_args = [p for p in original_params if p.annotated_type.is_cipher()] args_backends = OrderedDict.fromkeys([p.annotated_type.type_name.crypto_params for p in priv_args]) stmts = [] for crypto_params in args_backends: assert crypto_params in int_fct.used_crypto_backends # If there are any private arguments with homomorphism 'hom', we need the public key for that crypto backend ext_circuit._require_public_key_for_label_at(None, Expression.me_expr(), crypto_params) for crypto_params in cfg.all_crypto_params(): if crypto_params.is_symmetric_cipher(): if (MeExpr(), crypto_params) in ext_circuit.requested_global_keys or crypto_params in args_backends: # Make sure msg.sender's key pair is available in the circuit stmts += ext_circuit.request_private_key(crypto_params) # Verify that out parameter has correct size stmts += [RequireStatement(IdentifierExpr(cfg.zk_out_name).dot('length').binop('==', NumberLiteralExpr(ext_circuit.out_size_trans)))] # IdentifierExpr for array var holding serialized public circuit inputs in_arr_var = IdentifierExpr(cfg.zk_in_name).as_type(Array(AnnotatedTypeName.uint_all())) # Request static public keys offset = 0 key_req_stmts = [] me_key_idx: Dict[CryptoParams, int] = {} if ext_circuit.requested_global_keys: # Ensure that me public key is stored starting at in[0] keys = [key for key in ext_circuit.requested_global_keys] tmp_keys = {} for crypto_params in int_fct.used_crypto_backends: tmp_key_var = Identifier(f'_tmp_key_{crypto_params.identifier_name}') key_req_stmts.append(tmp_key_var.decl_var(AnnotatedTypeName.key_type(crypto_params))) tmp_keys[crypto_params] = tmp_key_var for (key_owner, crypto_params) in keys: tmp_key_var = tmp_keys[crypto_params] idf, assignment = ext_circuit.request_public_key(crypto_params, key_owner, ext_circuit.get_glob_key_name(key_owner, crypto_params)) assignment.lhs = IdentifierExpr(tmp_key_var.clone()) key_req_stmts.append(assignment) # Remember me-keys for later use in symmetrically encrypted keys if key_owner == MeExpr(): assert crypto_params not in me_key_idx me_key_idx[crypto_params] = offset # Manually add to circuit inputs key_len = crypto_params.key_len key_req_stmts.append(in_arr_var.slice(offset, key_len).assign(IdentifierExpr(tmp_key_var.clone()).slice(0, key_len))) offset += key_len assert offset == ext_circuit.in_size # Check encrypted parameters param_stmts = [] for p in original_params: """ * of T_e rule 8 """ if p.annotated_type.is_cipher(): cipher_payload_len = p.annotated_type.type_name.crypto_params.cipher_payload_len assign_stmt = in_arr_var.slice(offset, cipher_payload_len).assign(IdentifierExpr(p.idf.clone()).slice(0, cipher_payload_len)) ext_circuit.ensure_parameter_encryption(assign_stmt, p) # Manually add to circuit inputs param_stmts.append(assign_stmt) offset += cipher_payload_len # Populate sender field of parameters encrypted with a symmetric cipher copy_stmts = [] for p in original_params: if p.annotated_type.is_cipher(): c = p.annotated_type.type_name assert isinstance(c, CipherText) if c.crypto_params.is_symmetric_cipher(): sender_key = in_arr_var.index(me_key_idx[c.crypto_params]) idf = IdentifierExpr(p.idf.clone()).as_type(p.annotated_type.clone()) cipher_payload_len = cfg.get_crypto_params(p.annotated_type.homomorphism).cipher_payload_len lit = ArrayLiteralExpr([idf.clone().index(i) for i in range(cipher_payload_len)] + [sender_key]) copy_stmts.append(VariableDeclarationStatement(VariableDeclaration([], p.annotated_type.clone(), p.idf.clone(), 'memory'), lit)) if copy_stmts: param_stmts += [Comment(), Comment('Copy from calldata to memory and set sender field')] + copy_stmts # Declare in array new_in_array_expr = NewExpr(AnnotatedTypeName(TypeName.dyn_uint_array()), [NumberLiteralExpr(ext_circuit.in_size_trans)]) in_var_decl = in_arr_var.idf.decl_var(TypeName.dyn_uint_array(), new_in_array_expr) stmts.append(in_var_decl) stmts.append(Comment()) stmts += Comment.comment_wrap_block('Request static public keys', key_req_stmts) stmts += Comment.comment_wrap_block('Backup private arguments for verification', param_stmts) # Call internal function args = [IdentifierExpr(param.idf.clone()) for param in original_params] internal_call = FunctionCallExpr(IdentifierExpr(int_fct.idf.clone()).override(target=int_fct), args) internal_call.sec_start_offset = ext_circuit.priv_in_size if int_fct.requires_verification: ext_circuit.call_function(internal_call) args += [in_arr_var.clone(), NumberLiteralExpr(ext_circuit.in_size), IdentifierExpr(cfg.zk_out_name), NumberLiteralExpr(ext_circuit.out_size)] if int_fct.return_parameters: stmts += Comment.comment_list("Declare return variables", [VariableDeclarationStatement(deep_copy(vd)) for vd in int_fct.return_var_decls]) in_call = TupleExpr([IdentifierExpr(vd.idf.clone()) for vd in int_fct.return_var_decls]).assign(internal_call) else: in_call = ExpressionStatement(internal_call) stmts.append(Comment("Call internal function")) stmts.append(in_call) stmts.append(Comment()) # Call verifier if requires_proof and not cfg.disable_verification: verifier = IdentifierExpr(cfg.get_contract_var_name(ext_circuit.verifier_contract_type.code())) verifier_args = [IdentifierExpr(cfg.proof_param_name), IdentifierExpr(cfg.zk_in_name), IdentifierExpr(cfg.zk_out_name)] verify = ExpressionStatement(verifier.call(cfg.verification_function_name, verifier_args)) stmts.append(StatementList([Comment('Verify zk proof of execution'), verify], excluded_from_simulation=True)) # Add return statement at the end if necessary if int_fct.return_parameters: stmts.append(ReturnStatement(TupleExpr([IdentifierExpr(vd.idf.clone()) for vd in int_fct.return_var_decls]))) return Block(stmts)
def main(): # parse arguments a = parse_arguments() from zkay.config_version import Versions if a.cmd == 'version': print(Versions.ZKAY_VERSION) return if a.cmd == 'update-solc': try: import solcx solcx.install_solc_pragma( Versions.ZKAY_SOLC_VERSION_COMPATIBILITY.expression) except Exception as e: print(f'ERROR: Error while updating solc\n{e}') return from pathlib import Path import zkay.zkay_frontend as frontend from zkay import my_logging from zkay.config import cfg from zkay.utils.helpers import read_file, save_to_file from zkay.errors.exceptions import ZkayCompilerError from zkay.my_logging.log_context import log_context from zkay.utils.progress_printer import fail_print, success_print from zkay.zkay_ast.process_ast import get_processed_ast, get_parsed_ast_and_fake_code # Load configuration files try: cfg.load_configuration_from_disk(a.config_file) except Exception as e: with fail_print(): print(f"ERROR: Failed to load configuration files\n{e}") exit(42) # Support for overriding any user config setting via command line # The evaluation order for configuration loading is: # Default values in config.py -> Site config.json -> user config.json -> local config.json -> cmdline arguments # Settings defined at a later stage override setting values defined at an earlier stage override_dict = {} for name in vars(UserConfig): if name[0] != '_' and hasattr(a, name): val = getattr(a, name) if val is not None: override_dict[name] = val cfg.override_defaults(override_dict) if a.cmd in ['deploy-pki', 'deploy-crypto-libs']: import tempfile from zkay.compiler.privacy import library_contracts from zkay.transaction.runtime import Runtime with tempfile.TemporaryDirectory() as tmpdir: try: with cfg.library_compilation_environment(): if a.cmd == 'deploy-pki': for crypto_params in cfg.all_crypto_params(): pki_contract_code = library_contracts.get_pki_contract( crypto_params) pki_contract_name = cfg.get_pki_contract_name( crypto_params) file = save_to_file(tmpdir, f'{pki_contract_name}.sol', pki_contract_code) addr = Runtime.blockchain( ).deploy_solidity_contract(file, pki_contract_name, a.account) print( f'Deployed pki contract for crypto backend "{crypto_params.crypto_name}" at: {addr}' ) else: if not cfg.external_crypto_lib_names: print( 'Current proving scheme does not require library deployment' ) else: file = save_to_file( tmpdir, 'verify_libs.sol', library_contracts.get_verify_libs_code()) for lib in cfg.external_crypto_lib_names: addr = Runtime.blockchain( ).deploy_solidity_contract( file, lib, a.account) print( f'Deployed crypto library {lib} at: {addr}' ) except Exception as e: with fail_print(): print(f"ERROR: Deployment failed\n{e}") else: # Solc version override if hasattr(a, 'solc_version') and a.solc_version is not None: try: cfg.override_solc(a.solc_version) except ValueError as e: with fail_print(): print(f'Error: {e}') exit(10) print(f'Using solc version {Versions.SOLC_VERSION}') input_path = Path(a.input) if not input_path.exists(): with fail_print(): print(f'Error: input file \'{input_path}\' does not exist') exit(1) if a.cmd == 'check': # only type-check print(f'Type checking file {input_path.name}:') code = read_file(str(input_path)) try: get_processed_ast(code) except ZkayCompilerError as e: with fail_print(): print(f'{e}') exit(3) elif a.cmd == 'solify': was_unit_test = cfg.is_unit_test cfg._is_unit_test = True # Suppress other output try: _, fake_code = get_parsed_ast_and_fake_code( read_file(str(input_path))) print(fake_code) except ZkayCompilerError as e: with fail_print(): print(f'{e}') exit(3) finally: cfg._is_unit_test = was_unit_test exit(0) elif a.cmd == 'compile': # create output directory output_dir = Path(a.output).absolute() if not output_dir.exists(): os.makedirs(output_dir) elif not output_dir.is_dir(): with fail_print(): print(f'Error: \'{output_dir}\' is not a directory') exit(2) # Enable logging if a.log: log_file = my_logging.get_log_file(filename='compile', include_timestamp=False, label=None) my_logging.prepare_logger(log_file) # only type-check print(f'Compiling file {input_path.name}:') # compile with log_context(os.path.basename(a.input)): try: frontend.compile_zkay_file(str(input_path), str(output_dir)) except ZkayCompilerError as e: with fail_print(): print(f'{e}') exit(3) elif a.cmd == 'import': # create output directory output_dir = Path(a.output).absolute() if output_dir.exists(): with fail_print(): print(f'Error: \'{output_dir}\' already exists') exit(2) try: frontend.extract_zkay_package(str(input_path), str(output_dir)) except ZkayCompilerError as e: with fail_print(): print( f"ERROR while compiling unpacked zkay contract.\n{e}") exit(3) except Exception as e: with fail_print(): print(f"ERROR while unpacking zkay contract\n{e}") exit(5) elif a.cmd == 'export': output_filename = Path(a.output).absolute() os.makedirs(output_filename.parent, exist_ok=True) try: frontend.package_zkay_contract(str(input_path), str(output_filename)) except Exception as e: with fail_print(): print(f"ERROR while exporting zkay contract\n{e}") exit(4) elif a.cmd in ['run', 'deploy', 'connect']: from enum import IntEnum from zkay.transaction.offchain import ContractSimulator def echo_only_simple_expressions(e): if isinstance(e, (bool, int, str, list, tuple, IntEnum)): print(e) # Enable logging if a.log: log_file = my_logging.get_log_file( filename=f'transactions_{input_path.name}', include_timestamp=True, label=None) my_logging.prepare_logger(log_file) contract_dir = str(input_path.absolute()) frontend.use_configuration_from_manifest(contract_dir) if a.account is not None: me = a.account else: me = ContractSimulator.default_address() if me is not None: me = me.val import code import sys if a.cmd == 'run': # Dynamically load module and replace globals with module globals contract_mod = frontend.load_transaction_interface_from_directory( contract_dir) contract_mod.me = me sys.displayhook = echo_only_simple_expressions code.interact(local=contract_mod.__dict__) else: cmod = frontend.load_transaction_interface_from_directory( contract_dir) c = frontend.load_contract_transaction_interface_from_module( cmod) if a.cmd == 'deploy': from ast import literal_eval cargs = a.constructor_args args = [] for arg in cargs: try: val = literal_eval(arg) except Exception: val = arg args.append(val) try: c.deploy(*args, user=me, project_dir=contract_dir) except (ValueError, TypeError) as e: with fail_print(): print( f'ERROR invalid arguments.\n{e}\n\nExpected contructor signature: ', end='') if hasattr(c, 'constructor'): from inspect import signature sig = str(signature(c.constructor)) sig = sig[5:] if not sig[5:].startswith( ",") else sig[7:] # without self print(f'({sig}') else: print('()') exit(11) except Exception as e: with fail_print(): print(f'ERROR: failed to deploy contract\n{e}') exit(12) elif a.cmd == 'connect': try: c_inst = c.connect(address=a.address, user=me, project_dir=contract_dir) except Exception as e: with fail_print(): print(f'ERROR: failed to connect to contract\n{e}') exit(13) # Open interactive shell in context of contract object import inspect contract_scope = {name: getattr(c_inst, name) for name in dir(c_inst) if inspect.isclass(getattr(c_inst, name)) \ or (name != 'constructor' and hasattr(getattr(c_inst, name), '_can_be_external') and getattr(c_inst, name)._can_be_external) or name in ['state', 'api']} contract_scope['me'] = me contract_scope['help'] = lambda o=None: help( o ) if o is not None else ContractSimulator.reduced_help(c) sys.displayhook = echo_only_simple_expressions code.interact(local=contract_scope) else: raise ValueError(f'unexpected command {a.cmd}') exit(0) else: raise NotImplementedError(a.cmd) with success_print(): print("Finished successfully")