예제 #1
0
    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
예제 #3
0
 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
예제 #5
0
    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 ""
예제 #6
0
    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
예제 #8
0
 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
예제 #9
0
    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
예제 #10
0
    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
예제 #11
0
    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)
예제 #12
0
    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
예제 #13
0
    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 ""
예제 #14
0
    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
예제 #15
0
    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
예제 #16
0
    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
예제 #17
0
    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']