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
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
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()
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