def __get_delegators_and_delgators_balance(self, cycle, verbose=False): hash_snapshot_block = self.__get_snapshot_block_hash(cycle) if hash_snapshot_block == "": return 0, [] request = COMM_DELEGATES.format(self.node_url, hash_snapshot_block, self.baking_address) response = self.wllt_clnt_mngr.send_request(request) delegate_staking_balance = 0 delegators = {} try: response = parse_json_response(response) delegate_staking_balance = int(response["staking_balance"]) delegators_addresses = response["delegated_contracts"] for delegator in delegators_addresses: request = COMM_DELEGATE_BALANCE.format(self.node_url, hash_snapshot_block, delegator) response = self.wllt_clnt_mngr.send_request(request) response = parse_json_response(response) delegators[delegator] = int(response["balance"]) except: logger.warn('No delegators or unexpected error') return delegate_staking_balance, delegators
def __get_delegators_and_delgators_balance(self, cycle, verbose=False): hash_snapshot_block = self.__get_snapshot_block_hash(cycle) if hash_snapshot_block == "": return 0, [] request = COMM_DELEGATES.format(self.node_url, hash_snapshot_block, self.baking_address) _, response = self.wllt_clnt_mngr.send_request(request) delegate_staking_balance = 0 delegators = {} try: response = parse_json_response(response) delegate_staking_balance = int(response["staking_balance"]) delegators_addresses = response["delegated_contracts"] for idx, delegator in enumerate(delegators_addresses): request = COMM_DELEGATE_BALANCE.format(self.node_url, hash_snapshot_block, delegator) _, response = self.wllt_clnt_mngr.send_request(request) response = parse_json_response(response) delegators[delegator] = int(response["balance"]) logger.debug( "Delegator info ({}/{}) fetched: address {}, balance {}". format(idx, len(delegators_addresses), delegator, delegators[delegator])) except: logger.warn('No delegators or unexpected error', exc_info=True) return delegate_staking_balance, delegators
def get_revelation(self, pkh, verbose=False): _, response = self.wllt_clnt_mngr.send_request( COMM_REVELATION.format(self.node_url, pkh)) manager_key = parse_json_response(response, verbose=verbose) bool_revelation = "key" in manager_key.keys() and len( manager_key["key"]) > 0 return bool_revelation
def get_network_config_from_local_node(config_client_manager, node_addr): request_constants = CONSTANTS_COMM.format(node_addr) _, response_constants = config_client_manager.send_request( request_constants) constants = parse_json_response(response_constants) network_config_map = parse_constants(constants) return network_config_map
def __get_snapshot_block_hash(self, cycle, verbose=False): current_level, head_hash = self.__get_current_level(verbose) level_for_snapshot_request = ( cycle - self.preserved_cycles) * self.blocks_per_cycle + 1 if current_level - level_for_snapshot_request >= 0: request = COMM_SNAPSHOT.format( self.node_url, head_hash, current_level - level_for_snapshot_request, cycle) response = self.wllt_clnt_mngr.send_request(request) snapshots = parse_json_response(response) if len(snapshots) == 1: chosen_snapshot = snapshots[0] else: logger.info("Too few or too many possible snapshots found!") level_snapshot_block = ( cycle - self.preserved_cycles - 2) * self.blocks_per_cycle + ( chosen_snapshot + 1) * self.blocks_per_roll_snapshot request = COMM_BLOCK.format( self.node_url, head_hash, current_level - level_snapshot_block) + " | jq -r .hash" hash_snapshot_block = self.wllt_clnt_mngr.send_request( request).rstrip() return hash_snapshot_block else: logger.info("Cycle too far in the future") return ""
def get_current_cycle_position(self, level_hash, verbose=False): _, response = self.wllt_clnt_mngr.send_request( COMM_HEAD.format(self.node_url)) head = parse_json_response(response) cycle_position = int(head["metadata"]["level"]["cycle_position"]) return cycle_position
def get_revelation(self, pkh, verbose=False): _, response = self.wllt_clnt_mngr.send_request( COMM_REVELATION.format(self.node_url, pkh)) manager_key = parse_json_response(response, verbose=verbose) logger.debug("Manager key is '{}'".format(manager_key)) bool_revelation = manager_key and manager_key != 'null' return bool_revelation
def __get_current_level(self, verbose=False): response = self.wllt_clnt_mngr.send_request( COMM_HEAD.format(self.node_url)) head = parse_json_response(response) current_level = int(head["metadata"]["level"]["level"]) head_hash = head["hash"] return current_level, head_hash
def get_rewards_for_cycle_map(self, cycle, verbose=False): reward_data = {} reward_data["delegate_staking_balance"], reward_data[ "delegators"] = self.__get_delegators_and_delgators_balance( cycle, verbose) reward_data["delegators_nb"] = len(reward_data["delegators"]) current_level, head_hash, current_cycle = self.__get_current_level( verbose) logger.debug("Current level {}, head hash {}".format( current_level, head_hash)) # Get last block in cycle where rewards are unfrozen level_for_relevant_request = ( cycle + self.preserved_cycles + 1 - self.cycle_offset) * self.blocks_per_cycle logger.debug( "Cycle {}, preserved cycles {}, blocks per cycle {}, level of interest {}" .format(cycle, self.preserved_cycles, self.blocks_per_cycle, level_for_relevant_request)) if current_level - level_for_relevant_request >= 0: request_metadata = COMM_BLOCK.format( self.node_url, head_hash, current_level - level_for_relevant_request) + '/metadata/' _, response_metadata = self.wllt_clnt_mngr.send_request( request_metadata) metadata = parse_json_response(response_metadata) balance_updates = metadata["balance_updates"] unfrozen_rewards = unfrozen_fees = 0 for i in range(len(balance_updates)): balance_update = balance_updates[i] if balance_update["kind"] == "freezer": if balance_update["delegate"] == self.baking_address: if balance_update["category"] == "rewards": unfrozen_rewards = -int(balance_update["change"]) elif balance_update["category"] == "fees": unfrozen_fees = -int(balance_update["change"]) reward_data["total_rewards"] = unfrozen_rewards + unfrozen_fees else: logger.warn( "Please wait until the rewards and fees for cycle {} are unfrozen" .format(cycle)) reward_data["total_rewards"] = 0 reward_model = RewardProviderModel( reward_data["delegate_staking_balance"], reward_data["total_rewards"], reward_data["delegators"]) if self.validate: self.__validate_reward_data(reward_model, cycle) return reward_model
def get_next_cycle_first_level(self, current_cycle, verbose=False): _, response = self.wllt_clnt_mngr.send_request( COMM_HEAD.format(self.node_url)) head = parse_json_response(response) cycle_position = int(head["metadata"]["level"]["cycle_position"]) current_level = int(head["metadata"]["level"]["level"]) remaining_blocks = self.nw['BLOCKS_PER_CYCLE'] - cycle_position return current_level + remaining_blocks
def __get_payment_address_balance(self): payment_address_balance = None get_current_balance_request = COMM_DELEGATE_BALANCE.format( "head", self.source) result, command_response = self.wllt_clnt_mngr.send_request( get_current_balance_request) payment_address_balance = parse_json_response(command_response) return int(payment_address_balance)
def do_rpc_request(self, request): request = " rpc get "+request if self.verbose: logger.debug("[do_rpc_request] running command {}".format(request)) try: _, resp = self.wllt_clnt_mngr.send_request(request) response = parse_json_response(resp) except Exception as e: raise Exception("RPC request failed. Make sure you are using an Archive Node!") from e if self.verbose: logger.debug("[do_rpc_request] Response {}".format(response)) return response
def __get_snapshot_block_hash(self, cycle, verbose=False): current_level, head_hash, current_cycle = self.__get_current_level( verbose) level_for_snapshot_request = (cycle - self.preserved_cycles - self. cycle_offset) * self.blocks_per_cycle + 1 logger.debug("Current level {}, head hash {}".format( current_level, head_hash)) logger.debug( "Cycle {}, preserved cycles {}, blocks per cycle {}, level of interest {}" .format(cycle, self.preserved_cycles, self.blocks_per_cycle, level_for_snapshot_request)) block_level = (cycle - self.cycle_offset) * self.blocks_per_cycle + 1 if current_level - level_for_snapshot_request >= 0: request = COMM_SNAPSHOT.format(self.node_url, block_level, cycle) _, response = self.wllt_clnt_mngr.send_request(request) snapshots = parse_json_response(response) if len(snapshots) == 1: chosen_snapshot = snapshots[0] else: logger.error("Too few or too many possible snapshots found!") return "" level_snapshot_block = ( cycle - self.preserved_cycles - 2 - self.cycle_offset) * self.blocks_per_cycle + ( chosen_snapshot + 1) * self.blocks_per_roll_snapshot request = COMM_BLOCK.format(self.node_url, head_hash, current_level - level_snapshot_block) _, comm_block_response = self.wllt_clnt_mngr.send_request(request) comm_block_response = comm_block_response.rstrip() comm_block_response_json = extract_json_part(comm_block_response, verbose=True) cmd_mngr = CommandManager(verbose=verbose) _, hash_snapshot_block = cmd_mngr.execute( "echo '{}' | jq -r .hash".format(comm_block_response_json)) logger.debug( "Hash of snapshot block is {}".format(hash_snapshot_block)) return hash_snapshot_block else: logger.info("Cycle too far in the future") return ""
def attempt_single_batch(self, payment_records, op_counter, verbose=None, dry_run=None): if not op_counter.get(): _, response = self.wllt_clnt_mngr.send_request(self.comm_counter) counter = parse_json_response(response) counter = int(counter) op_counter.set(counter) _, response = self.wllt_clnt_mngr.send_request(self.comm_head, verbose_override=False) head = parse_json_response(response) branch = head["hash"] chain_id = head["chain_id"] protocol = head["metadata"]["protocol"] logger.debug("head: branch {} counter {} protocol {}".format( branch, op_counter.get(), protocol)) content_list = [] for payment_item in payment_records: pymnt_amnt = payment_item.amount # expects in micro tezos if self.delegator_pays_xfer_fee: pymnt_amnt = max(pymnt_amnt - self.default_fee, 0) # ensure not less than 0 # if, somehow, pymnt_amnt becomes 0, don't pay if pymnt_amnt == 0: continue op_counter.inc() content = CONTENT.replace("%SOURCE%", self.source).replace("%DESTINATION%", payment_item.paymentaddress) \ .replace("%AMOUNT%", str(pymnt_amnt)).replace("%COUNTER%", str(op_counter.get())) \ .replace("%fee%", str(self.default_fee)).replace("%gas_limit%", self.gas_limit).replace("%storage_limit%", self.storage_limit) content_list.append(content) if verbose: logger.info("Payment content: {}".format(content)) contents_string = ",".join(content_list) # run the operations logger.debug("Running {} operations".format(len(content_list))) runops_json = RUNOPS_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) runops_json = JSON_WRAP.replace("%JSON%", runops_json).replace( "%chain_id%", chain_id) runops_command_str = self.comm_runops.replace("%JSON%", runops_json) # if verbose: print("--> runops_command_str is |{}|".format(runops_command_str)) result, runops_command_response = self.wllt_clnt_mngr.send_request( runops_command_str) if not result: logger.error("Error in run_operation") logger.debug("Error in run_operation, request ->{}<-".format( runops_command_str)) logger.debug("---") logger.debug("Error in run_operation, response ->{}<-".format( runops_command_response)) return PaymentStatus.FAIL, "" # forge the operations logger.debug("Forging {} operations".format(len(content_list))) forge_json = FORGE_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) forge_command_str = self.comm_forge.replace("%JSON%", forge_json) #if verbose: print("--> forge_command_str is |{}|".format(forge_command_str)) result, forge_command_response = self.wllt_clnt_mngr.send_request( forge_command_str) if not result: logger.error("Error in forge operation") logger.debug( "Error in forge, request '{}'".format(forge_command_str)) logger.debug("---") logger.debug( "Error in forge, response '{}'".format(forge_command_response)) return PaymentStatus.FAIL, "" # sign the operations bytes = parse_json_response(forge_command_response, verbose=verbose) signed_bytes = self.wllt_clnt_mngr.sign(bytes, self.manager_alias, verbose_override=True) # pre-apply operations logger.debug("Preapplying the operations") preapply_json = PREAPPLY_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string).replace("%PROTOCOL%", protocol).replace("%SIGNATURE%", signed_bytes) preapply_command_str = self.comm_preapply.replace( "%JSON%", preapply_json) #if verbose: print("--> preapply_command_str is |{}|".format(preapply_command_str)) result, preapply_command_response = self.wllt_clnt_mngr.send_request( preapply_command_str) if not result: logger.error("Error in preapply operation") logger.debug( "Error in preapply, request '{}'".format(preapply_command_str)) logger.debug("---") logger.debug("Error in preapply, response '{}'".format( preapply_command_response)) return PaymentStatus.FAIL, "" # not necessary # preapplied = parse_response(preapply_command_response) # if dry_run, skip injection if dry_run: return PaymentStatus.DONE, "" # inject the operations logger.debug("Injecting {} operations".format(len(content_list))) decoded = base58.b58decode(signed_bytes).hex() if signed_bytes.startswith("edsig"): # edsig signature decoded_edsig_signature = decoded[ 10:][:-8] # first 5 bytes edsig, last 4 bytes checksum decoded_signature = decoded_edsig_signature elif signed_bytes.startswith("sig"): # generic signature decoded_sig_signature = decoded[ 6:][:-8] # first 3 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature elif signed_bytes.startswith("p2sig"): decoded_sig_signature = decoded[ 8:][:-8] # first 4 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature else: raise Exception("Signature '{}' is not in expected format".format( signed_bytes)) if len(decoded_signature) != 128: # must be 64 bytes # raise Exception("Signature length must be 128 but it is {}. Signature is '{}'".format(len(signed_bytes), signed_bytes)) logger.warn( "Signature length must be 128 but it is {}. Signature is '{}'". format(len(signed_bytes), signed_bytes)) # return False, "" signed_operation_bytes = bytes + decoded_signature inject_command_str = self.comm_inject.replace("%OPERATION_HASH%", signed_operation_bytes) #if verbose: print("--> inject_command_str is |{}|".format(inject_command_str)) result, inject_command_response = self.wllt_clnt_mngr.send_request( inject_command_str) if not result: logger.error("Error in inject operation") logger.debug( "Error in inject, response '{}'".format(inject_command_str)) logger.debug("---") logger.debug("Error in inject, response '{}'".format( inject_command_response)) return PaymentStatus.FAIL, "" operation_hash = parse_json_response(inject_command_response) logger.info("Operation hash is {}".format(operation_hash)) # wait for inclusion logger.info( "Waiting for operation {} to be included. Please be patient until the block has {} confirmation(s)" .format(operation_hash, CONFIRMATIONS)) try: cmd = self.comm_wait.replace("%OPERATION%", operation_hash) self.wllt_clnt_mngr.send_request( cmd, timeout=self.network_config[BLOCK_TIME_IN_SEC] * (CONFIRMATIONS + PATIENCE)) logger.info("Operation {} is included".format(operation_hash)) except TimeoutExpired: logger.warn( "Operation {} wait is timed out. Not sure about the result!". format(operation_hash)) return PaymentStatus.INJECTED, operation_hash return PaymentStatus.PAID, operation_hash
def pay_single_batch(self, payment_records, op_counter, verbose=None, dry_run=None): if not op_counter.get(): counter = parse_json_response( self.wllt_clnt_mngr.send_request(self.comm_counter)) counter = int(counter) op_counter.set(counter) head = parse_json_response( self.wllt_clnt_mngr.send_request(self.comm_head)) branch = head["hash"] protocol = head["metadata"]["protocol"] logger.debug("head: branch {} counter {} protocol {}".format( branch, op_counter.get(), protocol)) content_list = [] for payment_item in payment_records: pymnt_amnt = int(payment_item.payment * 1e6) # expects in micro tezos if self.delegator_pays_xfer_fee: pymnt_amnt = max(pymnt_amnt - int(self.default_fee), 0) # ensure not less than 0 if pymnt_amnt < 1e-3: # zero check continue op_counter.inc() content = CONTENT.replace("%SOURCE%", self.source).replace("%DESTINATION%", payment_item.address) \ .replace("%AMOUNT%", str(pymnt_amnt)).replace("%COUNTER%", str(op_counter.get())) \ .replace("%fee%", self.default_fee).replace("%gas_limit%", self.gas_limit).replace("%storage_limit%", self.storage_limit) content_list.append(content) logger.info("Payment content: {}".format(content)) contents_string = ",".join(content_list) # run the operations logger.debug("Running {} operations".format(len(content_list))) runops_json = RUNOPS_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) runops_command_str = self.comm_runops.replace("%JSON%", runops_json) if verbose: logger.debug( "runops_command_str is |{}|".format(runops_command_str)) runops_command_response = self.wllt_clnt_mngr.send_request( runops_command_str) if not check_response(runops_command_response): error_desc = parse_json_response(runops_command_response) # for content in runops_command_response["contents"]: # op_result = content["metadata"]["operation_result"] # if op_result["status"] == 'failed': # error_desc = op_result["errors"] # break logger.error( "Error in run_operation response '{}'".format(error_desc)) return False, "" # forge the operations logger.debug("Forging {} operations".format(len(content_list))) forge_json = FORGE_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) forge_command_str = self.comm_forge.replace("%JSON%", forge_json) if verbose: logger.debug("forge_command_str is |{}|".format(forge_command_str)) forge_command_response = self.wllt_clnt_mngr.send_request( forge_command_str) if not check_response(forge_command_response): logger.error( "Error in forge response '{}'".format(forge_command_response)) return False, "" # sign the operations bytes = parse_json_response(forge_command_response, verbose=verbose) signed_bytes = self.wllt_clnt_mngr.sign(bytes, self.manager_alias) # pre-apply operations logger.debug("Preapplying the operations") preapply_json = PREAPPLY_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string).replace("%PROTOCOL%", protocol).replace("%SIGNATURE%", signed_bytes) preapply_command_str = self.comm_preapply.replace( "%JSON%", preapply_json) if verbose: logger.debug( "preapply_command_str is |{}|".format(preapply_command_str)) preapply_command_response = self.wllt_clnt_mngr.send_request( preapply_command_str) if not check_response(preapply_command_response): logger.error("Error in preapply response '{}'".format( preapply_command_response)) return False, "" # not necessary # preapplied = parse_response(preapply_command_response) # if dry_run, skip injection if dry_run: return True, "" # inject the operations logger.debug("Injecting {} operations".format(len(content_list))) decoded = base58.b58decode(signed_bytes).hex() if signed_bytes.startswith("edsig"): # edsig signature decoded_edsig_signature = decoded[ 10:][:-8] # first 5 bytes edsig, last 4 bytes checksum decoded_signature = decoded_edsig_signature elif signed_bytes.startswith("sig"): # generic signature decoded_sig_signature = decoded[ 6:][:-8] # first 3 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature elif signed_bytes.startswith("p2sig"): decoded_sig_signature = decoded[ 8:][:-8] # first 4 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature else: raise Exception("Signature '{}' is not in expected format".format( signed_bytes)) if len(decoded_signature) != 128: # must be 64 bytes # raise Exception("Signature length must be 128 but it is {}. Signature is '{}'".format(len(signed_bytes), signed_bytes)) logger.warn( "Signature length must be 128 but it is {}. Signature is '{}'". format(len(signed_bytes), signed_bytes)) # return False, "" signed_operation_bytes = bytes + decoded_signature inject_command_str = self.comm_inject.replace("%OPERATION_HASH%", signed_operation_bytes) if verbose: logger.debug( "inject_command_str is |{}|".format(inject_command_str)) inject_command_response = self.wllt_clnt_mngr.send_request( inject_command_str) if not check_response(inject_command_response): logger.error("Error in inject response '{}'".format( inject_command_response)) return False, "" operation_hash = parse_json_response(inject_command_response) logger.debug("Operation hash is {}".format(operation_hash)) # wait for inclusion logger.debug( "Waiting for operation {} to be included. Please be patient until the block has {} confirmation(s)" .format(operation_hash, CONFIRMATIONS)) self.wllt_clnt_mngr.send_request( self.comm_wait.replace("%OPERATION%", operation_hash)) logger.debug("Operation {} is included".format(operation_hash)) return True, operation_hash
def attempt_single_batch(self, payment_records, op_counter, dry_run=None): if not op_counter.get(): _, response = self.wllt_clnt_mngr.send_request(self.comm_counter) counter = parse_json_response(response) counter = int(counter) op_counter.set(counter) _, response = self.wllt_clnt_mngr.send_request(self.comm_head, verbose_override=False) head = parse_json_response(response) branch = head["hash"] chain_id = head["chain_id"] protocol = head["metadata"]["protocol"] logger.debug("head: branch {} counter {} protocol {}".format( branch, op_counter.get(), protocol)) content_list = [] for payment_item in payment_records: storage = self.storage_limit pymnt_amnt = payment_item.amount # expects in micro tezos if payment_item.needs_activation: storage += RA_STORAGE if self.delegator_pays_ra_fee: pymnt_amnt = max(pymnt_amnt - RA_BURN_FEE, 0) # ensure not less than 0 if self.delegator_pays_xfer_fee: pymnt_amnt = max(pymnt_amnt - self.default_fee, 0) # ensure not less than 0 # if pymnt_amnt becomes 0, don't pay if pymnt_amnt == 0: logger.debug( "Payment to {} became 0 after deducting fees. Skipping.". format(payment_item.paymentaddress)) continue op_counter.inc() content = CONTENT.replace("%SOURCE%", self.source).replace("%DESTINATION%", payment_item.paymentaddress) \ .replace("%AMOUNT%", str(pymnt_amnt)).replace("%COUNTER%", str(op_counter.get())) \ .replace("%fee%", str(self.default_fee)).replace("%gas_limit%", self.gas_limit).replace( "%storage_limit%", str(storage)) content_list.append(content) verbose_logger.info("Payment content: {}".format(content)) contents_string = ",".join(content_list) # run the operations logger.debug("Running {} operations".format(len(content_list))) runops_json = RUNOPS_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) runops_json = JSON_WRAP.replace("%JSON%", runops_json).replace( "%chain_id%", chain_id) runops_command_str = self.comm_runops.replace("%JSON%", runops_json) result, runops_command_response = self.wllt_clnt_mngr.send_request( runops_command_str) if not result: logger.error("Error in run_operation") logger.debug("Error in run_operation, request ->{}<-".format( runops_command_str)) logger.debug("---") logger.debug("Error in run_operation, response ->{}<-".format( runops_command_response)) return PaymentStatus.FAIL, "" # Parse result of run_operation and check for potential failures run_ops_parsed = parse_json_response(runops_command_response) # Check each contents object for failure for op in run_ops_parsed["contents"]: # https://docs.python.org/3/glossary.html#term-eafp try: op_status = op["metadata"]["operation_result"]["status"] if op_status == "failed": op_error = op["metadata"]["operation_result"]["errors"][0][ "id"] logger.error( "Error while validating operation - Status: {}, Message: {}" .format(op_status, op_error)) return PaymentStatus.FAIL, "" except KeyError: logger.debug( "Unable to find metadata->operation_result->{status,errors} in run_ops response" ) pass # forge the operations logger.debug("Forging {} operations".format(len(content_list))) forge_json = FORGE_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string) forge_command_str = self.comm_forge.replace("%JSON%", forge_json) result, forge_command_response = self.wllt_clnt_mngr.send_request( forge_command_str) if not result: logger.error("Error in forge operation") logger.debug( "Error in forge, request '{}'".format(forge_command_str)) logger.debug("---") logger.debug( "Error in forge, response '{}'".format(forge_command_response)) return PaymentStatus.FAIL, "" # sign the operations bytes = parse_json_response(forge_command_response) signed_bytes = self.wllt_clnt_mngr.sign(bytes, self.manager_alias, verbose_override=True) # pre-apply operations logger.debug("Preapplying the operations") preapply_json = PREAPPLY_JSON.replace('%BRANCH%', branch).replace( "%CONTENT%", contents_string).replace("%PROTOCOL%", protocol).replace("%SIGNATURE%", signed_bytes) preapply_command_str = self.comm_preapply.replace( "%JSON%", preapply_json) result, preapply_command_response = self.wllt_clnt_mngr.send_request( preapply_command_str) if not result: logger.error("Error in preapply operation") logger.debug( "Error in preapply, request '{}'".format(preapply_command_str)) logger.debug("---") logger.debug("Error in preapply, response '{}'".format( preapply_command_response)) return PaymentStatus.FAIL, "" # not necessary # preapplied = parse_response(preapply_command_response) # if dry_run, skip injection if dry_run: return PaymentStatus.DONE, "" # inject the operations logger.debug("Injecting {} operations".format(len(content_list))) decoded = base58.b58decode(signed_bytes).hex() if signed_bytes.startswith("edsig"): # edsig signature decoded_edsig_signature = decoded[ 10:][:-8] # first 5 bytes edsig, last 4 bytes checksum decoded_signature = decoded_edsig_signature elif signed_bytes.startswith("sig"): # generic signature decoded_sig_signature = decoded[ 6:][:-8] # first 3 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature elif signed_bytes.startswith("p2sig"): decoded_sig_signature = decoded[ 8:][:-8] # first 4 bytes sig, last 4 bytes checksum decoded_signature = decoded_sig_signature else: raise Exception("Signature '{}' is not in expected format".format( signed_bytes)) if len(decoded_signature) != 128: # must be 64 bytes # raise Exception("Signature length must be 128 but it is {}. Signature is '{}'".format(len(signed_bytes), signed_bytes)) logger.warn( "Signature length must be 128 but it is {}. Signature is '{}'". format(len(signed_bytes), signed_bytes)) # return False, "" signed_operation_bytes = bytes + decoded_signature inject_command_str = self.comm_inject.replace("%OPERATION_HASH%", signed_operation_bytes) result, inject_command_response = self.wllt_clnt_mngr.send_request( inject_command_str) if not result: logger.error("Error in inject operation") logger.debug( "Error in inject, response '{}'".format(inject_command_str)) logger.debug("---") logger.debug("Error in inject, response '{}'".format( inject_command_response)) return PaymentStatus.FAIL, "" operation_hash = parse_json_response(inject_command_response) logger.info("Operation hash is {}".format(operation_hash)) # wait for inclusion logger.info( "Waiting for operation {} to be included. Please be patient until the block has {} confirmation(s)" .format(operation_hash, CONFIRMATIONS)) try: cmd = self.comm_wait.replace("%OPERATION%", operation_hash) self.wllt_clnt_mngr.send_request( cmd, timeout=self.get_confirmation_timeout()) logger.info("Operation {} is included".format(operation_hash)) except TimeoutExpired: logger.warn( "Operation {} wait is timed out. Not sure about the result!". format(operation_hash)) return PaymentStatus.INJECTED, operation_hash return PaymentStatus.PAID, operation_hash
def get_current_level_hash(self, verbose=False): _, response = self.wllt_clnt_mngr.send_request( COMM_HEAD.format(self.node_url)) head = parse_json_response(response) return head['hash']