From 0f18230e299e18bd860bdff6ab30f6fa636a5e1e Mon Sep 17 00:00:00 2001 From: Holger Nahrstaedt <holgernahrstaedt@gmx.de> Date: Fri, 12 Mar 2021 21:42:09 +0100 Subject: [PATCH] Add Blocks --- .circleci/config.yml | 4 ++ CHANGELOG.rst | 4 ++ beem/block.py | 37 ++++++++++++ beem/cli.py | 5 +- beem/utils.py | 40 +++++++++++++ examples/blockactivity.py | 7 ++- examples/blockstats.py | 115 ++++++++++++++++++++++++++++++++++++++ tests/beem/test_utils.py | 13 ++++- 8 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 examples/blockstats.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 7aebe02c..b97793a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,6 +34,7 @@ jobs: - run: name: install dependencies command: | + sudo python -m pip install --upgrade pip sudo python -m pip install -r requirements-test.txt sudo python -m pip install --upgrade secp256k1prp @@ -71,6 +72,7 @@ jobs: - run: name: install dependencies command: | + sudo python -m pip install --upgrade pip sudo python -m pip install --upgrade -r requirements-test.txt - run: @@ -93,6 +95,7 @@ jobs: - run: name: install dependencies command: | + sudo python -m pip install --upgrade pip sudo python -m pip install --upgrade -r requirements-test.txt - run: @@ -115,6 +118,7 @@ jobs: - run: name: install dependencies command: | + sudo python -m pip install --upgrade pip sudo python -m pip install --upgrade -r requirements-test.txt - run: name: run tests diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2e50b032..3ddb2681 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,11 @@ Changelog ------- * Fix bug in ecda (convert mpz into int when not supported) * add coinactivity example script +* add blockstats example script +* Add Blocks class, which uses get_block_range * PR #272: correct blockchain virtual op batch calls (thanks to @crokkon) +* PR #276: blockchain: get_account_reputations fix for first (thanks to @crokkon) +* PR #287: beempy witnessproperties: fix interest rate options 0.24.20 ------- diff --git a/beem/block.py b/beem/block.py index dd09ec78..91f95324 100644 --- a/beem/block.py +++ b/beem/block.py @@ -4,6 +4,7 @@ import json from .exceptions import BlockDoesNotExistsException from .utils import parse_time, formatTimeString from .blockchainobject import BlockchainObject +from beem.instance import shared_blockchain_instance from beemapi.exceptions import ApiNotSupported from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type @@ -73,6 +74,8 @@ class Block(BlockchainObject): blockchain_instance=blockchain_instance, **kwargs ) + if self.identifier is None: + self.identifier = self.block_num def _parse_json_data(self, block): parse_times = [ @@ -381,3 +384,37 @@ class BlockHeader(BlockchainObject): else: output[p] = p_date return json.loads(str(json.dumps(output))) + + +class Blocks(list): + """ Obtain a list of blocks + + :param list name_list: list of accounts to fetch + :param int count: (optional) maximum number of accounts + to fetch per call, defaults to 100 + :param Steem/Hive blockchain_instance: Steem() or Hive() instance to use when + accessing a RPCcreator = Account(creator, blockchain_instance=self) + """ + def __init__(self, starting_block_num, count=1000, lazy=False, full=True, blockchain_instance=None, **kwargs): + + if blockchain_instance is None: + if kwargs.get("steem_instance"): + blockchain_instance = kwargs["steem_instance"] + elif kwargs.get("hive_instance"): + blockchain_instance = kwargs["hive_instance"] + self.blockchain = blockchain_instance or shared_blockchain_instance() + + if not self.blockchain.is_connected(): + return + blocks = [] + + self.blockchain.rpc.set_next_node_on_empty_reply(False) + + blocks = self.blockchain.rpc.get_block_range({'starting_block_num': starting_block_num, "count": count}, api="block")['blocks'] + + super(Blocks, self).__init__( + [ + Block(x, lazy=lazy, full=full, blockchain_instance=self.blockchain) + for x in blocks + ] + ) diff --git a/beem/cli.py b/beem/cli.py index 96b5cefe..e0236436 100644 --- a/beem/cli.py +++ b/beem/cli.py @@ -2602,7 +2602,7 @@ def createpost(markdown_file, account, title, tags, community, beneficiaries, pe stm = shared_blockchain_instance() if stm.rpc is not None: stm.rpc.rpcconnect() - yaml_prefix = '---\n' + if account is None: account = input("author: ") if title is None: @@ -2626,7 +2626,6 @@ def createpost(markdown_file, account, title, tags, community, beneficiaries, pe if int(index) - 1 >= len(comm_cand): continue community = comm_cand[int(index) - 1] - ret = input("Selected community: %s - %s [yes/no]? " % (community["name"], community["title"])) if ret in ["y", "yes"]: community_found = True @@ -2655,7 +2654,7 @@ def createpost(markdown_file, account, title, tags, community, beneficiaries, pe if max_accepted_payout is None: max_accepted_payout = input("max accepted payout [return to skip]: ") - + yaml_prefix = '---\n' yaml_prefix += 'title: "%s"\n' % title yaml_prefix += 'author: %s\n' % account yaml_prefix += 'tags: %s\n' % tags diff --git a/beem/utils.py b/beem/utils.py index c7176270..700d5d89 100644 --- a/beem/utils.py +++ b/beem/utils.py @@ -381,6 +381,46 @@ def seperate_yaml_dict_from_body(content): body = content return body, parameter + +def create_yaml_header(comment, json_metadata={}, reply_identifier=None): + yaml_prefix = '---\n' + if comment["title"] != "": + yaml_prefix += 'title: "%s"\n' % comment["title"] + if "permlink" in comment: + yaml_prefix += 'permlink: %s\n' % comment["permlink"] + yaml_prefix += 'author: %s\n' % comment["author"] + if "author" in json_metadata: + yaml_prefix += 'authored by: %s\n' % json_metadata["author"] + if "description" in json_metadata: + yaml_prefix += 'description: "%s"\n' % json_metadata["description"] + if "canonical_url" in json_metadata: + yaml_prefix += 'canonical_url: %s\n' % json_metadata["canonical_url"] + if "app" in json_metadata: + yaml_prefix += 'app: %s\n' % json_metadata["app"] + if "last_update" in comment: + yaml_prefix += 'last_update: %s\n' % comment["last_update"] + elif "updated" in comment: + yaml_prefix += 'last_update: %s\n' % comment["updated"] + yaml_prefix += 'max_accepted_payout: %s\n' % str(comment["max_accepted_payout"]) + if "percent_steem_dollars" in comment: + yaml_prefix += 'percent_steem_dollars: %s\n' % str(comment["percent_steem_dollars"]) + elif "percent_hbd" in comment: + yaml_prefix += 'percent_hbd: %s\n' % str(comment["percent_hbd"]) + if "tags" in json_metadata: + if len(json_metadata["tags"]) > 0 and comment["category"] != json_metadata["tags"][0] and len(comment["category"]) > 0: + yaml_prefix += 'community: %s\n' % comment["category"] + yaml_prefix += 'tags: %s\n' % ",".join(json_metadata["tags"]) + if "beneficiaries" in comment: + beneficiaries = [] + for b in comment["beneficiaries"]: + beneficiaries.append("%s:%.2f%%" % (b["account"], b["weight"] / 10000 * 100)) + if len(beneficiaries) > 0: + yaml_prefix += 'beneficiaries: %s\n' % ",".join(beneficiaries) + if reply_identifier is not None: + yaml_prefix += 'reply_identifier: %s\n' % reply_identifier + yaml_prefix += '---\n' + return yaml_prefix + def load_dirty_json(dirty_json): regex_replace = [(r"([ \{,:\[])(u)?'([^']+)'", r'\1"\3"'), (r" False([, \}\]])", r' false\1'), (r" True([, \}\]])", r' true\1')] diff --git a/examples/blockactivity.py b/examples/blockactivity.py index 972a98f1..5902ec5c 100644 --- a/examples/blockactivity.py +++ b/examples/blockactivity.py @@ -59,7 +59,8 @@ def main(args=None): total_ops = 0 total_virtual_ops = 0 total_trx = 0 - blocksperday = 20 * 60 * 24 + duration_s = 60 * 60 * 24 + blocksperday = int(duration_s / 3) blockchain = Blockchain(blockchain_instance=blk_inst, ) current_block_num = blockchain.get_current_block_num() @@ -67,7 +68,7 @@ def main(args=None): last_block = Block(last_block_id, blockchain_instance=blk_inst) - stopTime = last_block.time() + timedelta(seconds=60 * 60 * 24) + stopTime = last_block.time() + timedelta(seconds=duration_s) start = timer() for entry in blockchain.blocks(start=last_block_id, max_batch_size=max_batch_size, threading=threading, thread_num=thread_num): @@ -93,7 +94,7 @@ def main(args=None): duration = timer() - start - stopTime = last_block.time() + timedelta(seconds=60 * 60 * 24) + stopTime = last_block.time() + timedelta(seconds=duration_s) start = timer() for entry in blockchain.blocks(start=last_block_id, max_batch_size=max_batch_size, threading=threading, thread_num=thread_num, only_virtual_ops=True): block_time = entry["timestamp"] diff --git a/examples/blockstats.py b/examples/blockstats.py new file mode 100644 index 00000000..6279c107 --- /dev/null +++ b/examples/blockstats.py @@ -0,0 +1,115 @@ +import sys +from datetime import datetime, timedelta +from prettytable import PrettyTable +import argparse +from timeit import default_timer as timer +import logging +from beem.blockchain import Blockchain +from beem.block import Block +from beem import Hive, Blurt, Steem +from beem.utils import parse_time +from beem.nodelist import NodeList + +log = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + + +def parse_args(args=None): + d = 'Show op type stats for either hive, blurt or steem.' + parser = argparse.ArgumentParser(description=d) + parser.add_argument('blockchain', type=str, nargs='?', + default=sys.stdin, + help='Blockchain (hive, blurt or steem)') + return parser.parse_args(args) + + +def main(args=None): + + args = parse_args(args) + blockchain = args.blockchain + + nodelist = NodeList() + nodelist.update_nodes(weights={"block": 1}) + + if blockchain == "hive" or blockchain is None: + max_batch_size = 50 + threading = False + thread_num = 16 + block_debug = 1000 + + nodes = nodelist.get_hive_nodes() + blk_inst = Hive(node=nodes, num_retries=3, num_retries_call=3, timeout=30) + elif blockchain == "blurt": + max_batch_size = None + threading = False + thread_num = 8 + block_debug = 20 + nodes = ["https://rpc.blurt.buzz/", "https://api.blurt.blog", "https://rpc.blurtworld.com", "https://rpc.blurtworld.com"] + blk_inst = Blurt(node=nodes, num_retries=3, num_retries_call=3, timeout=30) + elif blockchain == "steem": + max_batch_size = 50 + threading = False + thread_num = 16 + block_debug = 1000 + nodes = nodelist.get_steem_nodes() + blk_inst = Steem(node=nodes, num_retries=3, num_retries_call=3, timeout=30) + else: + raise Exception("Wrong parameter, can be hive, blurt or steem") + print(blk_inst) + block_count = 0 + total_ops = 0 + total_trx = 0 + duration_s = 60 * 60 * 1 + blocksperday = int(duration_s / 3) + + blockchain = Blockchain(blockchain_instance=blk_inst, ) + current_block_num = blockchain.get_current_block_num() + last_block_id = current_block_num - blocksperday + + last_block = Block(last_block_id, blockchain_instance=blk_inst) + + stopTime = last_block.time() + timedelta(seconds=duration_s) + + start = timer() + op_stats = {} + for entry in blockchain.blocks(start=last_block_id, max_batch_size=max_batch_size, threading=threading, thread_num=thread_num): + if "block" in entry: + block_time = parse_time(entry["block"]["timestamp"]) + else: + block_time = entry["timestamp"] + if block_time > stopTime: + break + block_count += 1 + if "block" in entry: + trxs = entry["block"]["transactions"] + else: + trxs = entry["transactions"] + for tx in trxs: + total_trx += 1 + for op in tx["operations"]: + if "_operation" in op["type"]: + op_type = op["type"][:-10] + else: + op_type = op["type"] + if op_type in op_stats: + op_stats[op_type] += 1 + else: + op_stats[op_type] = 1 + total_ops += 1 + + ops_per_day = total_ops / block_count * blocksperday + if block_count % (block_debug) == 0: + print("%d blocks remaining... estimated ops per day: %.1f" % (blocksperday - block_count, ops_per_day)) + + duration = timer() - start + t = PrettyTable(["Type", "Count", "percentage"]) + t.align = "l" + op_list = [] + for o in op_stats: + op_list.append({"type": o, "n": op_stats[o], "perc": op_stats[o] / total_ops * 100}) + op_list_sorted = sorted(op_list, key=lambda x: x['n'], reverse=True) + for op in op_list_sorted: + t.add_row([op["type"], op["n"], "%.2f %%" % op["perc"]]) + print(t) +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/beem/test_utils.py b/tests/beem/test_utils.py index b6b552f8..84719414 100644 --- a/tests/beem/test_utils.py +++ b/tests/beem/test_utils.py @@ -2,6 +2,7 @@ import unittest from datetime import datetime, date, timedelta import os +from ruamel.yaml import YAML from beem.utils import ( formatTimedelta, assets_from_string, @@ -23,7 +24,8 @@ from beem.utils import ( create_new_password, generate_password, import_coldcard_wif, - import_pubkeys + import_pubkeys, + create_yaml_header ) @@ -181,6 +183,15 @@ class Testcases(unittest.TestCase): self.assertEqual(par, {"par1": "data1", "par2": "data2", "par3": 3}) self.assertEqual(body, " test ---") + def create_yaml_header(self): + comment = {"title": "test", "author": "holger80", "max_accepted_payout": 100} + yaml_content = create_yaml_header(comment) + yaml_safe = YAML(typ="safe") + parameter = yaml_safe.load(yaml_content) + self.assertEqual(parameter["title"], "test") + self.assertEqual(parameter["author"], "holger80") + self.assertEqual(parameter["max_accepted_payout"], "100") + def test_create_new_password(self): new_password = create_new_password() self.assertEqual(len(new_password), 32) -- GitLab