예제 #1
0
    def calculate(self, reward_data):
        root = reward_data

        delegate_staking_balance = int(root["delegate_staking_balance"])
        blocks_rewards = int(root["blocks_rewards"])
        future_blocks_rewards = int(root["future_blocks_rewards"])
        endorsements_rewards = int(root["endorsements_rewards"])
        future_endorsements_rewards = int(root["future_endorsements_rewards"])
        lost_rewards_denounciation = int(root["lost_rewards_denounciation"])
        lost_fees_denounciation = int(root["lost_fees_denounciation"])
        fees = int(root["fees"])

        self.total_rewards = (
            blocks_rewards + endorsements_rewards + future_blocks_rewards +
            future_endorsements_rewards + fees - lost_rewards_denounciation -
            lost_fees_denounciation) / MUTEZ

        delegators_balance = root["delegators_balance"]

        effective_delegate_staking_balance = delegate_staking_balance
        effective_delegators_balance = []

        # excluded addresses are processed
        for dbalance in delegators_balance:
            address = dbalance[0]["tz"]
            balance = int(dbalance[1])

            if address in self.excluded_set:
                effective_delegate_staking_balance -= balance
                continue
            effective_delegators_balance.append(dbalance)

        rewards = []
        # calculate how rewards will be distributed
        for dbalance in effective_delegators_balance:
            address = dbalance[0]["tz"]
            balance = int(dbalance[1])

            # Skip those that did not delegate minimum amount
            if balance < self.min_delegation_amt_mutez:
                self.logger.debug(
                    "Skipping '{}': Low delegation amount ({:.6f})".format(
                        address, (balance / MUTEZ)))
                continue

            ratio = self.rc.round(balance / effective_delegate_staking_balance)
            reward = (self.total_rewards * ratio)

            reward_item = PaymentRecord(address=address,
                                        reward=reward,
                                        ratio=ratio)

            rewards.append(reward_item)

        return rewards, self.total_rewards
예제 #2
0
    def calculate(self, reward_data, verbose=False):
        total_rewards = 0
        rewards = []

        delegate_staking_balance, delegators = reward_data[
            "delegate_staking_balance"], reward_data["delegators"]

        if len(delegators) > 0:

            total_rewards = reward_data["total_rewards"] / MUTEZ

            if total_rewards > 0:

                effective_delegate_staking_balance = delegate_staking_balance
                effective_delegator_addresses = []

                # excluded addresses are processed
                for address in delegators:
                    balance = delegators[address]

                    if address in self.excluded_set:
                        effective_delegate_staking_balance -= balance
                        continue
                    effective_delegator_addresses.append(address)

                # calculate how rewards will be distributed
                for address in effective_delegator_addresses:
                    balance = delegators[address]

                    # Skip those that did not delegate minimum amount
                    if balance < self.min_delegation_amt_mutez:
                        self.logger.debug(
                            "Skipping '{}': Low delegation amount ({:.6f})".
                            format(address, (balance / MUTEZ)))
                        continue

                    ratio = self.rc.round(balance /
                                          effective_delegate_staking_balance)
                    reward = (total_rewards * ratio)

                    #                    print(address, str(ratio*100) + ' %   -->   ', reward)

                    reward_item = PaymentRecord(address=address,
                                                reward=reward,
                                                ratio=ratio)

                    rewards.append(reward_item)

        return rewards, total_rewards
