def git_clone(repo_url: str, repo_to_path: str = ''): if repo_to_path == '': repo_to_path = os.getcwd() if os.listdir(repo_to_path): raise PunicaException(PunicaError.file_exist_error) print('Downloading...') try: git.Repo.clone_from(url=repo_url, to_path=repo_to_path, depth=1) except git.GitCommandError as e: network_error = 'Could not read from remote repository' file_exist_error = 'already exists and is not an empty directory' if network_error in str(e.args[2]): raise PunicaException(PunicaError.network_error) elif file_exist_error in str(e.args[2]): raise PunicaException(PunicaError.file_exist_error) else: raise PunicaException(PunicaError.other_error(e.args[2]))
def handle_param_str(list_params2: list, p: str): list_p = p.split(':') if len(list_p) != 2: raise PunicaException(PunicaError.parameter_type_error) if list_p[0] == 'ByteArray': if len(list_p[1]) == 34: list_params2.append(Address.b58decode(list_p[1]).to_bytes()) else: list_params2.append(list_p[1].encode()) elif list_p[0] == 'String': list_params2.append(list_p[1]) elif list_p[0] == 'Address': list_params2.append(Address.b58decode(list_p[1]).to_bytes()) elif list_p[0] == 'Hex': list_params2.append(bytearray.fromhex(list_p[1])) else: raise PunicaException(PunicaError.parameter_type_error)
def get_password(): while True: acct_password = getpass.getpass('Please input password: '******'Please repeat password: '******'password not match'))
def generate_repo_url(box_name: str) -> str: if re.match(r'^([a-zA-Z0-9-])+$', box_name): repo_url = [ 'https://github.com/punica-box/', box_name, '-box', '.git' ] elif re.match(r'^([a-zA-Z0-9-])+/([a-zA-Z0-9-])+$', box_name) is None: repo_url = ['https://github.com/', box_name, '.git'] else: raise PunicaException(PunicaError.invalid_box_name) return ''.join(repo_url)
def read_wallet(wallet_dir_path: str, wallet_file_name: str = '') -> WalletManager: if not os.path.isdir(wallet_dir_path): raise PunicaException(PunicaError.directory_error) wallet_manager = WalletManager() if wallet_file_name == '': dir_list = os.listdir(wallet_dir_path) if len(dir_list) == 1: wallet_file_name = dir_list[0] else: raise PunicaException(PunicaError.wallet_file_unspecified) wallet_path = os.path.join(wallet_dir_path, wallet_file_name) if not os.path.isfile(wallet_path): raise PunicaException(PunicaError.wallet_file_not_found) try: wallet_manager.open_wallet(wallet_path) except SDKException: raise PunicaException(PunicaError.wallet_file_error) return wallet_manager
def generate_abi_info(dict_abi: dict) -> AbiInfo: try: contract_address = dict_abi['hash'] functions = dict_abi['functions'] except KeyError: raise PunicaException(PunicaError.abi_file_error) entry_point = dict_abi.get('entrypoint', '') events = dict_abi.get('events', list()) abi_info = AbiInfo(contract_address, entry_point, functions, events) return abi_info
def query_unbound_ong(project_dir_path, address, network): if project_dir_path == '': project_dir_path = os.getcwd() if not os.path.isdir(project_dir_path): raise PunicaException(PunicaError.dir_path_error) rpc_address = handle_network_config(project_dir_path, network) sdk = OntologySdk() sdk.rpc.set_address(rpc_address) balance = sdk.native_vm().asset().query_unbound_ong(address) print(address + ' UnboundOng: ', balance)
def generate_contract_address(avm_dir_path: str = '', avm_file_name: str = '') -> str: if avm_dir_path == '': avm_dir_path = os.path.join(os.getcwd(), 'build', 'contracts') if not os.path.isdir(avm_dir_path): raise PunicaException(PunicaError.dir_path_error) hex_avm_code = read_avm(avm_dir_path, avm_file_name)[0] hex_contract_address = Address.address_from_vm_code( hex_avm_code).to_reverse_hex_str() return hex_contract_address
def get_rpc_address(self, network: str = ''): try: if len(network) == 0: network = self.pj_config['defaultNet'] networks = self.pj_config['networks'] host = networks[network]['host'] port = networks[network]['port'] return f'{host}:{port}' except KeyError: raise PunicaException(PunicaError.invalid_network_config)
def get_all_contract(self) -> List[str]: try: files_in_dir = listdir(self.contract_dir) except FileNotFoundError: raise PunicaException(PunicaError.pj_dir_path_error) contract_list = list() for file in files_in_dir: if not file.endswith('.py'): continue contract_list.append(file) return contract_list
def get_all_avm_file(self): try: files_in_dir = listdir(self.contract_build_dir) except FileNotFoundError: raise PunicaException(PunicaError.pj_dir_path_error) avm_file_list = list() for file in files_in_dir: if not file.endswith('.avm'): continue avm_file_list.append(file) return avm_file_list
def compile_contract_remote(contract_path: str): with open(contract_path, "r") as f: contract = f.read() dict_payload = dict() if contract_path.endswith('.py'): dict_payload['type'] = 'Python' dict_payload['code'] = contract url = PYTHON_COMPILE_URL else: dict_payload['type'] = 'CSharp' dict_payload['code'] = contract url = CSHARP_COMPILE_URL header = {'Content-type': 'application/json'} timeout = 10 path = os.path.dirname(contract_path) file_name = os.path.basename(contract_path).split(".") requests.packages.urllib3.disable_warnings(InsecureRequestWarning) session = requests.session() response = session.post(url, json=dict_payload, headers=header, timeout=timeout, verify=False) if response.status_code != 200: raise PunicaException( PunicaError.get_error(5000, 'Remote compile failed.')) result = json.loads(response.content.decode('utf-8')) if result["errcode"] == 0: avm_save_path = os.path.join(path, 'build', file_name[0] + ".avm") if not os.path.exists(os.path.join(path, 'build')): os.makedirs(os.path.join(path, 'build')) with open(avm_save_path, "w", encoding='utf-8') as f: avm = result["avm"].lstrip('b\'') temp = avm.rstrip('\'') f.write(temp) abi_save_path = os.path.join(path, 'build', file_name[0] + "_abi.json") with open(abi_save_path, "w", encoding='utf-8') as f2: r = re.sub('\\\\n', '', str(result["abi"])) abi = str(r.lstrip('b\'')) temp = abi.rstrip('\'') f2.write(temp.replace(' ', '')) print("Compiled, Thank you") invoke_config_path = os.path.join(path, DEFAULT_CONFIG) if os.path.exists(invoke_config_path): PunicaCompiler.update_invoke_config( abi_save_path, invoke_config_path) else: PunicaCompiler.generate_invoke_config( abi_save_path, invoke_config_path) else: print("compile failed") print(result)
def generate_signed_deploy_transaction(hex_avm_code: str, project_path: str = '', wallet_file_name: str = '', config: str = ''): wallet_file, deploy_information, password_information = handle_deploy_config( project_path, config) if wallet_file_name != '': wallet_manager = read_wallet(project_path, wallet_file_name) else: wallet_manager = read_wallet(project_path, wallet_file) need_storage = deploy_information.get('needStorage', True) name = deploy_information.get('name', os.path.split(project_path)[1]) version = deploy_information.get('version', '0.0.1') author = deploy_information.get('author', '') email = deploy_information.get('email', '') desc = deploy_information.get('desc', '') b58_payer_address = deploy_information.get('payer', '') if b58_payer_address == '': b58_payer_address = wallet_manager.get_default_account( ).get_address() if b58_payer_address == '': raise PunicaException( PunicaError.other_error('payer address should not be None')) gas_limit = deploy_information.get('gasLimit', 21000000) gas_price = deploy_information.get('gasPrice', 500) ontology = OntologySdk() tx = ontology.neo_vm().make_deploy_transaction(hex_avm_code, need_storage, name, version, author, email, desc, b58_payer_address, gas_limit, gas_price) password = password_information.get(b58_payer_address, '') if password == '': password = getpass.getpass( '\tPlease input payer account password: '******' not found')) ontology.sign_transaction(tx, payer_acct) return tx
def read_avm(avm_dir_path: str, avm_file_name: str = '') -> (str, str): if not os.path.isdir(avm_dir_path): raise PunicaException(PunicaError.directory_error) if avm_file_name != '': avm_file_path = os.path.join(avm_dir_path, avm_file_name) if not os.path.exists(avm_file_path): raise PunicaException(PunicaError.other_error(avm_file_path + ' not exist')) with open(avm_file_path, 'r') as f: hex_avm = f.read() else: dir_list = os.listdir(avm_dir_path) hex_avm = '' for file in dir_list: split_path = os.path.splitext(file) if (split_path[0] == avm_file_name or avm_file_name == '') and split_path[1] == '.avm': avm_file_name = ''.join(split_path) avm_path = os.path.join(avm_dir_path, file) with open(avm_path, 'r') as f: hex_avm = f.read() break return hex_avm, avm_file_name
def get_function(params: dict, function_name: str, abi_info: AbiInfo): if function_name == '': raise PunicaException(PunicaError.other_error('function_name should not be nil')) params = Invoke.params_normalize(params) abi_function = abi_info.get_function(function_name) if len(abi_function.parameters) == 0: pass elif len(abi_function.parameters) == 1: abi_function.set_params_value((params,)) elif len(abi_function.parameters) == len(params): abi_function.set_params_value(tuple(params)) return abi_function
def list_boxes(self): response = requests.get(self.__box_repos_url).content.decode() repos = json.loads(response) if isinstance(repos, dict): message = repos.get('message', '') if 'API rate limit exceeded' in message: raise PunicaException(PunicaError.other_error(message)) echo('\nThe easiest way to get started:\n') for index, repo in enumerate(repos): name = repo.get('name', '') echo(f' {index}. {name}') echo('')
def handle_invoke_config(project_dir_path: str, config: str): try: if config != '': config_path = os.path.join(project_dir_path, config) if not os.path.exists(config_path): if os.path.dirname(config) != '': raise PunicaException(PunicaError.other_error(config + ' not found')) else: config_path = os.path.join(project_dir_path, 'contracts', config) if not os.path.exists(config_path): raise PunicaException(PunicaError.other_error(config_path + ' not exist')) else: config_path = os.path.join(project_dir_path, 'contracts', DEFAULT_CONFIG) if not os.path.exists(config_path): raise PunicaException(PunicaError.other_error(config_path + ' not found')) with open(config_path, 'r') as f: config = json.load(f) except FileNotFoundError: raise PunicaException(PunicaError.config_file_not_found) try: wallet_file = config['defaultWallet'] invoke_config = config['invokeConfig'] password_config = config['password'] except KeyError: raise PunicaException(PunicaError.other_error('the config file lack invokeConfig or password')) if not isinstance(invoke_config, dict): raise PunicaException(PunicaError.config_file_error) return wallet_file, invoke_config, password_config
def get_acct_by_address(self, address: str) -> Account: password = self.contract_config.get('password', dict()).get(address, '') if len(password) == 0: password = getpass(prompt=f'Unlock {address}: ') while True: try: return self.ontology.wallet_manager.get_account_by_b58_address(address, password) except SDKException as e: if 100017 == e.args[0]: password = getpass(prompt=f'Password error, try again: ') continue else: raise PunicaException(PunicaError.config_file_error)
def list_boxes(): repos_url = 'https://api.github.com/users/punica-box/repos' response = requests.get(repos_url).content.decode() repos = json.loads(response) if isinstance(repos, dict): message = repos.get('message', '') if 'API rate limit exceeded' in message: raise PunicaException(PunicaError.other_error(message)) name_list = [] for repo in repos: name = repo.get('name', '') name_list.append(name) return name_list
def balance_of(project_dir_path: str, asset: str, address: str, network: str): if asset == '' or asset.lower() != 'ont' and asset.lower() != 'ong': print(asset, ' asset should be ont or ong') return if project_dir_path == '': project_dir_path = os.getcwd() if not os.path.isdir(project_dir_path): raise PunicaException(PunicaError.dir_path_error) rpc_address = handle_network_config(project_dir_path, network) sdk = OntologySdk() sdk.rpc.set_address(rpc_address) balance = sdk.native_vm().asset().query_balance(asset, address) print(address + ' Balance: ', balance)
def __init__(self, project_dir: str = '', network: str = '', wallet_path: str = '', contract_config_path: str = ''): super().__init__(project_dir) contract_config_file_path = path.join(self.project_dir, 'contracts', 'config.json') old_contract_config_file_path = path.join(self.project_dir, 'contracts', 'default-config.json') if len(contract_config_path) != 0: self._contract_config_file_path = contract_config_path elif path.exists(contract_config_file_path): self._contract_config_file_path = contract_config_file_path elif path.exists(old_contract_config_file_path): self._contract_config_file_path = old_contract_config_file_path else: raise PunicaException(PunicaError.config_file_not_found) try: with open(self._contract_config_file_path, 'r') as f: self._contract_config = json.load(f) except FileNotFoundError: raise PunicaException(PunicaError.config_file_not_found) self._avm_dir = path.join(project_dir, 'build', 'contracts') self._wallet_dir = path.join(project_dir, 'wallet') if len(network) == 0: network = self.default_network self._network = network self._ontology = Ontology() self._ontology.rpc.set_address(self.get_rpc_address(network)) if len(wallet_path) == 0: wallet_path = path.join( self.wallet_dir, self._contract_config.get('defaultWallet', 'wallet.json')) self._wallet_path = wallet_path self._ontology.wallet_manager.open_wallet(self._wallet_path)
def params_normalize(dict_params: dict) -> list: list_params = list() isfirst = False if len(dict_params) == 0: return list_params for param in dict_params.values(): if isinstance(param, list): if len(param) == 0: continue temp_params_list = list() for i in range(len(param)): if isinstance(param[i], dict): list_params2 = list() for p in param[i].values(): if isinstance(p, str): Invoke.handle_param_str(list_params2, p) elif isinstance(p, int): list_params2.append(p) temp_params_list.append(list_params2) elif isinstance(param[i], int): isfirst = True temp_params_list.append(param[i]) elif isinstance(param[i], str): isfirst = True Invoke.handle_param_str(temp_params_list, param[i]) else: raise PunicaException(PunicaError.parameter_type_error) if len(temp_params_list) >= 2 and isfirst: list_params.append(temp_params_list) else: list_params = temp_params_list elif isinstance(param, str): if param == '': raise PunicaException(PunicaError.parameter_type_error) Invoke.handle_param_str(list_params, param) elif isinstance(param, int): list_params.append(param) return list_params
def handle_network_config(config_dir_path: str, network: str = '', is_print: bool = True) -> str: try: config_file_path = os.path.join(config_dir_path, 'punica-config.json') with open(config_file_path, 'r') as f: config = json.load(f) except FileNotFoundError: raise PunicaException(PunicaError.config_file_not_found) try: network_dict = config['networks'] except KeyError: raise PunicaException(PunicaError.config_file_error) if network == '': try: network = list(network_dict.keys())[0] except IndexError: raise PunicaException(PunicaError.config_file_error) try: rpc_address = ''.join([network_dict[network]['host'], ':', str(network_dict[network]['port'])]) except KeyError: raise PunicaException(PunicaError.config_file_error) if is_print: print('Using network \'{}\'.\n'.format(network)) return rpc_address
def check_deploy_state(tx_hash, project_path: str = '', network: str = ''): if project_path == '': project_path = os.getcwd() if not os.path.isdir(project_path): raise PunicaException(PunicaError.dir_path_error) rpc_address = handle_network_config(project_path, network, False) ontology = OntologySdk() ontology.rpc.set_address(rpc_address) time.sleep(6) tx = ontology.rpc.get_raw_transaction(tx_hash) if tx == 'unknown transaction': return False else: return True
def deploy_smart_contract(project_dir: str = '', network: str = '', avm_file_name: str = '', wallet_file_name: str = '', config: str = ''): if project_dir == '': project_dir = os.getcwd() if avm_file_name != '': avm_path = os.path.join(project_dir, avm_file_name) if os.path.exists(avm_path): avm_dir_path = os.path.dirname(avm_path) avm_file_name = os.path.basename(avm_path) else: avm_dir_path = os.path.join(project_dir, 'contracts', 'build') else: avm_dir_path = os.path.join(project_dir, 'contracts', 'build') if not os.path.exists(avm_dir_path): print('there is not the avm file, please compile first') return rpc_address = handle_network_config(project_dir, network) try: hex_avm_code, avm_file_name = read_avm(avm_dir_path, avm_file_name) except PunicaException as e: print(e.args) return if hex_avm_code == '': raise PunicaException(PunicaError.avm_file_empty) hex_contract_address = Deploy.generate_contract_address( avm_dir_path, avm_file_name) ontology = OntologySdk() ontology.rpc.set_address(rpc_address) contract = ontology.rpc.get_smart_contract(hex_contract_address) if contract == 'unknow contract' or contract == 'unknow contracts': try: tx = Deploy.generate_signed_deploy_transaction( hex_avm_code, project_dir, wallet_file_name, config) except PunicaException as e: print('\tDeploy failed...') print('\t', e.args) return print('Running deployment: {}'.format(avm_file_name)) print('\tDeploying...') ontology.rpc.set_address(rpc_address) tx_hash = ontology.rpc.send_raw_transaction(tx) return tx_hash else: print('\tDeploy failed...') print('\tContract has been deployed...') print('\tContract address is {}'.format(hex_contract_address))
def list_all_functions(project_dir: str, config_name: str): if config_name == '': config_name = DEFAULT_CONFIG try: wallet_file, invoke_config, password_config = handle_invoke_config( project_dir, config_name) except Exception as e: print(e.args) return try: abi_file_name = invoke_config['abi'] except KeyError: raise PunicaException(PunicaError.config_file_error) abi_dir_path = os.path.join(project_dir, 'contracts', 'build') try: dict_abi = read_abi(abi_dir_path, abi_file_name) except PunicaException as e: print(e.args) return try: func_in_abi_list = dict_abi['functions'] except KeyError: raise PunicaException(PunicaError.other_error('abi file is wrong')) func_name_in_abi_list = list() for func_in_abi_dict in func_in_abi_list: try: func_name = func_in_abi_dict['name'] except KeyError: raise PunicaException( PunicaError.other_error('abi file is wrong')) func_name_in_abi_list.append(func_name) print("All Functions:") invoke_function_list = invoke_config['functions'] for function_information in invoke_function_list: if function_information['operation'] in func_name_in_abi_list: print('\t', function_information['operation'])
def parse_param(param): if isinstance(param, bool): return param elif isinstance(param, int): return param elif isinstance(param, str): return Invoke.handle_param_str2(param) elif isinstance(param, list): list_temp = list() for i in param: list_temp.append(Invoke.parse_param(i)) return list_temp elif isinstance(param, dict): dict_temp = dict() for k, v in param.items(): dict_temp[k] = Invoke.parse_param(v) return dict_temp else: raise PunicaException(PunicaError.other_error('not support data type'))
def params_normalize2(list_param: list): list_params = list() for param in list_param: if isinstance(param, dict): item = param.get('value', '') if isinstance(item, bool): list_params.append(item) elif isinstance(item, int): list_params.append(item) elif isinstance(item, str): list_params.append(Invoke.handle_param_str2(item)) elif isinstance(item, list): list_temp = list() for i in item: list_temp.append(Invoke.parse_param(i)) list_params.append(list_temp) elif isinstance(item, dict): dict_temp = dict() for k, v in item.items(): dict_temp[k] = Invoke.parse_param(v) list_params.append(dict_temp) else: raise PunicaException(PunicaError.other_error('not support data type')) return list_params
def download_repo(repo_url: str, repo_to_path: str = ''): if repo_to_path == '': repo_to_path = getcwd() spinner = Halo(spinner='dots') def calcu_progress_scale(cur_count: int, max_count: int): return round(cur_count / max_count * 100, 2) def show_spinner(stage_info: str, cur_count: int, max_count: int, message: str = ''): if spinner.spinner_id is None: spinner.start() scale = calcu_progress_scale(cur_count, max_count) if len(message) == 0: spinner.text = f'{stage_info}: {scale}% ({cur_count}/{max_count})' else: spinner.text = f'{stage_info}: {scale}%, {message}' if scale == 100: spinner.succeed() return def update(self, op_code: RemoteProgress, cur_count: int, max_count: int = None, message: str = ''): if op_code == RemoteProgress.COUNTING: show_spinner('Counting objects', cur_count, max_count) return if op_code == RemoteProgress.COMPRESSING: show_spinner('Compressing objects', cur_count, max_count) return if op_code == RemoteProgress.RECEIVING: show_spinner('Receiving objects', cur_count, max_count, message) return if op_code == RemoteProgress.RESOLVING: show_spinner('Resolving deltas', cur_count, max_count) return if op_code == RemoteProgress.WRITING: show_spinner('Writing objects', cur_count, max_count) return if op_code == RemoteProgress.FINDING_SOURCES: show_spinner('Finding sources', cur_count, max_count) return if op_code == RemoteProgress.CHECKING_OUT: show_spinner('Checking out files', cur_count, max_count) return RemoteProgress.update = update try: Repo.clone_from(url=repo_url, to_path=repo_to_path, depth=1, progress=RemoteProgress()) if spinner.spinner_id is not None and len(spinner.text) != 0: spinner.fail() return False return True except GitCommandError as e: if spinner.spinner_id is not None and len(spinner.text) != 0: spinner.fail() if e.status == 126: echo('Please check your network.') elif e.status == 128: echo('Please check your Git tool.') else: raise PunicaException(PunicaError.other_error(e.args[2])) return False
def get_deploy_config(self) -> dict: try: return self.contract_config['deploy'] except KeyError: echo(crayons.red('Please provide deployment config.\n', bold=True)) raise PunicaException(PunicaError.config_file_error)