def test_history2(self, node_param): if node_param == "non_appbase": stm = self.bts else: stm = self.appbase account = Account("dpaygobot", dpay_instance=stm) h_list = [] max_index = account.virtual_op_count() for h in account.history(start=max_index - 4, stop=max_index, use_block_num=False, batch_size=2, raw_output=False): h_list.append(h) self.assertEqual(len(h_list), 5) for i in range(1, 5): self.assertEqual(h_list[i]["index"] - h_list[i - 1]["index"], 1) h_list = [] for h in account.history(start=max_index - 4, stop=max_index, use_block_num=False, batch_size=6, raw_output=False): h_list.append(h) self.assertEqual(len(h_list), 5) for i in range(1, 5): self.assertEqual(h_list[i]["index"] - h_list[i - 1]["index"], 1) h_list = [] for h in account.history(start=max_index - 4, stop=max_index, use_block_num=False, batch_size=2, raw_output=True): h_list.append(h) self.assertEqual(len(h_list), 5) for i in range(1, 5): self.assertEqual(h_list[i][0] - h_list[i - 1][0], 1) h_list = [] for h in account.history(start=max_index - 4, stop=max_index, use_block_num=False, batch_size=6, raw_output=True): h_list.append(h) self.assertEqual(len(h_list), 5) for i in range(1, 5): self.assertEqual(h_list[i][0] - h_list[i - 1][0], 1)
def test_history_votes(self): stm = self.bts account = Account("gtg", dpay_instance=stm) utc = pytz.timezone('UTC') limit_time = utc.localize(datetime.utcnow()) - timedelta(days=2) votes_list = [] for v in account.history(start=limit_time, only_ops=["vote"]): votes_list.append(v) start_num = votes_list[0]["block"] votes_list2 = [] for v in account.history(start=start_num, only_ops=["vote"]): votes_list2.append(v) self.assertTrue(abs(len(votes_list) - len(votes_list2)) < 2)
def test_history_block_num(self, node_param): if node_param == "non_appbase": stm = self.bts zero_element = 0 else: stm = self.appbase zero_element = 0 # bug in dpay account = Account("fullnodeupdate", dpay_instance=stm) h_all_raw = [] for h in account.history_reverse(raw_output=True): h_all_raw.append(h) h_list = [] for h in account.history(start=h_all_raw[-1][1]["block"], stop=h_all_raw[-11 + zero_element][1]["block"], use_block_num=True, batch_size=10, raw_output=True): h_list.append(h) self.assertEqual(h_list[0][0], zero_element) self.assertEqual(h_list[-1][0], 10) self.assertEqual(h_list[0][1]['block'], h_all_raw[-1][1]['block']) self.assertEqual(h_list[-1][1]['block'], h_all_raw[-11 + zero_element][1]['block']) h_list = [] for h in account.history_reverse(start=h_all_raw[-11 + zero_element][1]["block"], stop=h_all_raw[-1][1]["block"], use_block_num=True, batch_size=10, raw_output=True): h_list.append(h) self.assertEqual(h_list[0][0], 10) self.assertEqual(h_list[-1][0], zero_element) self.assertEqual(h_list[0][1]['block'], h_all_raw[-11 + zero_element][1]['block']) self.assertEqual(h_list[-1][1]['block'], h_all_raw[-1][1]['block']) h_list = [] for h in account.get_account_history(10, 10, use_block_num=True, start=h_all_raw[-2 + zero_element][1]["block"], stop=h_all_raw[-10 + zero_element][1]["block"], order=1, raw_output=True): h_list.append(h) self.assertEqual(h_list[0][0], 1) self.assertEqual(h_list[-1][0], 9) self.assertEqual(h_list[0][1]['block'], h_all_raw[-2 + zero_element][1]['block']) self.assertEqual(h_list[-1][1]['block'], h_all_raw[-10 + zero_element][1]['block']) h_list = [] for h in account.get_account_history(10, 10, use_block_num=True, start=h_all_raw[-10 + zero_element][1]["block"], stop=h_all_raw[-2 + zero_element][1]["block"], order=-1, raw_output=True): h_list.append(h) self.assertEqual(h_list[0][0], 9) self.assertEqual(h_list[-1][0], 1) self.assertEqual(h_list[0][1]['block'], h_all_raw[-10 + zero_element][1]['block']) self.assertEqual(h_list[-1][1]['block'], h_all_raw[-2 + zero_element][1]['block'])
print("dpay_acc.get_account_votes() {}".format( dpay_acc.get_account_votes())) # get_withdraw_routes print("dpaygo_acc.get_withdraw_routes() {}".format( dpaygo_acc.get_withdraw_routes())) print("dpay_acc.get_withdraw_routes() {}".format( dpay_acc.get_withdraw_routes())) # get_conversion_requests print("dpaygo_acc.get_conversion_requests() {}".format( dpaygo_acc.get_conversion_requests())) print("dpay_acc.get_conversion_requests() {}".format( dpay_acc.get_conversion_requests())) # export # history dpaygo_hist = [] for h in dpaygo_acc.history(only_ops=["transfer"]): dpaygo_hist.append(h) if len(dpaygo_hist) >= 10: break dpay_hist = [] for h in dpay_acc.history(filter_by="transfer", start=0): dpay_hist.append(h) if len(dpay_hist) >= 10: break print("dpaygo_acc.history() {}".format(dpaygo_hist)) print("dpay_acc.history() {}".format(dpay_hist)) # history_reverse dpaygo_hist = [] for h in dpaygo_acc.history_reverse(only_ops=["transfer"]): dpaygo_hist.append(h) if len(dpaygo_hist) >= 10:
class AccountSnapshot(list): """ This class allows to easily access Account history :param str account_name: Name of the account :param dpaygo.dpay.DPay dpay_instance: DPay instance """ def __init__(self, account, account_history=[], dpay_instance=None): self.dpay = dpay_instance or shared_dpay_instance() self.account = Account(account, dpay_instance=self.dpay) self.reset() super(AccountSnapshot, self).__init__(account_history) def reset(self): """ Resets the arrays not the stored account history """ self.own_vests = [Amount("0 VESTS", dpay_instance=self.dpay)] self.own_dpay = [Amount("0 BEX", dpay_instance=self.dpay)] self.own_bbd = [Amount("0 BBD", dpay_instance=self.dpay)] self.delegated_vests_in = [{}] self.delegated_vests_out = [{}] self.timestamps = [addTzInfo(datetime(1970, 1, 1, 0, 0, 0, 0))] import dpaygobase.operationids self.ops_statistics = dpaygobase.operationids.operations.copy() for key in self.ops_statistics: self.ops_statistics[key] = 0 self.reward_timestamps = [] self.author_rewards = [] self.curation_rewards = [] self.curation_per_1000_SP_timestamp = [] self.curation_per_1000_SP = [] self.out_vote_timestamp = [] self.out_vote_weight = [] self.in_vote_timestamp = [] self.in_vote_weight = [] self.in_vote_rep = [] self.in_vote_rshares = [] self.vp = [] self.vp_timestamp = [] self.rep = [] self.rep_timestamp = [] def search(self, search_str, start=None, stop=None, use_block_num=True): """ Returns ops in the given range""" ops = [] if start is not None: start = addTzInfo(start) if stop is not None: stop = addTzInfo(stop) for op in self: if use_block_num and start is not None and isinstance(start, int): if op["block"] < start: continue elif not use_block_num and start is not None and isinstance( start, int): if op["index"] < start: continue elif start is not None and isinstance(start, (datetime, date, time)): if start > formatTimeString(op["timestamp"]): continue if use_block_num and stop is not None and isinstance(stop, int): if op["block"] > stop: continue elif not use_block_num and stop is not None and isinstance( stop, int): if op["index"] > stop: continue elif stop is not None and isinstance(stop, (datetime, date, time)): if stop < formatTimeString(op["timestamp"]): continue op_string = json.dumps(list(op.values())) if re.search(search_str, op_string): ops.append(op) return ops def get_ops(self, start=None, stop=None, use_block_num=True, only_ops=[], exclude_ops=[]): """ Returns ops in the given range""" if start is not None: start = addTzInfo(start) if stop is not None: stop = addTzInfo(stop) for op in self: if use_block_num and start is not None and isinstance(start, int): if op["block"] < start: continue elif not use_block_num and start is not None and isinstance( start, int): if op["index"] < start: continue elif start is not None and isinstance(start, (datetime, date, time)): if start > formatTimeString(op["timestamp"]): continue if use_block_num and stop is not None and isinstance(stop, int): if op["block"] > stop: continue elif not use_block_num and stop is not None and isinstance( stop, int): if op["index"] > stop: continue elif stop is not None and isinstance(stop, (datetime, date, time)): if stop < formatTimeString(op["timestamp"]): continue if exclude_ops and op["type"] in exclude_ops: continue if not only_ops or op["type"] in only_ops: yield op def get_data(self, timestamp=None, index=0): """ Returns snapshot for given timestamp""" if timestamp is None: timestamp = datetime.utcnow() timestamp = addTzInfo(timestamp) # Find rightmost value less than x i = bisect_left(self.timestamps, timestamp) if i: index = i - 1 else: return {} ts = self.timestamps[index] own = self.own_vests[index] din = self.delegated_vests_in[index] dout = self.delegated_vests_out[index] dpay = self.own_dpay[index] bbd = self.own_bbd[index] sum_in = sum([din[key].amount for key in din]) sum_out = sum([dout[key].amount for key in dout]) bp_in = self.dpay.vests_to_sp(sum_in, timestamp=ts) bp_out = self.dpay.vests_to_sp(sum_out, timestamp=ts) bp_own = self.dpay.vests_to_sp(own, timestamp=ts) bp_eff = bp_own + bp_in - bp_out return { "timestamp": ts, "vests": own, "delegated_vests_in": din, "delegated_vests_out": dout, "bp_own": bp_own, "bp_eff": bp_eff, "dpay": dpay, "bbd": bbd, "index": index } def get_account_history(self, start=None, stop=None, use_block_num=True): """ Uses account history to fetch all related ops :param int/datetime start: start number/date of transactions to return (*optional*) :param int/datetime stop: stop number/date of transactions to return (*optional*) :param bool use_block_num: if true, start and stop are block numbers, otherwise virtual OP count numbers. """ super(AccountSnapshot, self).__init__([ h for h in self.account.history( start=start, stop=stop, use_block_num=use_block_num) ]) def update_rewards(self, timestamp, curation_reward, author_vests, author_dpay, author_bbd): self.reward_timestamps.append(timestamp) self.curation_rewards.append(curation_reward) self.author_rewards.append({ "vests": author_vests, "dpay": author_dpay, "bbd": author_bbd }) def update_out_vote(self, timestamp, weight): self.out_vote_timestamp.append(timestamp) self.out_vote_weight.append(weight) def update_in_vote(self, timestamp, weight, op): v = Vote(op) try: v.refresh() self.in_vote_timestamp.append(timestamp) self.in_vote_weight.append(weight) self.in_vote_rep.append(int(v["reputation"])) self.in_vote_rshares.append(int(v["rshares"])) except: print("Could not found: %s" % v) return def update(self, timestamp, own, delegated_in=None, delegated_out=None, dpay=0, bbd=0): """ Updates the internal state arrays :param datetime timestamp: datetime of the update :param Amount/float own: vests :param dict delegated_in: Incoming delegation :param dict delegated_out: Outgoing delegation :param Amount/float dpay: dpay :param Amount/float bbd: bbd """ self.timestamps.append(timestamp - timedelta(seconds=1)) self.own_vests.append(self.own_vests[-1]) self.own_dpay.append(self.own_dpay[-1]) self.own_bbd.append(self.own_bbd[-1]) self.delegated_vests_in.append(self.delegated_vests_in[-1]) self.delegated_vests_out.append(self.delegated_vests_out[-1]) self.timestamps.append(timestamp) self.own_vests.append(self.own_vests[-1] + own) self.own_dpay.append(self.own_dpay[-1] + dpay) self.own_bbd.append(self.own_bbd[-1] + bbd) new_deleg = dict(self.delegated_vests_in[-1]) if delegated_in is not None and delegated_in: if delegated_in['amount'] == 0: del new_deleg[delegated_in['account']] else: new_deleg[delegated_in['account']] = delegated_in['amount'] self.delegated_vests_in.append(new_deleg) new_deleg = dict(self.delegated_vests_out[-1]) if delegated_out is not None and delegated_out: if delegated_out['account'] is None: # return_vesting_delegation for delegatee in new_deleg: if new_deleg[delegatee]['amount'] == delegated_out[ 'amount']: del new_deleg[delegatee] break elif delegated_out['amount'] != 0: # new or updated non-zero delegation new_deleg[delegated_out['account']] = delegated_out['amount'] # skip undelegations here, wait for 'return_vesting_delegation' # del new_deleg[delegated_out['account']] self.delegated_vests_out.append(new_deleg) def build(self, only_ops=[], exclude_ops=[], enable_rewards=False, enable_out_votes=False, enable_in_votes=False): """ Builds the account history based on all account operations :param array only_ops: Limit generator by these operations (*optional*) :param array exclude_ops: Exclude thse operations from generator (*optional*) """ if len(self.timestamps) > 0: start_timestamp = self.timestamps[-1] else: start_timestamp = None for op in sorted(self, key=lambda k: k['timestamp']): ts = parse_time(op['timestamp']) if start_timestamp is not None and start_timestamp > ts: continue # print(op) if op['type'] in exclude_ops: continue if len(only_ops) > 0 and op['type'] not in only_ops: continue self.ops_statistics[op['type']] += 1 self.parse_op(op, only_ops=only_ops, enable_rewards=enable_rewards, enable_out_votes=enable_out_votes, enable_in_votes=enable_in_votes) def parse_op(self, op, only_ops=[], enable_rewards=False, enable_out_votes=False, enable_in_votes=False): """ Parse account history operation""" ts = parse_time(op['timestamp']) if op['type'] == "account_create": fee_dpay = Amount(op['fee'], dpay_instance=self.dpay).amount fee_vests = self.dpay.bp_to_vests(Amount( op['fee'], dpay_instance=self.dpay).amount, timestamp=ts) # print(fee_vests) if op['new_account_name'] == self.account["name"]: self.update(ts, fee_vests, 0, 0) return if op['creator'] == self.account["name"]: self.update(ts, 0, 0, 0, fee_dpay * (-1), 0) return elif op['type'] == "account_create_with_delegation": fee_dpay = Amount(op['fee'], dpay_instance=self.dpay).amount fee_vests = self.dpay.bp_to_vests(Amount( op['fee'], dpay_instance=self.dpay).amount, timestamp=ts) if op['new_account_name'] == self.account["name"]: if Amount(op['delegation'], dpay_instance=self.dpay).amount > 0: delegation = { 'account': op['creator'], 'amount': Amount(op['delegation'], dpay_instance=self.dpay) } else: delegation = None self.update(ts, fee_vests, delegation, 0) return if op['creator'] == self.account["name"]: delegation = { 'account': op['new_account_name'], 'amount': Amount(op['delegation'], dpay_instance=self.dpay) } self.update(ts, 0, 0, delegation, fee_dpay * (-1), 0) return elif op['type'] == "delegate_vesting_shares": vests = Amount(op['vesting_shares'], dpay_instance=self.dpay) # print(op) if op['delegator'] == self.account["name"]: delegation = {'account': op['delegatee'], 'amount': vests} self.update(ts, 0, 0, delegation) return if op['delegatee'] == self.account["name"]: delegation = {'account': op['delegator'], 'amount': vests} self.update(ts, 0, delegation, 0) return elif op['type'] == "transfer": amount = Amount(op['amount'], dpay_instance=self.dpay) # print(op) if op['from'] == self.account["name"]: if amount.symbol == "BEX": self.update(ts, 0, 0, 0, amount * (-1), 0) elif amount.symbol == "BBD": self.update(ts, 0, 0, 0, 0, amount * (-1)) if op['to'] == self.account["name"]: if amount.symbol == "BEX": self.update(ts, 0, 0, 0, amount, 0) elif amount.symbol == "BBD": self.update(ts, 0, 0, 0, 0, amount) # print(op, vests) # self.update(ts, vests, 0, 0) return elif op['type'] == "fill_order": current_pays = Amount(op["current_pays"], dpay_instance=self.dpay) open_pays = Amount(op["open_pays"], dpay_instance=self.dpay) if op["current_owner"] == self.account["name"]: if current_pays.symbol == "BEX": self.update(ts, 0, 0, 0, current_pays * (-1), open_pays) elif current_pays.symbol == "BBD": self.update(ts, 0, 0, 0, open_pays, current_pays * (-1)) if op["open_owner"] == self.account["name"]: if current_pays.symbol == "BEX": self.update(ts, 0, 0, 0, current_pays, open_pays * (-1)) elif current_pays.symbol == "BBD": self.update(ts, 0, 0, 0, open_pays * (-1), current_pays) # print(op) return elif op['type'] == "transfer_to_vesting": dpay = Amount(op['amount'], dpay_instance=self.dpay) vests = self.dpay.bp_to_vests(dpay.amount, timestamp=ts) if op['from'] == self.account["name"]: self.update(ts, vests, 0, 0, dpay * (-1), 0) else: self.update(ts, vests, 0, 0, 0, 0) # print(op) # print(op, vests) return elif op['type'] == "fill_vesting_withdraw": # print(op) vests = Amount(op['withdrawn'], dpay_instance=self.dpay) self.update(ts, vests * (-1), 0, 0) return elif op['type'] == "return_vesting_delegation": delegation = { 'account': None, 'amount': Amount(op['vesting_shares'], dpay_instance=self.dpay) } self.update(ts, 0, 0, delegation) return elif op['type'] == "claim_reward_balance": vests = Amount(op['reward_vests'], dpay_instance=self.dpay) dpay = Amount(op['reward_dpay'], dpay_instance=self.dpay) bbd = Amount(op['reward_bbd'], dpay_instance=self.dpay) self.update(ts, vests, 0, 0, dpay, bbd) return elif op['type'] == "curation_reward": if "curation_reward" in only_ops or enable_rewards: vests = Amount(op['reward'], dpay_instance=self.dpay) if "curation_reward" in only_ops: self.update(ts, vests, 0, 0) if enable_rewards: self.update_rewards(ts, vests, 0, 0, 0) return elif op['type'] == "author_reward": if "author_reward" in only_ops or enable_rewards: # print(op) vests = Amount(op['vesting_payout'], dpay_instance=self.dpay) dpay = Amount(op['dpay_payout'], dpay_instance=self.dpay) bbd = Amount(op['bbd_payout'], dpay_instance=self.dpay) if "author_reward" in only_ops: self.update(ts, vests, 0, 0, dpay, bbd) if enable_rewards: self.update_rewards(ts, 0, vests, dpay, bbd) return elif op['type'] == "producer_reward": vests = Amount(op['vesting_shares'], dpay_instance=self.dpay) self.update(ts, vests, 0, 0) return elif op['type'] == "comment_benefactor_reward": if op['benefactor'] == self.account["name"]: vests = Amount(op['reward'], dpay_instance=self.dpay) self.update(ts, vests, 0, 0) return else: return elif op['type'] == "fill_convert_request": amount_in = Amount(op["amount_in"], dpay_instance=self.dpay) amount_out = Amount(op["amount_out"], dpay_instance=self.dpay) if op["owner"] == self.account["name"]: self.update(ts, 0, 0, 0, amount_out, amount_in * (-1)) return elif op['type'] == "interest": interest = Amount(op["interest"], dpay_instance=self.dpay) self.update(ts, 0, 0, 0, 0, interest) return elif op['type'] == "vote": if "vote" in only_ops or enable_out_votes: weight = int(op['weight']) if op["voter"] == self.account["name"]: self.update_out_vote(ts, weight) if "vote" in only_ops or enable_in_votes and op[ "author"] == self.account["name"]: weight = int(op['weight']) self.update_in_vote(ts, weight, op) return elif op['type'] in [ 'comment', 'feed_publish', 'shutdown_witness', 'account_witness_vote', 'witness_update', 'custom_json', 'limit_order_create', 'account_update', 'account_witness_proxy', 'limit_order_cancel', 'comment_options', 'delete_comment', 'interest', 'recover_account', 'pow', 'fill_convert_request', 'convert', 'request_account_recovery' ]: return # if "vests" in str(op).lower(): # print(op) # else: # print(op) def build_bp_arrays(self): """ Builds the own_sp and eff_sp array""" self.own_sp = [] self.eff_sp = [] for (ts, own, din, dout) in zip(self.timestamps, self.own_vests, self.delegated_vests_in, self.delegated_vests_out): sum_in = sum([din[key].amount for key in din]) sum_out = sum([dout[key].amount for key in dout]) bp_in = self.dpay.vests_to_sp(sum_in, timestamp=ts) bp_out = self.dpay.vests_to_sp(sum_out, timestamp=ts) bp_own = self.dpay.vests_to_sp(own, timestamp=ts) bp_eff = bp_own + bp_in - bp_out self.own_sp.append(bp_own) self.eff_sp.append(bp_eff) def build_rep_arrays(self): """ Build reputation arrays """ self.rep_timestamp = [self.timestamps[1]] self.rep = [reputation_to_score(0)] current_reputation = 0 for (ts, rshares, rep) in zip(self.in_vote_timestamp, self.in_vote_rshares, self.in_vote_rep): if rep > 0: if rshares > 0 or (rshares < 0 and rep > current_reputation): current_reputation += rshares >> 6 self.rep.append(reputation_to_score(current_reputation)) self.rep_timestamp.append(ts) def build_vp_arrays(self): """ Build vote power arrays""" self.vp_timestamp = [self.timestamps[1]] self.vp = [DPAY_100_PERCENT] for (ts, weight) in zip(self.out_vote_timestamp, self.out_vote_weight): self.vp.append(self.vp[-1]) if self.vp[-1] < DPAY_100_PERCENT: regenerated_vp = ((ts - self.vp_timestamp[-1]).total_seconds( )) * DPAY_100_PERCENT / DPAY_VOTE_REGENERATION_SECONDS self.vp[-1] += int(regenerated_vp) if self.vp[-1] > DPAY_100_PERCENT: self.vp[-1] = DPAY_100_PERCENT self.vp[-1] -= self.dpay._calc_resulting_vote(self.vp[-1], weight) if self.vp[-1] < 0: self.vp[-1] = 0 self.vp_timestamp.append(ts) def build_curation_arrays(self, end_date=None, sum_days=7): """ Build curation arrays""" self.curation_per_1000_SP_timestamp = [] self.curation_per_1000_SP = [] if sum_days <= 0: raise ValueError("sum_days must be greater than 0") index = 0 curation_sum = 0 days = (self.reward_timestamps[-1] - self.reward_timestamps[0]).days // sum_days * sum_days if end_date is None: end_date = self.reward_timestamps[-1] - timedelta(days=days) for (ts, vests) in zip(self.reward_timestamps, self.curation_rewards): if vests == 0: continue sp = self.dpay.vests_to_sp(vests, timestamp=ts) data = self.get_data(timestamp=ts, index=index) index = data["index"] if "bp_eff" in data and data["bp_eff"] > 0: curation_1k_sp = sp / data["bp_eff"] * 1000 / sum_days * 7 else: curation_1k_sp = 0 if ts < end_date: curation_sum += curation_1k_sp else: self.curation_per_1000_SP_timestamp.append(end_date) self.curation_per_1000_SP.append(curation_sum) end_date = end_date + timedelta(days=sum_days) curation_sum = 0 def __str__(self): return self.__repr__() def __repr__(self): return "<%s %s>" % (self.__class__.__name__, str(self.account["name"]))