예제 #3
0
def main(args):
    logger.info("Arguments Configuration = {}".format(
        json.dumps(args.__dict__, indent=1)))

    # 1- find where configuration is
    config_dir = os.path.expanduser(args.config_dir)

    # create configuration directory if it is not present
    # so that user can easily put his configuration there
    if config_dir and not os.path.exists(config_dir):
        os.makedirs(config_dir)

    # 2- Load master configuration file if it is present
    master_config_file_path = os.path.join(config_dir, "master.yaml")

    master_cfg = {}
    if os.path.isfile(master_config_file_path):
        logger.info("Loading master configuration file {}".format(
            master_config_file_path))

        master_parser = YamlConfParser(
            ConfigParser.load_file(master_config_file_path))
        master_cfg = master_parser.parse()
    else:
        logger.info("master configuration file not present.")

    managers = None
    contracts_by_alias = None
    addresses_by_pkh = None
    if 'managers' in master_cfg:
        managers = master_cfg['managers']
    if 'contracts_by_alias' in master_cfg:
        contracts_by_alias = master_cfg['contracts_by_alias']
    if 'addresses_by_pkh' in master_cfg:
        addresses_by_pkh = master_cfg['addresses_by_pkh']

    # 3- load payments file
    payments_file = os.path.expanduser(args.payments_file)
    if not os.path.isfile(payments_file):
        raise Exception(
            "payments_file ({}) does not exist.".format(payments_file))

    with open(payments_file, 'r') as file:
        payment_lines = file.readlines()

    payments_dict = {}
    for line in payment_lines:
        pkh, amt = line.split(":")
        pkh = pkh.strip()
        amt = float(amt.strip())

        payments_dict[pkh] = amt

    if not payments_dict:
        raise Exception("No payments to process")

    # 4- get client path
    network_config = network_config_map[args.network]
    client_path = get_client_path(
        [x.strip() for x in args.executable_dirs.split(',')], args.docker,
        network_config, args.verbose)

    logger.debug("Tezos client path is {}".format(client_path))

    # 6- is it a reports run
    dry_run = args.dry_run

    # 7- get reporting directories
    reports_dir = os.path.expanduser(args.reports_dir)
    # if in reports run mode, do not create consumers
    # create reports in reports directory
    if dry_run:
        reports_dir = os.path.expanduser("./reports")

    reports_dir = os.path.join(reports_dir, "manual")

    payments_root = get_payment_root(reports_dir, create=True)
    calculations_root = get_calculations_root(reports_dir, create=True)
    get_successful_payments_dir(payments_root, create=True)
    get_failed_payments_dir(payments_root, create=True)

    wllt_clnt_mngr = WalletClientManager(client_path, contracts_by_alias,
                                         addresses_by_pkh, managers)

    for i in range(NB_CONSUMERS):
        c = PaymentConsumer(name='manual_payment_consumer',
                            payments_dir=payments_root,
                            key_name=args.paymentaddress,
                            client_path=client_path,
                            payments_queue=payments_queue,
                            node_addr=args.node_addr,
                            wllt_clnt_mngr=wllt_clnt_mngr,
                            verbose=args.verbose,
                            dry_run=dry_run,
                            delegator_pays_xfer_fee=False)
        time.sleep(1)
        c.start()

    base_name_no_ext = os.path.basename(payments_file)
    base_name_no_ext = os.path.splitext(base_name_no_ext)[0]
    now = datetime.now()
    now_str = now.strftime("%Y%m%d%H%M%S")
    file_name = base_name_no_ext + "_" + now_str

    payment_items = []
    for key, value in payments_dict.items():
        payment_items.append(
            PaymentRecord.ManualInstance(file_name, key, value))

    payments_queue.put(payment_items)
    payments_queue.put([PaymentRecord.ExitInstance()])
    def retry_failed_payments(self):
        logger.info("retry_failed_payments started")

        # 1 - list csv files under payments/failed directory
        # absolute path of csv files found under payments_root/failed directory
        failed_payments_dir = get_failed_payments_dir(self.payments_root)
        payment_reports_failed = [
            os.path.join(failed_payments_dir, x)
            for x in os.listdir(failed_payments_dir) if x.endswith('.csv')
        ]

        logger.debug("Trying failed payments : '{}'".format(
            ",".join(payment_reports_failed)))

        # 2- for each csv file with name csv_report.csv
        for payment_failed_report_file in payment_reports_failed:
            logger.debug("Working on failed payment file {}".format(
                payment_failed_report_file))

            # 2.1 - if there is a file csv_report.csv under payments/done, it means payment is already done
            if os.path.isfile(
                    payment_failed_report_file.replace(PAYMENT_FAILED_DIR,
                                                       PAYMENT_DONE_DIR)):
                # remove payments/failed/csv_report.csv
                os.remove(payment_failed_report_file)
                logger.debug(
                    "Payment for failed payment {} is already done. Removing.".
                    format(payment_failed_report_file))

                # remove payments/failed/csv_report.csv.BUSY
                # if there is a busy failed payment report file, remove it.
                remove_busy_file(payment_failed_report_file)

                # do not double pay
                continue

            # 2.2 - if queue is full, wait for sometime
            # make sure the queue is not full
            while self.payments_queue.full():
                logger.info("Payments queue is full. Wait for 30 seconds.")
                time.sleep(30)

            cycle = int(
                os.path.splitext(
                    os.path.basename(payment_failed_report_file))[0])

            # 2.3 read payments/failed/csv_report.csv file into a list of dictionaries
            with open(payment_failed_report_file) as f:
                # read csv into list of dictionaries
                dict_rows = [{key: value
                              for key, value in row.items()}
                             for row in csv.DictReader(
                                 f, delimiter='\t', skipinitialspace=True)]

                batch = PaymentRecord.FromPaymentCSVDictRows(dict_rows, cycle)

                # 2.4 put records into payment_queue. payment_consumer will make payments
                if batch:
                    self.payments_queue.put(batch)
                else:
                    logger.info("Nothing to pay.")

            # 2.5 rename payments/failed/csv_report.csv to payments/failed/csv_report.csv.BUSY
            # mark the files as in use. we do not want it to be read again
            # BUSY file will be removed, if successful payment is done
            os.rename(payment_failed_report_file,
                      get_busy_file(payment_failed_report_file))
 def create_exit_payment():
     return PaymentRecord.ExitInstance()
