Example #1
0
    def try_to_pay(self, pymnt_cycle):
        try:
            logger.info("Payment cycle is " + str(pymnt_cycle))

            # 0- check for past payment evidence for current cycle
            past_payment_state = check_past_payment(self.payments_root, pymnt_cycle)

            if not self.dry_run and past_payment_state:
                logger.warn(past_payment_state)
                return True

            # 1- get reward data
            reward_model = self.reward_api.get_rewards_for_cycle_map(pymnt_cycle)
            # 2- calculate rewards
            reward_logs, total_amount = self.payment_calc.calculate(reward_model)
            # set cycle info
            for rl in reward_logs: rl.cycle = pymnt_cycle
            total_amount_to_pay = sum([rl.amount for rl in reward_logs if rl.payable])

            # 4- if total_rewards > 0, proceed with payment
            if total_amount_to_pay > 0:
                report_file_path = get_calculation_report_file(self.calculations_dir, pymnt_cycle)

                # 5- send to payment consumer
                self.payments_queue.put(PaymentBatch(self, pymnt_cycle, reward_logs))
                # logger.info("Total payment amount is {:,} mutez. %s".format(total_amount_to_pay),
                #            "" if self.delegator_pays_xfer_fee else "(Transfer fee is not included)")

                logger.debug("Creating calculation report (%s)", report_file_path)

                # 6- create calculations report file. This file contains calculations details
                self.create_calculations_report(reward_logs, report_file_path, total_amount)
                # 7- next cycle
                # processing of cycle is done
                logger.info(
                    "Reward creation is done for cycle {}, created {} rewards.".format(pymnt_cycle, len(reward_logs)))

            elif total_amount_to_pay == 0:
                logger.info("Total payment amount is 0. Nothing to pay!")

            return True
        except ReadTimeout:
            logger.info("Tzscan call failed, will try again.")
            logger.debug("Tzscan call failed", exc_info=False)
            return False
        except ConnectTimeout:
            logger.info("Tzscan connection failed, will try again.")
            logger.debug("Tzscan connection failed", exc_info=False)
            return False
        except TzScanException:
            logger.info("Tzscan error at reward loop, will try again.")
            logger.debug("Tzscan error at reward loop", exc_info=False)
            return False
        except Exception:
            logger.error("Error at payment producer loop, will try again.", exc_info=True)
            return False
        finally:
            sleep(10)
    def try_to_pay(self, payment_cycle):
        try:
            logger.info("Payment cycle is " + str(payment_cycle))

            # 1- get reward data
            reward_data = self.reward_api.get_rewards_for_cycle_map(
                payment_cycle, verbose=self.verbose)

            # 2- make payment calculations from reward data
            pymnt_logs, total_rewards = self.make_payment_calculations(
                payment_cycle, reward_data)

            # 3- check for past payment evidence for current cycle
            past_payment_state = check_past_payment(self.payments_root,
                                                    payment_cycle)
            if not self.dry_run and total_rewards > 0 and past_payment_state:
                logger.warn(past_payment_state)
                total_rewards = 0

            # 4- if total_rewards > 0, proceed with payment
            if total_rewards > 0:
                report_file_path = get_calculation_report_file(
                    self.calculations_dir, payment_cycle)

                # 5- send to payment consumer
                self.payments_queue.put(pymnt_logs)

                # 6- create calculations report file. This file contains calculations details
                self.create_calculations_report(payment_cycle, pymnt_logs,
                                                report_file_path,
                                                total_rewards)

            # processing of cycle is done
            logger.info("Reward creation done for cycle %s", payment_cycle)
            return True
        except TzScanException:
            logger.warn("Tzscan error at reward calculation", exc_info=True)
            return False
        except Exception:
            logger.error("Error at reward calculation", exc_info=True)
            return False
