From a905e3fbaf93caee6a7aeda4e1093ae6a9d55655 Mon Sep 17 00:00:00 2001 From: Holger Nahrstaedt <holgernahrstaedt@gmx.de> Date: Thu, 5 Nov 2020 22:41:56 +0100 Subject: [PATCH] Improve account history * Adapt account history on api changes and fixes issue #267 * Speed up history call, when limit is below 1000 * Improve unit tests for account history --- CHANGELOG.rst | 6 + beem/account.py | 50 ++- beem/blurt.py | 10 +- beem/hive.py | 2 +- setup.py | 2 +- tests/beem/nodes.py | 4 + tests/beem/test_account.py | 43 ++- tests/beem/test_account_blurt.py | 304 +++++++++++++++++ tests/beem/test_account_steem.py | 559 +++++++++++++++++++++++++++++++ 9 files changed, 949 insertions(+), 31 deletions(-) create mode 100644 tests/beem/test_account_blurt.py create mode 100644 tests/beem/test_account_steem.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 157cde17..90ccc0ac 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,5 +1,11 @@ Changelog ========= +0.24.18 +------- +* Adapt account history on api changes and fixes issue #267 +* Speed up history call, when limit is below 1000 +* Improve unit tests for account history + 0.24.17 ------- * Fixed a bug when using skip_account_check=True diff --git a/beem/account.py b/beem/account.py index c78ec60e..ed84b3b9 100644 --- a/beem/account.py +++ b/beem/account.py @@ -1831,20 +1831,22 @@ class Account(BlockchainObject): else: try: op_count = 0 - op_count = self._get_account_history(start=-1, limit=0) + op_count = self._get_account_history(start=-1, limit=1) if op_count is None or len(op_count) == 0: op_count = self._get_account_history(start=-1, limit=1) if isinstance(op_count, list) and len(op_count) > 0 and len(op_count[0]) > 0: - return op_count[0][0] + return op_count[-1][0] else: return 0 except IndexError: return 0 - def _get_account_history(self, account=None, start=-1, limit=0): + def _get_account_history(self, account=None, start=-1, limit=1): if account is None: account = self["name"] account = extract_account_name(account) + if limit < 1: + limit = 1 if not self.blockchain.is_connected(): raise OfflineHasNoRPCException("No RPC available in offline mode!") self.blockchain.rpc.set_next_node_on_empty_reply(False) @@ -1857,8 +1859,6 @@ class Account(BlockchainObject): ret = self.blockchain.rpc.get_account_history(account, start, limit, api="condenser") else: ret = self.blockchain.rpc.get_account_history(account, start, limit, api="database") - if ret is None or (len(ret) == 0 and limit == 0): - ret = self.blockchain.rpc.get_account_history(account, start, limit + 1, api="database") return ret def estimate_virtual_op_num(self, blocktime, stop_diff=0, max_count=100): @@ -1904,6 +1904,8 @@ class Account(BlockchainObject): """ def get_blocknum(index): + if index == 0: + index = 1 op = self._get_account_history(start=(index)) return op[0][1]['block'] @@ -1912,7 +1914,7 @@ class Account(BlockchainObject): return 0 # calculate everything with block numbers - created = get_blocknum(0) + created = get_blocknum(1) # convert blocktime to block number if given as a datetime/date/time if isinstance(blocktime, (datetime, date, time)): @@ -2207,6 +2209,8 @@ class Account(BlockchainObject): start_index = start elif start is not None and max_index > batch_size: op_est = self.estimate_virtual_op_num(start, stop_diff=1) + if op_est == 0: + op_est = 1 est_diff = 0 if isinstance(start, (datetime, date, time)): for h in self.get_account_history(op_est, 0): @@ -2229,16 +2233,26 @@ class Account(BlockchainObject): start_index = op_est - est_diff else: start_index = 0 + + if stop is not None and not use_block_num and not isinstance(stop, (datetime, date, time)): + if start_index + stop < _limit: + _limit = stop - first = start_index + _limit + first = start_index + _limit - 1 if first > max_index: - _limit = max_index - start_index + 1 - first = start_index + _limit + _limit = max_index - start_index + first = start_index + _limit - 1 + elif first < _limit: + first = _limit last_round = False + if _limit < 0: return + last_item_index = -1 while True: # RPC call + if first < _limit: + first = _limit for item in self.get_account_history(first, _limit, start=None, stop=None, order=1, raw_output=raw_output): if raw_output: item_index, event = item @@ -2258,6 +2272,8 @@ class Account(BlockchainObject): continue elif start is not None and not use_block_num and item_index < start: continue + elif last_item_index == item_index: + continue if stop is not None and isinstance(stop, (datetime, date, time)): timediff = stop - formatTimeString(timestamp) if timediff.total_seconds() < 0: @@ -2271,13 +2287,14 @@ class Account(BlockchainObject): continue if not only_ops or op_type in only_ops: yield item + last_item_index = item_index if first < max_index and first + _limit >= max_index and not last_round: - _limit = max_index - first - 1 + _limit = max_index - first first = max_index last_round = True else: - first += (_limit + 1) - if stop is not None and not use_block_num and isinstance(stop, int) and first >= stop + _limit: + first += (_limit) + if stop is not None and not use_block_num and isinstance(stop, int) and first >= stop + _limit + 1: break elif first > max_index or last_round: break @@ -2374,6 +2391,8 @@ class Account(BlockchainObject): elif start is not None and first > batch_size: op_est = self.estimate_virtual_op_num(start, stop_diff=1) est_diff = 0 + if op_est == 0: + op_est = 1 if isinstance(start, (datetime, date, time)): for h in self.get_account_history(op_est, 0): block_date = formatTimeString(h["timestamp"]) @@ -2395,7 +2414,7 @@ class Account(BlockchainObject): first = op_est + est_diff if stop is not None and isinstance(stop, int) and stop < 0 and not use_block_num: stop += first - + last_item_index = -1 while True: # RPC call if first - _limit < 0: @@ -2419,6 +2438,8 @@ class Account(BlockchainObject): continue elif start is not None and not use_block_num and item_index > start: continue + elif item_index == last_item_index: + continue if stop is not None and isinstance(stop, (datetime, date, time)): timediff = stop - formatTimeString(timestamp) if timediff.total_seconds() > 0: @@ -2434,7 +2455,8 @@ class Account(BlockchainObject): continue if not only_ops or op_type in only_ops: yield item - first -= (_limit + 1) + last_item_index = item_index + first -= (_limit) if first < 1: break diff --git a/beem/blurt.py b/beem/blurt.py index 4bc17168..9850f75c 100644 --- a/beem/blurt.py +++ b/beem/blurt.py @@ -75,20 +75,20 @@ class Blurt(BlockChainInstance): .. code-block:: python - steem = Steem(<host>) + blurt = Blurt(<host>) where ``<host>`` starts with ``https://``, ``ws://`` or ``wss://``. The purpose of this class it to simplify interaction with - Steem. + Blurt. The idea is to have a class that allows to do this: .. code-block:: python - >>> from beem import Steem - >>> steem = Steem() - >>> print(steem.get_blockchain_version()) # doctest: +SKIP + >>> from beem import Blurt + >>> blurt = Blurt() + >>> print(blurt.get_blockchain_version()) # doctest: +SKIP This class also deals with edits, votes and reading content. diff --git a/beem/hive.py b/beem/hive.py index b2f88fc4..a8178162 100644 --- a/beem/hive.py +++ b/beem/hive.py @@ -82,7 +82,7 @@ class Hive(BlockChainInstance): where ``<host>`` starts with ``https://``, ``ws://`` or ``wss://``. The purpose of this class it to simplify interaction with - Steem. + Hive. The idea is to have a class that allows to do this: diff --git a/setup.py b/setup.py index aa949be6..156e6461 100755 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ except LookupError: ascii = codecs.lookup('ascii') codecs.register(lambda name, enc=ascii: {True: enc}.get(name == 'mbcs')) -VERSION = '0.24.17' +VERSION = '0.24.18' tests_require = ['mock >= 2.0.0', 'pytest', 'pytest-mock', 'parameterized'] diff --git a/tests/beem/nodes.py b/tests/beem/nodes.py index 49ab544c..cbab206f 100644 --- a/tests/beem/nodes.py +++ b/tests/beem/nodes.py @@ -13,3 +13,7 @@ def get_hive_nodes(): def get_steem_nodes(): return "https://api.steemit.com" + + +def get_blurt_nodes(): + return "https://rpc.blurt.world" diff --git a/tests/beem/test_account.py b/tests/beem/test_account.py index 819d5b21..7dc2c468 100644 --- a/tests/beem/test_account.py +++ b/tests/beem/test_account.py @@ -4,14 +4,14 @@ import pytz from datetime import datetime, timedelta from parameterized import parameterized from pprint import pprint -from beem import Steem, exceptions +from beem import Steem, exceptions, Hive from beem.account import Account, extract_account_name from beem.block import Block from beem.amount import Amount from beem.asset import Asset from beem.utils import formatTimeString -from beem.instance import set_shared_steem_instance -from .nodes import get_hive_nodes +from beem.instance import set_shared_blockchain_instance +from .nodes import get_hive_nodes, get_steem_nodes wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" @@ -20,10 +20,9 @@ class Testcases(unittest.TestCase): @classmethod def setUpClass(cls): - node_list = get_hive_nodes() - cls.bts = Steem( - node=node_list, + cls.bts = Hive( + node=get_hive_nodes(), nobroadcast=True, bundle=False, unsigned=True, @@ -31,8 +30,8 @@ class Testcases(unittest.TestCase): keys={"active": wif}, num_retries=10 ) - cls.account = Account("beembot", steem_instance=cls.bts) - set_shared_steem_instance(cls.bts) + cls.account = Account("beembot", steem_instance=cls.bts) + set_shared_blockchain_instance(cls.bts) def test_account(self): stm = self.bts @@ -68,6 +67,10 @@ class Testcases(unittest.TestCase): h_all_raw = [] for h in account.history_reverse(raw_output=True): h_all_raw.append(h) + index = h_all_raw[0][0] + for op in h_all_raw: + self.assertEqual(op[0], index) + index -= 1 # h_all_raw = h_all_raw[zero_element:] zero_element = h_all_raw[-1][0] h_list = [] @@ -159,8 +162,9 @@ class Testcases(unittest.TestCase): h_list = [] for h in account.get_account_history(10, 10, start=start, stop=stop, order=-1, raw_output=True): h_list.append(h) - self.assertEqual(h_list[0][0], 9) - self.assertEqual(h_list[-1][0], 1) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], 9 - i) + 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']) @@ -196,6 +200,21 @@ class Testcases(unittest.TestCase): for i in range(1, 5): self.assertEqual(h_list[i][0] - h_list[i - 1][0], 1) + def test_history_index(self): + stm = self.bts + account = Account("beembot", steem_instance=stm) + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=10, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=2, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + def test_history_reverse2(self): stm = self.bts account = Account("beembot", steem_instance=stm) @@ -486,6 +505,10 @@ class Testcases(unittest.TestCase): h_all_raw = [] for h in account.history(raw_output=False): h_all_raw.append(h) + index = h_all_raw[0]["index"] + for op in h_all_raw: + self.assertEqual(op["index"], index) + index += 1 last_block = h_all_raw[0]["block"] i = 1 for op in h_all_raw[1:5]: diff --git a/tests/beem/test_account_blurt.py b/tests/beem/test_account_blurt.py new file mode 100644 index 00000000..9bda0206 --- /dev/null +++ b/tests/beem/test_account_blurt.py @@ -0,0 +1,304 @@ +# -*- coding: utf-8 -*- +import unittest +import pytz +from datetime import datetime, timedelta +from parameterized import parameterized +from pprint import pprint +from beem import Steem, exceptions, Blurt +from beem.account import Account, extract_account_name +from beem.block import Block +from beem.amount import Amount +from beem.asset import Asset +from beem.utils import formatTimeString +from beem.instance import set_shared_blockchain_instance +from .nodes import get_blurt_nodes + +wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" + + +class Testcases(unittest.TestCase): + + @classmethod + def setUpClass(cls): + + cls.bts = Blurt( + node=get_blurt_nodes(), + nobroadcast=True, + bundle=False, + unsigned=True, + # Overwrite wallet to use this list of wifs only + keys={"active": wif}, + num_retries=10 + ) + cls.account = Account("beembot", steem_instance=cls.bts) + set_shared_blockchain_instance(cls.bts) + + def test_account(self): + stm = self.bts + account = self.account + Account("beembot", steem_instance=stm) + with self.assertRaises( + exceptions.AccountDoesNotExistsException + ): + Account("DoesNotExistsXXX", steem_instance=stm) + # asset = Asset("1.3.0") + # symbol = asset["symbol"] + self.assertEqual(account.name, "beembot") + self.assertEqual(account["name"], account.name) + self.assertIsInstance(account.get_balance("available", "BLURT"), Amount) + account.print_info() + # self.assertIsInstance(account.balance({"symbol": symbol}), Amount) + self.assertIsInstance(account.available_balances, list) + # self.assertTrue(account.virtual_op_count() > 0) + + # BlockchainObjects method + account.cached = False + self.assertTrue(list(account.items())) + account.cached = False + self.assertIn("id", account) + account.cached = False + # self.assertEqual(account["id"], "1.2.1") + self.assertEqual(str(account), "<Account beembot>") + self.assertIsInstance(Account(account), Account) + + def test_history_index(self): + stm = self.bts + account = Account("beembot", steem_instance=stm) + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=10, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=2, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + + def test_account_props(self): + account = self.account + rep = account.get_reputation() + self.assertTrue(isinstance(rep, float)) + vp = account.get_voting_power() + self.assertTrue(vp >= 0) + self.assertTrue(vp <= 100) + sp = account.get_token_power() + self.assertTrue(sp >= 0) + vv = account.get_voting_value_SBD() + self.assertTrue(vv >= 0) + bw = account.get_bandwidth() + # self.assertTrue(bw['used'] <= bw['allocated']) + followers = account.get_followers() + self.assertTrue(isinstance(followers, list)) + following = account.get_following() + self.assertTrue(isinstance(following, list)) + count = account.get_follow_count() + self.assertEqual(count['follower_count'], len(followers)) + self.assertEqual(count['following_count'], len(following)) + + + def test_MissingKeyError(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.convert("1 BLURT") + with self.assertRaises( + exceptions.MissingKeyError + ): + tx.sign() + + def test_withdraw_vesting(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.withdraw_vesting("100 VESTS") + self.assertEqual( + (tx["operations"][0][0]), + "withdraw_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["account"]) + + def test_delegate_vesting_shares(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.delegate_vesting_shares("test1", "100 VESTS") + self.assertEqual( + (tx["operations"][0][0]), + "delegate_vesting_shares" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["delegator"]) + + def test_claim_reward_balance(self): + w = self.account + w.blockchain.txbuffer.clear() + #tx = w.claim_reward_balance() + #self.assertEqual( + # (tx["operations"][0][0]), + # "claim_reward_balance" + #) + #op = tx["operations"][0][1] + #self.assertIn( + # "beembot", + # op["account"]) + + def test_cancel_transfer_from_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.cancel_transfer_from_savings(0) + self.assertEqual( + (tx["operations"][0][0]), + "cancel_transfer_from_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer_from_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_from_savings(1, "BLURT", "") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_from_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer_to_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_to_savings(1, "BLURT", "") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_convert(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.convert("1 BLURT") + self.assertEqual( + (tx["operations"][0][0]), + "convert" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["owner"]) + + def test_proxy(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.setproxy(proxy="gtg") + self.assertEqual( + (tx["operations"][0][0]), + "account_witness_proxy" + ) + op = tx["operations"][0][1] + self.assertIn( + "gtg", + op["proxy"]) + + def test_transfer_to_vesting(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_to_vesting("1 BLURT") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + w.blockchain.txbuffer.clear() + tx = w.transfer_to_vesting("1 BLURT", skip_account_check=True) + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer("beembot", "1", "BLURT") + self.assertEqual( + (tx["operations"][0][0]), + "transfer" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + self.assertIn( + "beembot", + op["to"]) + + w.blockchain.txbuffer.clear() + tx = w.transfer("beembot", "1", "BLURT", skip_account_check=True) + self.assertEqual( + (tx["operations"][0][0]), + "transfer" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + self.assertIn( + "beembot", + op["to"]) + + def test_json_export(self): + account = Account("beembot", steem_instance=self.bts) + if account.blockchain.rpc.get_use_appbase(): + content = self.bts.rpc.find_accounts({'accounts': [account["name"]]}, api="database")["accounts"][0] + else: + content = self.bts.rpc.get_accounts([account["name"]])[0] + keys = list(content.keys()) + json_content = account.json() + exclude_list = ['owner_challenged', 'average_bandwidth'] # ['json_metadata', 'reputation', 'active_votes', 'savings_sbd_seconds'] + for k in keys: + if k not in exclude_list: + if isinstance(content[k], dict) and isinstance(json_content[k], list): + content_list = [content[k]["amount"], content[k]["precision"], content[k]["nai"]] + self.assertEqual(content_list, json_content[k]) + else: + self.assertEqual(content[k], json_content[k]) + + def test_reply_history(self): + account = self.account + replies = [] + for r in account.reply_history(limit=1): + replies.append(r) + #self.assertEqual(len(replies), 1) + if len(replies) > 0: + self.assertTrue(replies[0].is_comment()) + self.assertTrue(replies[0].depth > 0) + + def test_history(self): + stm = self.bts + account = Account("holger80", steem_instance=stm) + h_all_raw = [] + for h in account.history(raw_output=False): + h_all_raw.append(h) + index = h_all_raw[0]["index"] + for op in h_all_raw: + self.assertEqual(op["index"], index) + index += 1 diff --git a/tests/beem/test_account_steem.py b/tests/beem/test_account_steem.py new file mode 100644 index 00000000..4552d6dc --- /dev/null +++ b/tests/beem/test_account_steem.py @@ -0,0 +1,559 @@ +# -*- coding: utf-8 -*- +import unittest +import pytz +from datetime import datetime, timedelta +from parameterized import parameterized +from pprint import pprint +from beem import Steem, exceptions, Hive +from beem.account import Account, extract_account_name +from beem.block import Block +from beem.amount import Amount +from beem.asset import Asset +from beem.utils import formatTimeString +from beem.instance import set_shared_blockchain_instance +from .nodes import get_hive_nodes, get_steem_nodes + +wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" + + +class Testcases(unittest.TestCase): + + @classmethod + def setUpClass(cls): + + cls.bts = Steem( + node=get_steem_nodes(), + nobroadcast=True, + bundle=False, + unsigned=True, + # Overwrite wallet to use this list of wifs only + keys={"active": wif}, + num_retries=10 + ) + cls.account = Account("beembot", steem_instance=cls.bts) + set_shared_blockchain_instance(cls.bts) + + def test_account(self): + stm = self.bts + account = self.account + Account("beembot", steem_instance=stm) + with self.assertRaises( + exceptions.AccountDoesNotExistsException + ): + Account("DoesNotExistsXXX", steem_instance=stm) + # asset = Asset("1.3.0") + # symbol = asset["symbol"] + self.assertEqual(account.name, "beembot") + self.assertEqual(account["name"], account.name) + self.assertIsInstance(account.get_balance("available", "SBD"), Amount) + account.print_info() + # self.assertIsInstance(account.balance({"symbol": symbol}), Amount) + self.assertIsInstance(account.available_balances, list) + self.assertTrue(account.virtual_op_count() > 0) + + # BlockchainObjects method + account.cached = False + self.assertTrue(list(account.items())) + account.cached = False + self.assertIn("id", account) + account.cached = False + # self.assertEqual(account["id"], "1.2.1") + self.assertEqual(str(account), "<Account beembot>") + self.assertIsInstance(Account(account), Account) + + def test_history(self): + account = self.account + zero_element = 0 + h_all_raw = [] + for h in account.history_reverse(raw_output=True): + h_all_raw.append(h) + # h_all_raw = h_all_raw[zero_element:] + zero_element = h_all_raw[-1][0] + h_list = [] + for h in account.history(stop=10, use_block_num=False, 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(start=1, stop=9, use_block_num=False, batch_size=10, 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']) + start = formatTimeString(h_list[0][1]["timestamp"]) + stop = formatTimeString(h_list[-1][1]["timestamp"]) + h_list = [] + for h in account.history(start=start, stop=stop, use_block_num=False, batch_size=10, 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.history_reverse(start=10, stop=0, use_block_num=False, batch_size=10, raw_output=False): + h_list.append(h) + # zero_element = h_list[-1]['index'] + self.assertEqual(h_list[0]['index'], 10) + # self.assertEqual(h_list[-1]['index'], zero_element) + self.assertEqual(h_list[0]['block'], h_all_raw[-11 + zero_element][1]['block']) + self.assertEqual(h_list[-1]['block'], h_all_raw[-1][1]['block']) + h_list = [] + for h in account.history_reverse(start=9, stop=1, use_block_num=False, batch_size=10, 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']) + start = formatTimeString(h_list[0][1]["timestamp"]) + stop = formatTimeString(h_list[-1][1]["timestamp"]) + h_list = [] + for h in account.history_reverse(start=start, stop=stop, use_block_num=False, batch_size=10, raw_output=True): + h_list.append(h) + # self.assertEqual(h_list[0][0], 8) + 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']) + h_list = [] + for h in account.get_account_history(10, 10, use_block_num=False, order=1, 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.get_account_history(10, 10, use_block_num=False, start=1, stop=9, 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']) + start = formatTimeString(h_list[0][1]["timestamp"]) + stop = formatTimeString(h_list[-1][1]["timestamp"]) + h_list = [] + for h in account.get_account_history(10, 10, use_block_num=False, start=start, stop=stop, 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=False, order=-1, 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=False, start=9, stop=1, 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']) + start = formatTimeString(h_list[0][1]["timestamp"]) + stop = formatTimeString(h_list[-1][1]["timestamp"]) + h_list = [] + for h in account.get_account_history(10, 10, start=start, stop=stop, order=-1, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], 9 - i) + + 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']) + + def test_history2(self): + stm = self.bts + account = Account("beembot", steem_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_index(self): + stm = self.bts + account = Account("beembot", steem_instance=stm) + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=10, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + + h_list = [] + for h in account.history(start=1, stop=10, use_block_num=False, batch_size=2, raw_output=True): + h_list.append(h) + for i in range(len(h_list)): + self.assertEqual(h_list[i][0], i + 1) + + def test_history_reverse2(self): + stm = self.bts + account = Account("beembot", steem_instance=stm) + h_list = [] + max_index = account.virtual_op_count() + for h in account.history_reverse(start=max_index, stop=max_index - 4, 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_reverse(start=max_index, stop=max_index - 4, 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_reverse(start=max_index, stop=max_index - 4, 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) + + h_list = [] + for h in account.history_reverse(start=max_index, stop=max_index - 4, 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) + + def test_history_block_num(self): + stm = self.bts + zero_element = 0 + account = Account("beembot", steem_instance=stm) + h_all_raw = [] + for h in account.history_reverse(use_block_num=False, stop=-15, raw_output=True): + h_all_raw.append(h) + h_list = [] + self.assertTrue(len(h_all_raw) > 0) + self.assertTrue(len(h_all_raw[0]) > 1) + self.assertTrue("block" in h_all_raw[0][1]) + for h in account.history(start=h_all_raw[-1][1]["block"], stop=h_all_raw[0][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], h_all_raw[0][0]) + self.assertEqual(h_list[0][1]['block'], h_all_raw[-1][1]['block']) + self.assertEqual(h_list[-1][1]['block'], h_all_raw[0][1]['block']) + h_list = [] + for h in account.history_reverse(start=h_all_raw[0][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[0][0], h_all_raw[0][0]) + self.assertEqual(h_list[0][1]['block'], h_all_raw[0][1]['block']) + self.assertEqual(h_list[-1][1]['block'], h_all_raw[-1][1]['block']) + + def test_account_props(self): + account = self.account + rep = account.get_reputation() + self.assertTrue(isinstance(rep, float)) + vp = account.get_voting_power() + self.assertTrue(vp >= 0) + self.assertTrue(vp <= 100) + sp = account.get_token_power() + self.assertTrue(sp >= 0) + vv = account.get_voting_value_SBD() + self.assertTrue(vv >= 0) + bw = account.get_bandwidth() + # self.assertTrue(bw['used'] <= bw['allocated']) + followers = account.get_followers() + self.assertTrue(isinstance(followers, list)) + following = account.get_following() + self.assertTrue(isinstance(following, list)) + count = account.get_follow_count() + self.assertEqual(count['follower_count'], len(followers)) + self.assertEqual(count['following_count'], len(following)) + + + def test_MissingKeyError(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.convert("1 SBD") + with self.assertRaises( + exceptions.MissingKeyError + ): + tx.sign() + + def test_withdraw_vesting(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.withdraw_vesting("100 VESTS") + self.assertEqual( + (tx["operations"][0][0]), + "withdraw_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["account"]) + + def test_delegate_vesting_shares(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.delegate_vesting_shares("test1", "100 VESTS") + self.assertEqual( + (tx["operations"][0][0]), + "delegate_vesting_shares" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["delegator"]) + + def test_claim_reward_balance(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.claim_reward_balance() + self.assertEqual( + (tx["operations"][0][0]), + "claim_reward_balance" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["account"]) + + def test_cancel_transfer_from_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.cancel_transfer_from_savings(0) + self.assertEqual( + (tx["operations"][0][0]), + "cancel_transfer_from_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer_from_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_from_savings(1, "STEEM", "") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_from_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer_to_savings(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_to_savings(1, "STEEM", "") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_savings" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_convert(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.convert("1 SBD") + self.assertEqual( + (tx["operations"][0][0]), + "convert" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["owner"]) + + def test_proxy(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.setproxy(proxy="gtg") + self.assertEqual( + (tx["operations"][0][0]), + "account_witness_proxy" + ) + op = tx["operations"][0][1] + self.assertIn( + "gtg", + op["proxy"]) + + def test_transfer_to_vesting(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer_to_vesting("1 STEEM") + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + w.blockchain.txbuffer.clear() + tx = w.transfer_to_vesting("1 STEEM", skip_account_check=True) + self.assertEqual( + (tx["operations"][0][0]), + "transfer_to_vesting" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + + def test_transfer(self): + w = self.account + w.blockchain.txbuffer.clear() + tx = w.transfer("beembot", "1", "STEEM") + self.assertEqual( + (tx["operations"][0][0]), + "transfer" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + self.assertIn( + "beembot", + op["to"]) + + w.blockchain.txbuffer.clear() + tx = w.transfer("beembot", "1", "STEEM", skip_account_check=True) + self.assertEqual( + (tx["operations"][0][0]), + "transfer" + ) + op = tx["operations"][0][1] + self.assertIn( + "beembot", + op["from"]) + self.assertIn( + "beembot", + op["to"]) + + def test_json_export(self): + account = Account("beembot", steem_instance=self.bts) + if account.blockchain.rpc.get_use_appbase(): + content = self.bts.rpc.find_accounts({'accounts': [account["name"]]}, api="database")["accounts"][0] + else: + content = self.bts.rpc.get_accounts([account["name"]])[0] + keys = list(content.keys()) + json_content = account.json() + exclude_list = ['owner_challenged', 'average_bandwidth'] # ['json_metadata', 'reputation', 'active_votes', 'savings_sbd_seconds'] + for k in keys: + if k not in exclude_list: + if isinstance(content[k], dict) and isinstance(json_content[k], list): + content_list = [content[k]["amount"], content[k]["precision"], content[k]["nai"]] + self.assertEqual(content_list, json_content[k]) + else: + self.assertEqual(content[k], json_content[k]) + + def test_estimate_virtual_op_num(self): + stm = self.bts + account = Account("gtg", steem_instance=stm) + block_num = 21248120 + block = Block(block_num, steem_instance=stm) + op_num1 = account.estimate_virtual_op_num(block.time(), stop_diff=1, max_count=100) + op_num2 = account.estimate_virtual_op_num(block_num, stop_diff=1, max_count=100) + op_num3 = account.estimate_virtual_op_num(block_num, stop_diff=100, max_count=100) + op_num4 = account.estimate_virtual_op_num(block_num, stop_diff=0.00001, max_count=100) + self.assertTrue(abs(op_num1 - op_num2) < 2) + self.assertTrue(abs(op_num1 - op_num4) < 2) + self.assertTrue(abs(op_num1 - op_num3) < 200) + block_diff1 = 0 + block_diff2 = 0 + for h in account.get_account_history(op_num4 - 1, 0): + block_diff1 = (block_num - h["block"]) + for h in account.get_account_history(op_num4 + 1, 0): + block_diff2 = (block_num - h["block"]) + self.assertTrue(block_diff1 > 0) + self.assertTrue(block_diff2 <= 0) + + def test_estimate_virtual_op_num2(self): + account = self.account + h_all_raw = [] + for h in account.history(raw_output=False): + h_all_raw.append(h) + last_block = h_all_raw[0]["block"] + i = 1 + for op in h_all_raw[1:5]: + new_block = op["block"] + block_num = last_block + int((new_block - last_block) / 2) + op_num = account.estimate_virtual_op_num(block_num, stop_diff=0.1, max_count=100) + if op_num > 0: + op_num -= 1 + self.assertTrue(op_num <= i) + i += 1 + last_block = new_block + + def test_comment_history(self): + account = self.account + comments = [] + for c in account.comment_history(limit=1): + comments.append(c) + self.assertEqual(len(comments), 1) + self.assertEqual(comments[0]["author"], account["name"]) + self.assertTrue(comments[0].is_comment()) + self.assertTrue(comments[0].depth > 0) + + def test_blog_history(self): + account = Account("holger80", steem_instance=self.bts) + posts = [] + for p in account.blog_history(limit=5): + if p["author"] != account["name"]: + continue + posts.append(p) + self.assertTrue(len(posts) >= 1) + self.assertEqual(posts[0]["author"], account["name"]) + self.assertTrue(posts[0].is_main_post()) + self.assertTrue(posts[0].depth == 0) + + def test_reply_history(self): + account = self.account + replies = [] + for r in account.reply_history(limit=1): + replies.append(r) + #self.assertEqual(len(replies), 1) + if len(replies) > 0: + self.assertTrue(replies[0].is_comment()) + self.assertTrue(replies[0].depth > 0) + + def test_get_vote_pct_for_vote_value(self): + account = self.account + for vote_pwr in range(5, 100, 5): + self.assertTrue(9900 <= account.get_vote_pct_for_vote_value(account.get_voting_value(voting_power=vote_pwr), voting_power=vote_pwr) <= 11000) + + def test_extract_account_name(self): + stm = self.bts + account = Account("holger80", steem_instance=stm) + self.assertEqual(extract_account_name(account), "holger80") + self.assertEqual(extract_account_name("holger80"), "holger80") + self.assertEqual(extract_account_name({"name": "holger80"}), "holger80") + self.assertEqual(extract_account_name(""), "") -- GitLab