예제 #6
0
    def calculate(self):
        pymnts = []

        # 1- calculate delegators payments
        delegators_total_pymnt = 0
        delegators_total_ratio = 0
        delegators_total_fee = 0
        for ri in self.reward_list:
            # set fee rate
            fee_rate = self.fee_calc.calculate(ri.address)
            pymnt_amnt = self.rounding_command.roundDown(ri.reward *
                                                         (1 - fee_rate))

            # this indicates, service fee is very low (e.g. 0) and pymnt_amnt is rounded up
            if pymnt_amnt - ri.reward > 0:
                pymnt_amnt = ri.reward

            fee = (ri.reward - pymnt_amnt)

            pr = PaymentRecord.DelegatorInstance(self.cycle, ri.address,
                                                 ri.ratio, fee_rate, ri.reward,
                                                 fee, pymnt_amnt)
            pymnts.append(pr)

            delegators_total_pymnt = delegators_total_pymnt + pymnt_amnt
            delegators_total_ratio = delegators_total_ratio + ri.ratio
            delegators_total_fee = delegators_total_fee + fee

        # 2- calculate deposit owners payments. They share the remaining rewards according to their ratio (check config)
        owners_total_pymnt = 0
        owners_total_reward = self.total_rewards - (delegators_total_pymnt +
                                                    delegators_total_fee)
        no_owners = True

        # Skip if no owners defined
        if len(self.owners_map) > 0:
            no_owners = False
            for address, ratio in self.owners_map.items():
                owner_pymnt_amnt = self.rounding_command.roundDown(
                    ratio * owners_total_reward)
                owners_total_pymnt = owners_total_pymnt + owner_pymnt_amnt
                pymnts.append(
                    PaymentRecord.OwnerInstance(self.cycle, address, ratio,
                                                owner_pymnt_amnt,
                                                owner_pymnt_amnt))

        # move remaining rewards to service fee bucket
        # 3- service fee is shared among founders according to founders_map ratios
        founders_total_pymnt = 0
        founders_total_reward = self.total_rewards - delegators_total_pymnt - owners_total_pymnt
        no_founders = True

        # If no owners paid, must include their would-be reward in this calculation
        if no_owners:
            founders_total_reward = founders_total_reward - owners_total_reward

        # Skip if no founders defined
        if len(self.founders_map) > 0:
            no_founders = False
            for address, ratio in self.founders_map.items():
                # founder pymnt is rounded to scale 6 regardless of the settings.
                # because we allow difference between total_sum and self.total_rewards be less than 5e-6
                founder_pymnt_amnt = floorf(ratio * founders_total_reward, 6)
                pymnts.append(
                    PaymentRecord.FounderInstance(self.cycle, address, ratio,
                                                  founder_pymnt_amnt))

        ###
        # sanity check
        #####
        total_sum = 0
        for payment_log in pymnts:
            total_sum = total_sum + payment_log.payment

        # if no owners/no founders, add that would-be amount to total_sum
        # the baker will keep (owners_total_reward + founders_total_reward) automatically when unfrozen
        if no_owners:
            total_sum = total_sum + owners_total_reward
        if no_founders:
            total_sum = total_sum + founders_total_reward

        # if there is a minor difference due to floor function; it is added to last payment
        if self.total_rewards - total_sum > 1e-6:
            last_pymnt_log = pymnts[
                -1]  # last payment, probably one of the founders
            last_pymnt_log.payment = last_pymnt_log.payment + (
                self.total_rewards - total_sum)

        # this must never return true
        if abs(total_sum - self.total_rewards) > 5e-6:
            raise Exception(
                "Calculated reward {} is not equal to total reward {}".format(
                    total_sum, self.total_rewards))

        return pymnts