Example #3
0
    def try_to_pay(self, pymnt_cycle, expected_rewards=False):
        try:
            logger.info("Payment cycle is " + str(pymnt_cycle))

            # 0- check for past payment evidence for current cycle
            past_payment_state = check_past_payment(self.payments_root,
                                                    pymnt_cycle)

            if not self.dry_run and past_payment_state:
                logger.warn(past_payment_state)
                return True

            # 1- get reward data
            if expected_rewards:
                logger.info(
                    "Using expected/ideal rewards for payouts calculations")
            else:
                logger.info("Using actual rewards for payouts calculations")

            reward_model = self.reward_api.get_rewards_for_cycle_map(
                pymnt_cycle, expected_rewards)

            # 2- calculate rewards
            reward_logs, total_amount = self.payment_calc.calculate(
                reward_model)

            # 3- set cycle info
            for rl in reward_logs:
                rl.cycle = pymnt_cycle
            total_amount_to_pay = sum(
                [rl.amount for rl in reward_logs if rl.payable])

            # 4- if total_rewards > 0, proceed with payment
            if total_amount_to_pay > 0:
                report_file_path = get_calculation_report_file(
                    self.calculations_dir, pymnt_cycle)

                # 5- send to payment consumer
                self.payments_queue.put(
                    PaymentBatch(self, pymnt_cycle, reward_logs))

                # logger.info("Total payment amount is {:,} mutez. %s".format(total_amount_to_pay),
                #            "" if self.delegator_pays_xfer_fee else "(Transfer fee is not included)")

                logger.debug("Creating calculation report (%s)",
                             report_file_path)

                sleep(5.0)

                # 6- create calculations report file. This file contains calculations details
                self.create_calculations_report(reward_logs, report_file_path,
                                                total_amount, expected_rewards)

                # 7- processing of cycle is done
                logger.info(
                    "Reward creation is done for cycle {}, created {} rewards."
                    .format(pymnt_cycle, len(reward_logs)))

            elif total_amount_to_pay == 0:
                logger.info("Total payment amount is 0. Nothing to pay!")

        except ApiProviderException as a:
            logger.error("[try_to_pay] API provider error {:s}".format(str(a)))
            raise a from a
        except Exception as e:
            logger.error("[try_to_pay] Generic exception {:s}".format(str(e)))
            raise e from e

        # Either succeeded or raised exception
        return True
    def try_to_pay(self, pymnt_cycle, rewards_type, network_config):
        try:
            logger.info("Payment cycle is {:s}".format(str(pymnt_cycle)))

            # 0- check for past payment evidence for current cycle
            past_payment_state = check_past_payment(self.payments_root,
                                                    pymnt_cycle)

            if past_payment_state:
                logger.warn(past_payment_state)
                return True

            # 1- get reward data
            reward_model = self.reward_api.get_rewards_for_cycle_map(
                pymnt_cycle, rewards_type)

            # 2- compute reward amount to distribute based on configuration
            reward_model.computed_reward_amount = self.compute_rewards(
                reward_model, rewards_type, network_config)

            # 3- calculate rewards for delegators
            reward_logs, total_amount = self.payment_calc.calculate(
                reward_model)

            # 4- set cycle info
            for rl in reward_logs:
                rl.cycle = pymnt_cycle
            total_amount_to_pay = sum(
                [rl.amount for rl in reward_logs if rl.payable])

            # 5- if total_rewards > 0, proceed with payment
            if total_amount_to_pay > 0:

                # 6- send to payment consumer
                self.payments_queue.put(
                    PaymentBatch(self, pymnt_cycle, reward_logs))

                sleep(5.0)

                # 7- create calculations report file. This file contains calculations details
                report_file_path = get_calculation_report_file(
                    self.calculations_dir, pymnt_cycle)
                logger.debug("Creating calculation report (%s)",
                             report_file_path)
                self.create_calculations_report(reward_logs, report_file_path,
                                                total_amount, rewards_type)

                # 8- processing of cycle is done
                logger.info(
                    "Reward creation is done for cycle {}, created {} rewards."
                    .format(pymnt_cycle, len(reward_logs)))

            elif total_amount_to_pay == 0:
                logger.info("Total payment amount is 0. Nothing to pay!")

        except ApiProviderException as a:
            logger.error("[try_to_pay] API provider error {:s}".format(str(a)))
            raise a from a
        except Exception as e:
            logger.error("[try_to_pay] Generic exception {:s}".format(str(e)))
            raise e from e

        # Either succeeded or raised exception
        return True
    def run(self):

        current_cycle = self.block_api.get_current_cycle()

        payment_cycle = self.initial_payment_cycle

        # if non-positive initial_payment_cycle, set initial_payment_cycle to
        # 'current cycle - abs(initial_cycle) - (NB_FREEZE_CYCLE+1)'

        if self.initial_payment_cycle <= 0:
            payment_cycle = current_cycle - abs(self.initial_payment_cycle) - (
                self.nw_config['NB_FREEZE_CYCLE'] + 1)
            logger.debug("Payment cycle is set to {}".format(payment_cycle))

        while self.life_cycle.is_running():

            # take a breath
            time.sleep(5)

            logger.debug("Trying payments for cycle {}".format(payment_cycle))

            current_level = self.block_api.get_current_level()
            current_cycle = self.block_api.level_to_cycle(current_level)

            # create reports dir
            if self.calculations_dir and not os.path.exists(
                    self.calculations_dir):
                os.makedirs(self.calculations_dir)

            logger.debug(
                "Checking for pending payments : payment_cycle <= "
                "current_cycle - (self.nw_config['NB_FREEZE_CYCLE'] + 1) - self.release_override"
            )
            logger.debug(
                "Checking for pending payments : checking {} <= {} - ({} + 1) - {}"
                .format(payment_cycle, current_cycle,
                        self.nw_config['NB_FREEZE_CYCLE'],
                        self.release_override))

            # payments should not pass beyond last released reward cycle
            if payment_cycle <= current_cycle - (
                    self.nw_config['NB_FREEZE_CYCLE'] +
                    1) - self.release_override:
                if not self.payments_queue.full():
                    try:

                        logger.info("Payment cycle is " + str(payment_cycle))

                        # 1- get reward data
                        reward_data = self.reward_api.get_rewards_for_cycle_map(
                            payment_cycle, verbose=self.verbose)

                        # 2- make payment calculations from reward data
                        pymnt_logs, total_rewards = self.make_payment_calculations(
                            payment_cycle, reward_data)

                        # 3- check for past payment evidence for current cycle
                        past_payment_state = check_past_payment(
                            self.payments_root, payment_cycle)
                        if not self.dry_run and total_rewards > 0 and past_payment_state:
                            logger.warn(past_payment_state)
                            total_rewards = 0

                        # 4- if total_rewards > 0, proceed with payment
                        if total_rewards > 0:
                            report_file_path = get_calculation_report_file(
                                self.calculations_dir, payment_cycle)

                            # 5- send to payment consumer
                            self.payments_queue.put(pymnt_logs)

                            # 6- create calculations report file. This file contains calculations details
                            self.create_calculations_report(
                                payment_cycle, pymnt_logs, report_file_path,
                                total_rewards)

                        # 7- next cycle
                        # processing of cycle is done
                        logger.info("Reward creation done for cycle %s",
                                    payment_cycle)
                        payment_cycle = payment_cycle + 1

                        # single run is done. Do not continue.
                        if self.run_mode == RunMode.ONETIME:
                            logger.info(
                                "Run mode ONETIME satisfied. Killing the thread ..."
                            )
                            self.exit()
                            break

                    except Exception:
                        logger.error("Error at reward calculation",
                                     exc_info=True)

                # end of queue size check
                else:
                    logger.debug("Wait a few minutes, queue is full")
                    # wait a few minutes to let payments done
                    time.sleep(60 * 3)
            # end of payment cycle check
            else:
                logger.debug(
                    "No pending payments for cycle {}, current cycle is {}".
                    format(payment_cycle, current_cycle))
                # pending payments done. Do not wait any more.
                if self.run_mode == RunMode.PENDING:
                    logger.info(
                        "Run mode PENDING satisfied. Killing the thread ...")
                    self.exit()
                    break

                time.sleep(10)

                self.retry_failed_payments()

                # calculate number of blocks until end of current cycle
                nb_blocks_remaining = (
                    current_cycle +
                    1) * self.nw_config['BLOCKS_PER_CYCLE'] - current_level
                # plus offset. cycle beginnings may be busy, move payments forward
                nb_blocks_remaining = nb_blocks_remaining + self.payment_offset

                logger.debug("Wait until next cycle, for {} blocks".format(
                    nb_blocks_remaining))

                # wait until current cycle ends
                self.waint_until_next_cycle(nb_blocks_remaining)

        # end of endless loop
        logger.info("Producer returning ...")

        # ensure consumer exits
        self.exit()

        return
    def try_to_pay(self, pymnt_cycle, rewards_type, network_config,
                   current_cycle):
        try:
            logger.info("Payment cycle is {:s}".format(str(pymnt_cycle)))

            # 0- check for past payment evidence for current cycle
            past_payment_state = check_past_payment(self.payments_root,
                                                    pymnt_cycle)

            if past_payment_state:
                logger.warn(past_payment_state)
                return True

            adjustments = {}
            early_payout = False
            current_cycle_rewards_type = rewards_type
            # 1- adjust past cycle if necessary
            if self.release_override == -11 and pymnt_cycle >= current_cycle:
                early_payout = True
                completed_cycle = pymnt_cycle - 6
                adjustments = self.recompute_rewards(completed_cycle,
                                                     rewards_type,
                                                     network_config)
                # payout for current cycle will be estimated since we don't know actual rewards yet
                current_cycle_rewards_type = RewardsType.ESTIMATED

            # 2- get reward data and compute how to distribute them
            reward_logs, total_amount = self.compute_rewards(
                pymnt_cycle, current_cycle_rewards_type, network_config,
                adjustments)
            total_recovered_adjustments = int(
                sum([rl.adjustment for rl in reward_logs]))
            total_adjustments_to_recover = int(sum(adjustments.values()))
            if total_adjustments_to_recover > 0:
                logger.debug(
                    "Total adjustments to recover is {:<,d} mutez, total recovered adjustment is {:<,d} mutez."
                    .format(total_adjustments_to_recover,
                            total_recovered_adjustments))
                logger.info(
                    "After early payout of cycle {:s}, {:<,d} mutez were not recovered."
                    .format(
                        str(completed_cycle),
                        total_adjustments_to_recover +
                        total_recovered_adjustments,
                    ))

            # 3- create calculations report file. This file contains calculations details
            report_file_path = get_calculation_report_file_path(
                self.calculations_dir, pymnt_cycle)
            logger.debug("Creating calculation report (%s)", report_file_path)
            CsvCalculationFileParser().write(
                reward_logs,
                report_file_path,
                total_amount,
                current_cycle_rewards_type,
                self.baking_address,
                early_payout,
            )

            # 4- set cycle info
            for reward_log in reward_logs:
                reward_log.cycle = pymnt_cycle
            total_amount_to_pay = int(
                sum([
                    reward_log.adjusted_amount for reward_log in reward_logs
                    if reward_log.payable
                ]))

            # 5- if total_rewards > 0, proceed with payment
            if total_amount_to_pay > 0:

                self.payments_queue.put(
                    PaymentBatch(self, pymnt_cycle, reward_logs))

                sleep(5.0)

                # 6- processing of cycle is done
                logger.info(
                    "Reward creation is done for cycle {}, created {} rewards."
                    .format(pymnt_cycle, len(reward_logs)))

            elif total_amount_to_pay == 0:
                logger.info("Total payment amount is 0. Nothing to pay!")

        except ApiProviderException as a:
            logger.error("[try_to_pay] API provider error {:s}".format(str(a)))
            raise a from a
        except Exception as e:
            logger.error("[try_to_pay] Generic exception {:s}".format(str(e)))
            raise e from e

        # Either succeeded or raised exception
        return True