From 8fef5541ef22cbd53f5629f6fddf7faee70f1d31 Mon Sep 17 00:00:00 2001
From: Holger Nahrstaedt <holger@nahrstaedt.de>
Date: Mon, 7 May 2018 17:44:11 +0200
Subject: [PATCH] Virtual-Ops support added for block and blockchain. Comment
 improved. More CLI functions.

Account
* estimate_account_op renamed to estimate_virtual_op_num
Block
* only_ops and only_virtual_ops added as new parameter
* the transactions property returns a list of transactions
* The operations property returns a list of operations
* Block which contain only only_ops can be received now
Blockchain
* only_ops and only_virtual_ops added to get_current_block, blocks, wait_for_and_get_block and stream
* ops() is obsolete now
* stream uses now blocks internally
* only_ops=True streams now also virtual operaitons
cli
* autoconnect is the to False now and only when needed a  stm.rpc.rpcconnect() is performed
* ascii option added to all plots
* rewards added, this new command lists outstanding rewards (posts, comment and votes)
* curation added which shows the vote curation rewards for a single post
* verify added, which returns the public signing key of a transaction
Comment
* reward, is_pending, time_elapsed, curation_penalty_compensation_SBD, estimate_curation_SBD, curation_reward_pct, get_vote, get_beneficiaries_pct, get_rewards, get_author_rewards and get_curation_rewards added

Unit tests
* new function added and tests adapted to changes
---
 beem/account.py                         |  60 ++--
 beem/block.py                           |  78 ++++-
 beem/blockchain.py                      | 219 +++++++------
 beem/cli.py                             | 398 ++++++++++++++++++++++--
 beem/comment.py                         | 238 +++++++++++++-
 beemgraphenebase/signedtransactions.py  |   2 +-
 examples/memory_profiler1.py            |   2 +-
 tests/beem/test_block.py                |  26 +-
 tests/beem/test_blockchain.py           |  33 +-
 tests/beem/test_blockchain_batch.py     |   4 +-
 tests/beem/test_blockchain_threading.py |   3 +-
 tests/beem/test_cli.py                  |  21 ++
 12 files changed, 910 insertions(+), 174 deletions(-)

diff --git a/beem/account.py b/beem/account.py
index 7f546327..f17dba22 100644
--- a/beem/account.py
+++ b/beem/account.py
@@ -811,7 +811,7 @@ class Account(BlockchainObject):
             :rtype: list
         """
         if until is not None:
-            return self.estimate_account_op(until, op_accuracy=1)
+            return self.estimate_virtual_op_num(until, accuracy=1)
         else:
             try:
                 op_count = 0
@@ -837,12 +837,14 @@ class Account(BlockchainObject):
             ret = self.steem.rpc.get_account_history(account["name"], start, limit, api="database")
         return ret
 
-    def estimate_account_op(self, start, op_accuracy=10, max_count=10, reverse=False):
-        """ Returns a estiamtion of account operation index for a given time or blockindex
+    def estimate_virtual_op_num(self, blocktime, accuracy=10, max_count=-1, reverse=False):
+        """ Returns an estimation of an virtual operation index for a given time or blockindex
 
-            :param int/datetime start: start time or start block index from which account
+            :param int/datetime blocktime: start time or start block index from which account
                 operation should be fetched
-            :param int op_accuracy: defines the estimation accuracy (default 10)
+            :param int accuracy: defines the estimation accuracy (default 10)
+            :param int max_count: sets the maximum number of iterations. -1 disables this (default -1)
+            :param bool revers: Set to true when used in history_reverse (default is False)
 
             Example:::
 
@@ -853,43 +855,43 @@ class Account(BlockchainObject):
                 utc = pytz.timezone('UTC')
                 start_time = utc.localize(datetime.utcnow()) - timedelta(days=7)
                 acc = Account("gtg")
-                start_op = acc.estimate_account_op(start_time)
+                start_op = acc.estimate_virtual_op_num(start_time)
 
                 b = Blockchain()
                 start_block_num = b.get_estimated_block_num(start_time)
-                start_op2 = acc.estimate_account_op(start_block_num)
+                start_op2 = acc.estimate_virtual_op_num(start_block_num)
         """
         max_index = self.virtual_op_count()
         created = self["created"]
-        if not isinstance(start, datetime):
+        if not isinstance(blocktime, datetime):
             b = Blockchain(steem_instance=self.steem)
             current_block_num = b.get_current_block_num()
             created_blocknum = b.get_estimated_block_num(created, accurate=True)
-            if start < created_blocknum and not reverse:
+            if blocktime < created_blocknum and not reverse:
                 return 0
-            elif start < created_blocknum:
+            elif blocktime < created_blocknum:
                 return max_index
         else:
-            if start < created and not reverse:
+            if blocktime < created and not reverse:
                 return 0
-            elif start < created:
+            elif blocktime < created:
                 return max_index
-        if max_index < op_accuracy and not reverse:
+        if max_index < accuracy and not reverse:
             return 0
-        elif max_index < op_accuracy:
+        elif max_index < accuracy:
             return max_index
-        if isinstance(start, datetime):
+        if isinstance(blocktime, datetime):
             utc = pytz.timezone('UTC')
             now = utc.localize(datetime.utcnow())
             account_lifespan_sec = (now - created).total_seconds()
-            start = addTzInfo(start)
-            estimated_op_num = int((start - created).total_seconds() / account_lifespan_sec * max_index)
+            blocktime = addTzInfo(blocktime)
+            estimated_op_num = int((blocktime - created).total_seconds() / account_lifespan_sec * max_index)
         else:
             account_lifespan_block = (current_block_num - created_blocknum)
-            estimated_op_num = int((start - created_blocknum) / account_lifespan_block * max_index)
-        op_diff = op_accuracy + 1
+            estimated_op_num = int((blocktime - created_blocknum) / account_lifespan_block * max_index)
+        op_diff = accuracy + 1
         cnt = 0
-        while op_diff > op_accuracy and cnt < max_count:
+        while op_diff > accuracy and (max_count < 0 or cnt < max_count):
             op_start = self._get_account_history(start=estimated_op_num)
             if isinstance(op_start, list) and len(op_start) > 0 and len(op_start[0]) > 0:
                 trx = op_start[0][1]
@@ -898,12 +900,12 @@ class Account(BlockchainObject):
                 return 0
             else:
                 return max_index
-            if isinstance(start, datetime):
+            if isinstance(blocktime, datetime):
                 diff_time = (now - formatTimeString(trx["timestamp"])).total_seconds()
-                op_diff = ((start - formatTimeString(trx["timestamp"])).total_seconds() / diff_time * (max_index - estimated_op_num))
+                op_diff = ((blocktime - formatTimeString(trx["timestamp"])).total_seconds() / diff_time * (max_index - estimated_op_num))
             else:
                 diff_block = (current_block_num - trx["block"])
-                op_diff = ((start - trx["block"]) / diff_block * (max_index - estimated_op_num))
+                op_diff = ((blocktime - trx["block"]) / diff_block * (max_index - estimated_op_num))
             if reverse:
                 estimated_op_num += math.ceil(op_diff)
             else:
@@ -917,10 +919,10 @@ class Account(BlockchainObject):
             return estimated_op_num
         elif int(op_diff) == 0 and not reverse:
             return estimated_op_num
-        elif estimated_op_num > op_accuracy and not reverse:
-            return estimated_op_num - op_accuracy
-        elif estimated_op_num + op_accuracy < max_index:
-            return estimated_op_num + op_accuracy
+        elif estimated_op_num > accuracy and not reverse:
+            return estimated_op_num - accuracy
+        elif estimated_op_num + accuracy < max_index:
+            return estimated_op_num + accuracy
         elif reverse:
             return max_index
         else:
@@ -1141,7 +1143,7 @@ class Account(BlockchainObject):
         if start is not None and not use_block_num and not isinstance(start, datetime):
             start_index = start
         elif start is not None:
-            start_index = self.estimate_account_op(start, op_accuracy=10)
+            start_index = self.estimate_virtual_op_num(start, accuracy=10)
         else:
             start_index = 0
 
@@ -1283,7 +1285,7 @@ class Account(BlockchainObject):
         elif start is not None and isinstance(start, int) and not use_block_num:
             first = start
         elif start is not None:
-            first = self.estimate_account_op(start, op_accuracy=10, reverse=True)
+            first = self.estimate_virtual_op_num(start, accuracy=10, reverse=True)
         if stop is not None and isinstance(stop, int) and stop < 0 and not use_block_num:
             stop += first
         start = addTzInfo(start)
diff --git a/beem/block.py b/beem/block.py
index b6b89869..568a87c5 100644
--- a/beem/block.py
+++ b/beem/block.py
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
 from .exceptions import BlockDoesNotExistsException
 from .utils import parse_time
 from .blockchainobject import BlockchainObject
+from beemapi.exceptions import ApiNotSupported
 
 
 class Block(BlockchainObject):
@@ -15,6 +16,8 @@ class Block(BlockchainObject):
         :param beem.steem.Steem steem_instance: Steem
             instance
         :param bool lazy: Use lazy loading
+        :param bool only_ops: Includes only operations, when set to True (default: False)
+        :param bool only_virtual_ops: Includes only virtual operations (default: False)
 
         Instances of this class are dictionaries that come with additional
         methods (see below) that allow dealing with a block and it's
@@ -34,6 +37,34 @@ class Block(BlockchainObject):
                   refreshed with ``Account.refresh()``.
 
     """
+    def __init__(
+        self,
+        block,
+        only_ops=False,
+        only_virtual_ops=False,
+        full=True,
+        lazy=False,
+        steem_instance=None
+    ):
+        """ Initilize a block
+
+            :param int block: block number
+            :param beem.steem.Steem steem_instance: Steem
+                instance
+            :param bool lazy: Use lazy loading
+            :param bool only_ops: Includes only operations, when set to True (default: False)
+            :param bool only_virtual_ops: Includes only virtual operations (default: False)
+
+        """
+        self.full = full
+        self.only_ops = only_ops
+        self.only_virtual_ops = only_virtual_ops
+        super(Block, self).__init__(
+            block,
+            lazy=lazy,
+            full=full,
+            steem_instance=steem_instance
+        )
 
     def refresh(self):
         """ Even though blocks never change, you freshly obtain its contents
@@ -41,12 +72,24 @@ class Block(BlockchainObject):
         """
         if not isinstance(self.identifier, int):
             self.identifier = int(self.identifier)
-        if self.steem.rpc.get_use_appbase():
-            block = self.steem.rpc.get_block({"block_num": self.identifier}, api="block")
-            if block and "block" in block:
-                block = block["block"]
+        if self.only_ops:
+            if self.steem.rpc.get_use_appbase():
+                try:
+                    ops = self.steem.rpc.get_ops_in_block({"block_num": self.identifier, 'only_virtual': self.only_virtual_ops}, api="account_history")["ops"]
+                except ApiNotSupported:
+                    ops = self.steem.rpc.get_ops_in_block(self.identifier, self.only_virtual_ops)
+            else:
+                ops = self.steem.rpc.get_ops_in_block(self.identifier, self.only_virtual_ops)
+            block = {'block': ops[0]["block"],
+                     'timestamp': ops[0]["timestamp"],
+                     'operations': ops}
         else:
-            block = self.steem.rpc.get_block(self.identifier)
+            if self.steem.rpc.get_use_appbase():
+                block = self.steem.rpc.get_block({"block_num": self.identifier}, api="block")
+                if block and "block" in block:
+                    block = block["block"]
+            else:
+                block = self.steem.rpc.get_block(self.identifier)
         if not block:
             raise BlockDoesNotExistsException(str(self.identifier))
         super(Block, self).__init__(block, steem_instance=self.steem)
@@ -60,8 +103,25 @@ class Block(BlockchainObject):
         """Return a datatime instance for the timestamp of this block"""
         return parse_time(self['timestamp'])
 
-    def ops(self):
-        """Returns all block operations"""
+    @property
+    def transactions(self):
+        """ Returns all transactions as list"""
+        if self.only_ops:
+            return None
+        trxs = self["transactions"]
+        for i in range(len(trxs)):
+            trx = trxs[i]
+            trx['transaction_id'] = self['transaction_ids'][i]
+            trx['block_num'] = self.block_num
+            trx['transaction_num'] = i
+            trxs[i] = trx
+        return trxs
+
+    @property
+    def operations(self):
+        """Returns all block operations as list"""
+        if self.only_ops:
+            return self["operations"]
         ops = []
         trxs = self["transactions"]
         for tx in trxs:
@@ -80,6 +140,10 @@ class Block(BlockchainObject):
                 ops_stat[key] = 0
         else:
             ops_stat = add_to_ops_stat.copy()
+        if self.only_ops:
+            for op in self["operations"]:
+                ops_stat[op["op"][0]] += 1
+            return ops_stat
         trxs = self["transactions"]
         for tx in trxs:
             for op in tx["operations"]:
diff --git a/beem/blockchain.py b/beem/blockchain.py
index 23dac556..5cc677bc 100644
--- a/beem/blockchain.py
+++ b/beem/blockchain.py
@@ -12,8 +12,9 @@ import hashlib
 import json
 import math
 from datetime import datetime, timedelta
+from .utils import formatTimeString
 from .block import Block
-from .exceptions import BatchedCallsNotSupported
+from .exceptions import BatchedCallsNotSupported, BlockDoesNotExistsException
 from .blockchainobject import BlockchainObject
 from beemgraphenebase.py23 import py23_bytes
 from beem.instance import shared_steem_instance
@@ -96,10 +97,22 @@ class Blockchain(object):
             self.max_block_wait_repetition = max_block_wait_repetition
         else:
             self.max_block_wait_repetition = 3
+        self.block_interval = self.steem.get_block_interval()
 
     def is_irreversible_mode(self):
         return self.mode == 'last_irreversible_block_num'
 
+    def get_transaction(self, transaction_id):
+        """ Returns a transaction from the blockchain
+
+            :param str transaction_id: transaction_id
+        """
+        if self.steem.rpc.get_use_appbase():
+            ret = self.steem.rpc.get_transaction({'id': transaction_id}, api="database")
+        else:
+            ret = self.steem.rpc.get_transaction(transaction_id, api="database")
+        return ret
+
     def get_current_block_num(self):
         """ This call returns the current block number
 
@@ -109,14 +122,19 @@ class Blockchain(object):
         props = self.steem.get_dynamic_global_properties(False)
         return int(props.get(self.mode))
 
-    def get_current_block(self):
+    def get_current_block(self, only_ops=False, only_virtual_ops=False):
         """ This call returns the current block
 
+            :param bool only_ops: Returns block with operations only, when set to True (default: False)
+            :param bool only_virtual_ops: Includes only virtual operations (default: False)
+
             .. note:: The block number returned depends on the ``mode`` used
                       when instanciating from this class.
         """
         return Block(
             self.get_current_block_num(),
+            only_ops=only_ops,
+            only_virtual_ops=only_virtual_ops,
             steem_instance=self.steem
         )
 
@@ -128,25 +146,24 @@ class Blockchain(object):
             .. note:: The block number returned depends on the ``mode`` used
                       when instanciating from this class.
         """
-        block_time_seconds = self.steem.get_block_interval()
         last_block = self.get_current_block()
         if estimateForwards:
             block_offset = 10
             first_block = Block(block_offset, steem_instance=self.steem)
             time_diff = date - first_block.time()
-            block_number = math.floor(time_diff.total_seconds() / block_time_seconds + block_offset)
+            block_number = math.floor(time_diff.total_seconds() / self.block_interval + block_offset)
         else:
             time_diff = last_block.time() - date
-            block_number = math.floor(last_block.identifier - time_diff.total_seconds() / block_time_seconds)
+            block_number = math.floor(last_block.identifier - time_diff.total_seconds() / self.block_interval)
 
         if accurate:
             if block_number > last_block.identifier:
                 block_number = last_block.identifier
             block_time_diff = timedelta(seconds=10)
-            while block_time_diff.total_seconds() > block_time_seconds or block_time_diff.total_seconds() < -block_time_seconds:
+            while block_time_diff.total_seconds() > self.block_interval or block_time_diff.total_seconds() < -self.block_interval:
                 block = Block(block_number, steem_instance=self.steem)
                 block_time_diff = date - block.time()
-                delta = block_time_diff.total_seconds() // block_time_seconds
+                delta = block_time_diff.total_seconds() // self.block_interval
                 if delta == 0 and block_time_diff.total_seconds() < 0:
                     delta = -1
                 elif delta == 0 and block_time_diff.total_seconds() > 0:
@@ -180,21 +197,29 @@ class Blockchain(object):
         ).time()
         return int(time.mktime(block_time.timetuple()))
 
-    def blocks(self, start=None, stop=None, max_batch_size=None, threading=False, thread_num=8):
+    def blocks(self, start=None, stop=None, max_batch_size=None, threading=False, thread_num=8, only_ops=False, only_virtual_ops=False):
         """ Yields blocks starting from ``start``.
 
             :param int start: Starting block
             :param int stop: Stop at this block
-            :param str mode: We here have the choice between
-             "head" (the last block) and "irreversible" (the block that is
-             confirmed by 2/3 of all block producers and is thus irreversible)
+            :param int max_batch_size: only for appbase nodes. When not None, batch calls of are used.
+                Cannot combine with threading
+            :param bool threading: Enables threading. Cannot be combined with batch calls
+            :param int thread_num: Defines the number of threads, when `threading` is set.
+            :param bool only_ops: Only yielding operations, when set to True (default: False)
+            :param bool only_virtual_ops: Only yield virtual operations (default: False)
+
+            .. note:: If you want instant confirmation, you need to instantiate
+                      class:`beem.blockchain.Blockchain` with
+                      ``mode="head"``, otherwise, the call will wait until
+                      confirmed in an irreversible block.
+
         """
         # Let's find out how often blocks are generated!
-        self.block_interval = self.steem.get_block_interval()
         current_block_num = self.get_current_block_num()
         if not start:
             start = current_block_num
-
+        head_block_reached = False
         # We are going to loop indefinitely
         while True:
 
@@ -204,14 +229,14 @@ class Blockchain(object):
             else:
                 current_block_num = self.get_current_block_num()
                 head_block = current_block_num
-            if threading and FUTURES_MODULE:
+            if threading and FUTURES_MODULE and not head_block_reached:
                 pool = ThreadPoolExecutor(max_workers=thread_num + 1)
                 latest_block = 0
                 for blocknum in range(start, head_block + 1, thread_num):
                     futures = []
                     i = blocknum
                     while i < blocknum + thread_num and i <= head_block:
-                        futures.append(pool.submit(Block, i, steem_instance=self.steem))
+                        futures.append(pool.submit(Block, i, only_ops=only_ops, only_virtual_ops=only_virtual_ops, steem_instance=self.steem))
                         i += 1
                     results = [r.result() for r in as_completed(futures)]
                     block_nums = []
@@ -225,9 +250,9 @@ class Blockchain(object):
                         yield b
                 if latest_block < head_block:
                     for blocknum in range(latest_block, head_block + 1):
-                        block = Block(blocknum, steem_instance=self.steem)
+                        block = Block(blocknum, only_ops=only_ops, only_virtual_ops=only_virtual_ops, steem_instance=self.steem)
                         yield block
-            elif max_batch_size is not None and (head_block - start) >= max_batch_size:
+            elif max_batch_size is not None and (head_block - start) >= max_batch_size and not head_block_reached:
                 latest_block = start - 1
                 batches = max_batch_size
                 for blocknumblock in range(start, head_block + 1, batches):
@@ -235,18 +260,32 @@ class Blockchain(object):
                     if (head_block - blocknumblock) < batches:
                         batches = head_block - blocknumblock + 1
                     for blocknum in range(blocknumblock, blocknumblock + batches - 1):
-                        if self.steem.rpc.get_use_appbase():
-                            self.steem.rpc.get_block({"block_num": blocknum}, api="block", add_to_queue=True)
+                        if only_virtual_ops:
+                            if self.steem.rpc.get_use_appbase():
+                                # self.steem.rpc.get_ops_in_block({"block_num": blocknum, 'only_virtual': only_virtual_ops}, api="account_history", add_to_queue=True)
+                                self.steem.rpc.get_ops_in_block(blocknum, only_virtual_ops, add_to_queue=True)
+                            else:
+                                self.steem.rpc.get_ops_in_block(blocknum, only_virtual_ops, add_to_queue=True)
                         else:
-                            self.steem.rpc.get_block(blocknum, add_to_queue=True)
+                            if self.steem.rpc.get_use_appbase():
+                                self.steem.rpc.get_block({"block_num": blocknum}, api="block", add_to_queue=True)
+                            else:
+                                self.steem.rpc.get_block(blocknum, add_to_queue=True)
                         latest_block = blocknum
                     if batches >= 1:
                         latest_block += 1
                     if latest_block <= head_block:
-                        if self.steem.rpc.get_use_appbase():
-                            block_batch = self.steem.rpc.get_block({"block_num": latest_block}, api="block", add_to_queue=False)
+                        if only_virtual_ops:
+                            if self.steem.rpc.get_use_appbase():
+                                # self.steem.rpc.get_ops_in_block({"block_num": blocknum, 'only_virtual': only_virtual_ops}, api="account_history", add_to_queue=False)
+                                block_batch = self.steem.rpc.get_ops_in_block(blocknum, only_virtual_ops, add_to_queue=False)
+                            else:
+                                block_batch = self.steem.rpc.get_ops_in_block(blocknum, only_virtual_ops, add_to_queue=False)
                         else:
-                            block_batch = self.steem.rpc.get_block(latest_block, add_to_queue=False)
+                            if self.steem.rpc.get_use_appbase():
+                                block_batch = self.steem.rpc.get_block({"block_num": latest_block}, api="block", add_to_queue=False)
+                            else:
+                                block_batch = self.steem.rpc.get_block(latest_block, add_to_queue=False)
                         if not bool(block_batch):
                             raise BatchedCallsNotSupported()
                         blocknum = latest_block - len(block_batch) + 1
@@ -254,18 +293,22 @@ class Blockchain(object):
                             block_batch = [block_batch]
                         for block in block_batch:
                             if self.steem.rpc.get_use_appbase():
-                                block = block["block"]
+                                if only_virtual_ops:
+                                    block = block["ops"]
+                                else:
+                                    block = block["block"]
                             block["id"] = blocknum
-                            yield Block(block, steem_instance=self.steem)
+                            yield Block(block, only_ops=only_ops, only_virtual_ops=only_virtual_ops, steem_instance=self.steem)
                             blocknum += 1
             else:
                 # Blocks from start until head block
                 for blocknum in range(start, head_block + 1):
                     # Get full block
-                    block = self.wait_for_and_get_block(blocknum, last_fetched_block_num=current_block_num)
+                    block = self.wait_for_and_get_block(blocknum, only_ops=only_ops, only_virtual_ops=only_virtual_ops)
                     yield block
             # Set new start
             start = head_block + 1
+            head_block_reached = True
 
             if stop and start > stop:
                 # raise StopIteration
@@ -274,18 +317,20 @@ class Blockchain(object):
             # Sleep for one block
             time.sleep(self.block_interval)
 
-    def wait_for_and_get_block(self, block_number, blocks_waiting_for=None, last_fetched_block_num=None):
+    def wait_for_and_get_block(self, block_number, blocks_waiting_for=None, only_ops=False, only_virtual_ops=False):
         """ Get the desired block from the chain, if the current head block is smaller (for both head and irreversible)
             then we wait, but a maxmimum of blocks_waiting_for * max_block_wait_repetition time before failure.
 
             :param int block_number: desired block number
             :param int blocks_waiting_for: (default) difference between block_number and current head
                 how many blocks we are willing to wait, positive int
+            :param bool only_ops: Returns blocks with operations only, when set to True (default: False)
+            :param bool only_virtual_ops: Includes only virtual operations (default: False)
 
         """
-        if last_fetched_block_num is None or (last_fetched_block_num is not None and block_number > last_fetched_block_num):
-            if not blocks_waiting_for:
-                blocks_waiting_for = max(1, block_number - self.get_current_block_num())
+        if not blocks_waiting_for:
+            blocks_waiting_for = max(
+                1, block_number - self.get_current_block_num())
 
             repetition = 0
             # can't return the block before the chain has reached it (support future block_num)
@@ -295,41 +340,26 @@ class Blockchain(object):
                 if repetition > blocks_waiting_for * self.max_block_wait_repetition:
                     raise Exception("Wait time for new block exceeded, aborting")
         # block has to be returned properly
-        block = Block(block_number, steem_instance=self.steem)
+        try:
+            block = Block(block_number, only_ops=only_ops, only_virtual_ops=only_virtual_ops, steem_instance=self.steem)
+        except BlockDoesNotExistsException:
+            block = None
         repetition = 0
         while not block:
             repetition += 1
             time.sleep(self.block_interval)
-            if repetition > self.max_block_wait_repetition:
+            if repetition > blocks_waiting_for * self.max_block_wait_repetition:
                 raise Exception("Wait time for new block exceeded, aborting")
-            block = Block(block_number, steem_instance=self.steem)
+            try:
+                block = Block(block_number, only_ops=only_ops, only_virtual_ops=only_virtual_ops, steem_instance=self.steem)
+            except BlockDoesNotExistsException:
+                block = None
         return block
 
-    def ops(self, start=None, stop=None, **kwargs):
-        """ Yields all operations (including virtual operations) starting from
-            ``start``.
-
-            :param int start: Starting block
-            :param int stop: Stop at this block
-            :param str mode: We here have the choice between
-             "head" (the last block) and "irreversible" (the block that is
-             confirmed by 2/3 of all block producers and is thus irreversible)
-            :param bool only_virtual_ops: Only yield virtual operations
-
-            This call returns a list that only carries one operation and
-            its type!
+    def ops(self, start=None, stop=None, only_virtual_ops=False, **kwargs):
+        """ Blockchain.ops() is deprecated. Please use Blockchain.stream() instead.
         """
-
-        for block in self.blocks(start=start, stop=stop, **kwargs):
-            for tx in block["transactions"]:
-                for op in tx["operations"]:
-                    # Replace opid by op name
-                    # op[0] = getOperationNameForId(op[0])
-                    yield {
-                        "block_num": block.identifier,
-                        "op": op,
-                        "timestamp": block["timestamp"]
-                    }
+        raise DeprecationWarning('Blockchain.ops() is deprecated. Please use Blockchain.stream() instead.')
 
     def ops_statistics(self, start, stop=None, add_to_ops_stat=None, verbose=False):
         """ Generates a statistics for all operations (including virtual operations) starting from
@@ -355,15 +385,10 @@ class Blockchain(object):
             return
         if stop is None:
             stop = current_block
-        for block in self.blocks(start=start, stop=stop):
+        for block in self.blocks(start=start, stop=stop, only_ops=True):
             if verbose:
-                print(block.identifier + " " + block["timestamp"])
-            for tx in block["transactions"]:
-                for op in tx["operations"]:
-                    if isinstance(op, list):
-                        ops_stat[op[0]] += 1
-                    elif isinstance(op, dict):
-                        ops_stat[op["type"][:-10]] += 1
+                print(block["identifier"] + " " + block["timestamp"])
+            ops_stat = block.ops_statistics(add_to_ops_stat=ops_stat)
         return ops_stat
 
     def stream(self, opNames=[], *args, **kwargs):
@@ -372,34 +397,54 @@ class Blockchain(object):
             :param array opNames: List of operations to filter for
             :param int start: Start at this block
             :param int stop: Stop at this block
-            :param str mode: We here have the choice between
-             "head" (the last block) and "irreversible" (the block that is
-             confirmed by 2/3 of all block producers and is thus irreversible)
+            :param int max_batch_size: only for appbase nodes. When not None, batch calls of are used.
+                Cannot combine with threading
+            :param bool threading: Enables threading. Cannot be combined with batch calls
+            :param int thread_num: Defines the number of threads, when `threading` is set.
+            :param bool only_ops: Only yielding operations, when set to True (default: False)
+            :param bool only_virtual_ops: Only yield virtual operations (default: False)
 
             The dict output is formated such that ``type`` caries the
             operation type, timestamp and block_num are taken from the
             block the operation was stored in and the other key depend
             on the actualy operation.
+
+            .. note:: If you want instant confirmation, you need to instantiate
+                      class:`beem.blockchain.Blockchain` with
+                      ``mode="head"``, otherwise, the call will wait until
+                      confirmed in an irreversible block.
+
         """
-        for op in self.ops(**kwargs):
-            if isinstance(op["op"], list):
-                if not opNames or op["op"][0] in opNames:
-                    r = {
-                        "type": op["op"][0],
-                        "timestamp": op.get("timestamp"),
-                        "block_num": op.get("block_num"),
-                    }
-                    r.update(op["op"][1])
-                    yield r
-            elif isinstance(op["op"], dict):
-                if not opNames or op["op"]["type"][:-10] in opNames:
-                    r = {
-                        "type": op["op"]["type"][:-10],
-                        "timestamp": op.get("timestamp"),
-                        "block_num": op.get("block_num"),
-                    }
-                    r.update(op["op"]["value"])
-                    yield r
+        for block in self.blocks(**kwargs):
+            if "transactions" in block:
+                trx = block["transactions"]
+            else:
+                trx = [block]
+            for trx_nr in range(len(trx)):
+                for event in trx[trx_nr]["operations"]:
+                    if isinstance(event, list):
+                        op_type, op = event
+                        trx_id = block["transaction_ids"][trx_nr]
+                        block_num = block.get("id")
+                        _id = self.hash_op(event)
+                        timestamp = formatTimeString(block.get("timestamp"))
+                    else:
+                        op_type, op = event["op"]
+                        trx_id = event.get("trx_id")
+                        block_num = event.get("block")
+                        _id = self.hash_op(event["op"])
+                        timestamp = formatTimeString(event.get("timestamp"))
+                    if not opNames or op_type in opNames:
+                        if kwargs.get('raw_output'):
+                            yield event
+                        else:
+                            updated_op = {"type": op_type}
+                            updated_op.update(op.copy())
+                            updated_op.update({"_id": _id,
+                                               "timestamp": timestamp,
+                                               "block_num": block_num,
+                                               "trx_id": trx_id})
+                            yield updated_op
 
     def awaitTxConfirmation(self, transaction, limit=10):
         """ Returns the transaction as seen by the blockchain after being
diff --git a/beem/cli.py b/beem/cli.py
index f1d90372..3f2a9c9c 100644
--- a/beem/cli.py
+++ b/beem/cli.py
@@ -13,6 +13,7 @@ from beem.comment import Comment
 from beem.market import Market
 from beem.block import Block
 from beem.profile import Profile
+from beem.wallet import Wallet
 from beem.asset import Asset
 from beem.witness import Witness, WitnessesRankedByVote, WitnessesVotedByAccount
 from beem.blockchain import Blockchain
@@ -22,10 +23,12 @@ from beem import exceptions
 from beem.version import version as __version__
 from datetime import datetime, timedelta
 from beem.asciichart import AsciiChart
+from beem.transactionbuilder import TransactionBuilder
 import pytz
 from timeit import default_timer as timer
 from beembase import operations
 from beemgraphenebase.account import PrivateKey, PublicKey
+from beemgraphenebase.base58 import Base58
 import os
 import ast
 import json
@@ -168,7 +171,11 @@ def cli(node, offline, no_broadcast, no_wallet, unsigned, expires, verbose):
         nowallet=no_wallet,
         unsigned=unsigned,
         expiration=expires,
-        debug=debug
+        debug=debug,
+        num_retries=10,
+        num_retries_call=3,
+        timeout=10,
+        autoconnect=False
     )
     set_shared_steem_instance(stm)
 
@@ -190,6 +197,8 @@ def set(key, value):
     """
     stm = shared_steem_instance()
     if key == "default_account":
+        if stm.rpc:
+            stm.rpc.rpcconnect()
         stm.set_default_account(value)
     elif key == "default_vote_weight":
         stm.set_default_vote_weight(value)
@@ -220,6 +229,8 @@ def nextnode(results):
     """ Uses the next node in list
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     stm.move_current_node_to_front()
     node = stm.get_default_nodes()
     offline = stm.offline
@@ -266,6 +277,8 @@ def pingnode(raw, sort, remove, threading):
     """ Returns the answer time in milliseconds
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     nodes = stm.get_default_nodes()
     if not raw:
         t = PrettyTable(["Node", "Answer time [ms]"])
@@ -324,6 +337,8 @@ def currentnode(version, url):
     """ Sets the currently working node at the first place in the list
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     offline = stm.offline
     stm.move_current_node_to_front()
     node = stm.get_default_nodes()
@@ -466,6 +481,8 @@ def addkey(unsafe_import_key):
         and a prompt for entering the private key are shown.
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not unlock_wallet(stm):
         return
     if not unsafe_import_key:
@@ -487,6 +504,8 @@ def delkey(confirm, pub):
         which will be deleted from the wallet
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not unlock_wallet(stm):
         return
     stm.wallet.removePrivateKeyFromPublicKey(pub)
@@ -509,6 +528,8 @@ def listkeys():
 def listaccounts():
     """Show stored accounts"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     t = PrettyTable(["Name", "Type", "Available Key"])
     t.align = "l"
     for account in stm.wallet.getAccounts():
@@ -530,6 +551,8 @@ def upvote(post, vote_weight, account, weight):
         POST is @author/permlink
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not weight and vote_weight:
         weight = vote_weight
     elif not weight and not vote_weight:
@@ -559,6 +582,8 @@ def downvote(post, vote_weight, account, weight):
         POST is @author/permlink
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not weight and vote_weight:
         weight = vote_weight
     elif not weight and not vote_weight:
@@ -586,6 +611,8 @@ def downvote(post, vote_weight, account, weight):
 def transfer(to, amount, asset, memo, account):
     """Transfer SBD/STEEM"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not bool(memo):
@@ -605,6 +632,8 @@ def transfer(to, amount, asset, memo, account):
 def powerup(amount, account, to):
     """Power up (vest STEEM as STEEM POWER)"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -628,6 +657,8 @@ def powerdown(amount, account):
         amount is in VESTS
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -651,6 +682,8 @@ def powerdown(amount, account):
 def powerdownroute(to, percentage, account, auto_vest):
     """Setup a powerdown route"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -667,6 +700,8 @@ def powerdownroute(to, percentage, account, auto_vest):
 def convert(amount, account):
     """Convert STEEMDollars to Steem (takes a week to settle)"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -707,6 +742,8 @@ def power(account):
     """ Shows vote power and bandwidth
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if len(account) == 0:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -722,6 +759,8 @@ def balance(account):
     """ Shows balance
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if len(account) == 0:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -763,6 +802,8 @@ def interest(account):
     """ Get information about interest payment
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -791,6 +832,8 @@ def follower(account):
     """ Get information about followers
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -807,6 +850,8 @@ def following(account):
     """ Get information about following
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -823,6 +868,8 @@ def muter(account):
     """ Get information about muter
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -839,6 +886,8 @@ def muting(account):
     """ Get information about muting
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = [stm.config["default_account"]]
@@ -855,6 +904,8 @@ def permissions(account):
     """ Show permissions of an account
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         if "default_account" in stm.config:
             account = stm.config["default_account"]
@@ -891,6 +942,8 @@ def allow(foreign_account, permission, account, weight, threshold):
             This derived key will then interact with your account.
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -917,6 +970,8 @@ def allow(foreign_account, permission, account, weight, threshold):
 def disallow(foreign_account, permission, account, threshold):
     """Remove allowance an account/key to interact with your account"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -928,7 +983,7 @@ def disallow(foreign_account, permission, account, threshold):
     if not foreign_account:
         from beemgraphenebase.account import PasswordKey
         pwd = click.prompt("Password for Key Derivation", confirmation_prompt=True)
-        foreign_account = format(PasswordKey(account, pwd, permission).get_public(), stm.prefix)
+        foreign_account = [format(PasswordKey(account, pwd, permission).get_public(), stm.prefix)]
     tx = acc.disallow(foreign_account, permission=permission, threshold=threshold)
     tx = json.dumps(tx, indent=4)
     print(tx)
@@ -941,6 +996,8 @@ def disallow(foreign_account, permission, account, threshold):
 def newaccount(accountname, account, fee):
     """Create a new account"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -963,6 +1020,8 @@ def newaccount(accountname, account, fee):
 def setprofile(variable, value, account, pair):
     """Set a variable in an account\'s profile"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     keys = []
     values = []
     if pair:
@@ -996,6 +1055,8 @@ def setprofile(variable, value, account, pair):
 def delprofile(variable, account):
     """Delete a variable in an account\'s profile"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
 
     if not account:
         account = stm.config["default_account"]
@@ -1019,6 +1080,8 @@ def importaccount(account, roles):
     """Import an account using a passphrase"""
     from beemgraphenebase.account import PasswordKey
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not unlock_wallet(stm):
         return
     account = Account(account, steem_instance=stm)
@@ -1075,6 +1138,8 @@ def importaccount(account, roles):
 def updatememokey(account, key):
     """Update an account\'s memo key"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1099,6 +1164,8 @@ def updatememokey(account, key):
 def approvewitness(witness, account):
     """Approve a witnesses"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1115,6 +1182,8 @@ def approvewitness(witness, account):
 def disapprovewitness(witness, account):
     """Disapprove a witnesses"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1130,6 +1199,8 @@ def disapprovewitness(witness, account):
 def sign(file):
     """Sign a provided transaction with available and required keys"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if file and file != "-":
         if not os.path.isfile(file):
             raise Exception("File %s does not exist!" % file)
@@ -1148,6 +1219,8 @@ def sign(file):
 def broadcast(file):
     """broadcast a signed transaction"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if file and file != "-":
         if not os.path.isfile(file):
             raise Exception("File %s does not exist!" % file)
@@ -1166,6 +1239,8 @@ def ticker():
     """ Show ticker
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     t = PrettyTable(["Key", "Value"])
     t.align = "l"
     market = Market(steem_instance=stm)
@@ -1176,12 +1251,15 @@ def ticker():
 
 
 @cli.command()
-@click.option('--width', help='Plot width (default 75)', default=75)
-@click.option('--height', help='Plot height (default 15)', default=15)
-def pricehistory(width, height):
+@click.option('--width', '-w', help='Plot width (default 75)', default=75)
+@click.option('--height', '-h', help='Plot height (default 15)', default=15)
+@click.option('--ascii', help='Use only ascii symbols', is_flag=True, default=False)
+def pricehistory(width, height, ascii):
     """ Show price history
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     feed_history = stm.get_feed_history()
     current_base = Amount(feed_history['current_median_history']["base"], steem_instance=stm)
     current_quote = Amount(feed_history['current_median_history']["quote"], steem_instance=stm)
@@ -1191,7 +1269,11 @@ def pricehistory(width, height):
         base = Amount(h["base"])
         quote = Amount(h["quote"])
         price.append(base.amount / quote.amount)
-    chart = AsciiChart(height=height, width=width, offset=4, placeholder='{:6.2f} $')
+    if ascii:
+        charset = u'ascii'
+    else:
+        charset = u'utf8'
+    chart = AsciiChart(height=height, width=width, offset=4, placeholder='{:6.2f} $', charset=charset)
     print("\n            Price history for STEEM (median price %4.2f $)\n" % (float(current_base) / float(current_quote)))
 
     chart.adapt_on_series(price)
@@ -1203,15 +1285,18 @@ def pricehistory(width, height):
 
 
 @cli.command()
-@click.option('--days', help='Limit the days of shown trade history (default 7)', default=7)
+@click.option('--days', '-d', help='Limit the days of shown trade history (default 7)', default=7)
 @click.option('--hours', help='Limit the intervall history intervall (default 2 hours)', default=2.0)
-@click.option('--limit', help='Limit number of trades which is fetched at each intervall point (default 100)', default=100)
-@click.option('--width', help='Plot width (default 75)', default=75)
-@click.option('--height', help='Plot height (default 15)', default=15)
-def tradehistory(days, hours, limit, width, height):
+@click.option('--limit', '-l', help='Limit number of trades which is fetched at each intervall point (default 100)', default=100)
+@click.option('--width', '-w', help='Plot width (default 75)', default=75)
+@click.option('--height', '-h', help='Plot height (default 15)', default=15)
+@click.option('--ascii', help='Use only ascii symbols', is_flag=True, default=False)
+def tradehistory(days, hours, limit, width, height, ascii):
     """ Show price history
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     m = Market(steem_instance=stm)
     utc = pytz.timezone('UTC')
     stop = utc.localize(datetime.utcnow())
@@ -1226,7 +1311,11 @@ def tradehistory(days, hours, limit, width, height):
             base += float(order.as_base("SBD")["base"])
             quote += float(order.as_base("SBD")["quote"])
         price.append(base / quote)
-    chart = AsciiChart(height=height, width=width, offset=3, placeholder='{:6.2f} ')
+    if ascii:
+        charset = u'ascii'
+    else:
+        charset = u'utf8'
+    chart = AsciiChart(height=height, width=width, offset=3, placeholder='{:6.2f} ', charset=charset)
     print("\n     Trade history %s - %s \n\nSTEEM/SBD" % (formatTimeString(start), formatTimeString(stop)))
     chart.adapt_on_series(price)
     chart.new_chart()
@@ -1237,13 +1326,16 @@ def tradehistory(days, hours, limit, width, height):
 
 @cli.command()
 @click.option('--chart', help='Enable charting', is_flag=True)
-@click.option('--limit', help='Limit number of returned open orders (default 25)', default=25)
+@click.option('--limit', '-l', help='Limit number of returned open orders (default 25)', default=25)
 @click.option('--show-date', help='Show dates', is_flag=True, default=False)
-@click.option('--width', help='Plot width (default 75)', default=75)
-@click.option('--height', help='Plot height (default 15)', default=15)
-def orderbook(chart, limit, show_date, width, height):
+@click.option('--width', '-w', help='Plot width (default 75)', default=75)
+@click.option('--height', '-h', help='Plot height (default 15)', default=15)
+@click.option('--ascii', help='Use only ascii symbols', is_flag=True, default=False)
+def orderbook(chart, limit, show_date, width, height, ascii):
     """Obtain orderbook of the internal market"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     market = Market(steem_instance=stm)
     orderbook = market.orderbook(limit=limit, raw_data=False)
     if not show_date:
@@ -1283,7 +1375,11 @@ def orderbook(chart, limit, show_date, width, height):
         if n < len(bids_date):
             n = len(bids_date)
     if chart:
-        chart = AsciiChart(height=height, width=width, offset=4, placeholder=' {:10.2f} $')
+        if ascii:
+            charset = u'ascii'
+        else:
+            charset = u'utf8'
+        chart = AsciiChart(height=height, width=width, offset=4, placeholder=' {:10.2f} $', charset=charset)
         print("\n            Orderbook \n")
         chart.adapt_on_series(sumsum_asks[::-1] + sumsum_bids)
         chart.new_chart()
@@ -1336,6 +1432,8 @@ def buy(amount, asset, price, account, orderid):
         Limit buy price denoted in (SBD per STEEM)
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if asset == "SBD":
@@ -1377,6 +1475,8 @@ def sell(amount, asset, price, account, orderid):
         Limit sell price denoted in (SBD per STEEM)
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if asset == "SBD":
         market = Market(base=Asset("STEEM"), quote=Asset("SBD"), steem_instance=stm)
     else:
@@ -1411,6 +1511,8 @@ def sell(amount, asset, price, account, orderid):
 def cancel(orderid, account):
     """Cancel order in the internal market"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     market = Market(steem_instance=stm)
     if not account:
         account = stm.config["default_account"]
@@ -1427,6 +1529,8 @@ def cancel(orderid, account):
 def openorders(account):
     """Show open orders"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     market = Market(steem_instance=stm)
     if not account:
         account = stm.config["default_account"]
@@ -1448,6 +1552,8 @@ def openorders(account):
 def resteem(identifier, account):
     """Resteem an existing post"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1466,6 +1572,8 @@ def resteem(identifier, account):
 def follow(follow, account, what):
     """Follow another account"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if isinstance(what, str):
@@ -1485,6 +1593,8 @@ def follow(follow, account, what):
 def mute(mute, account, what):
     """Mute another account"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if isinstance(what, str):
@@ -1503,6 +1613,8 @@ def mute(mute, account, what):
 def unfollow(unfollow, account):
     """Unfollow/Unmute another account"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1523,6 +1635,8 @@ def unfollow(unfollow, account):
 def witnessupdate(witness, maximum_block_size, account_creation_fee, sbd_interest_rate, url, signing_key):
     """Change witness properties"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not witness:
         witness = stm.config["default_account"]
     if not unlock_wallet(stm):
@@ -1551,6 +1665,8 @@ def witnessupdate(witness, maximum_block_size, account_creation_fee, sbd_interes
 def witnesscreate(witness, signing_key, maximum_block_size, account_creation_fee, sbd_interest_rate, url):
     """Create a witness"""
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not unlock_wallet(stm):
         return
     props = {
@@ -1574,6 +1690,8 @@ def witnesses(account, limit):
     """ List witnesses
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if account:
         witnesses = WitnessesVotedByAccount(account, steem_instance=stm)
     else:
@@ -1589,6 +1707,8 @@ def votes(account, direction, days):
     """ List outgoing/incoming account votes
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     utc = pytz.timezone('UTC')
@@ -1605,6 +1725,151 @@ def votes(account, direction, days):
         votes.printAsTable(votee=account["name"])
 
 
+@cli.command()
+@click.argument('authorperm', nargs=1, required=True)
+@click.option('--payout', '-p', default=None, help="Show the curation for a potential payout in SBD as float")
+def curation(authorperm, payout):
+    """ Lists curation rewords of all votes for authorperm
+    """
+    stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
+    comment = Comment(authorperm, steem_instance=stm)
+    if payout is not None and comment.is_pending():
+        payout = float(payout)
+    elif payout is not None:
+        payout = None
+    t = PrettyTable(["Voter", "Voting time", "Vote", "Early vote loss", "Curation", "Performance"])
+    t.align = "l"
+    curation_rewards_SBD = comment.get_curation_rewards(pending_payout_SBD=True, pending_payout_value=payout)
+    curation_rewards_SP = comment.get_curation_rewards(pending_payout_SBD=False, pending_payout_value=payout)
+    rows = []
+    for vote in comment["active_votes"]:
+        vote_SBD = stm.rshares_to_sbd(int(vote["rshares"]))
+        curation_SBD = curation_rewards_SBD["active_votes"][vote["voter"]]
+        curation_SP = curation_rewards_SP["active_votes"][vote["voter"]]
+        if vote_SBD > 0:
+            penalty = ((100 - comment.get_curation_penalty(vote_time=vote["time"])) / 100 * vote_SBD)
+            performance = (float(curation_SBD) / vote_SBD * 100)
+        else:
+            performance = 0
+            penalty = 0
+        rows.append([vote["voter"],
+                     ((formatTimeString(vote["time"]) - comment["created"]).total_seconds() / 60),
+                     vote_SBD,
+                     penalty,
+                     float(curation_SP),
+                     performance])
+    sortedList = sorted(rows, key=lambda row: (row[1]), reverse=False)
+    for row in sortedList:
+        t.add_row([row[0],
+                   "%.1f min" % row[1],
+                   "%.3f SBD" % float(row[2]),
+                   "%.3f SBD" % float(row[3]),
+                   "%.3f SP" % (row[4]),
+                   "%.1f %%" % (row[5])])
+    print(t)
+
+
+@cli.command()
+@click.argument('account', nargs=1, required=False)
+@click.option('--post', '-p', help='Show pending post payout', is_flag=True, default=False)
+@click.option('--comment', '-c', help='Show pending comments payout', is_flag=True, default=False)
+@click.option('--curation', '-v', help='Shows  pending curation', is_flag=True, default=False)
+def rewards(account, post, comment, curation):
+    """ Lists outstanding rewards
+    """
+    stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
+    if not account:
+        account = stm.config["default_account"]
+    if not comment and not curation:
+        post = True
+
+    utc = pytz.timezone('UTC')
+    limit_time = utc.localize(datetime.utcnow()) - timedelta(days=7)
+    sum_reward = [0, 0, 0, 0]
+    account = Account(account, steem_instance=stm)
+    median_price = Price(stm.get_current_median_history(), steem_instance=stm)
+    m = Market(steem_instance=stm)
+    latest = m.ticker()["latest"]
+    t = PrettyTable(["Description", "Cashout", "SBD", "SP", "Liquid USD", "Invested USD"])
+    t.align = "l"
+    rows = []
+    c_list = {}
+    start_op = account.estimate_virtual_op_num(limit_time)
+    length = (account.virtual_op_count() - start_op) / 1000
+    with click.progressbar(map(Comment, account.history(start=start_op, use_block_num=False, only_ops=["comment"])), length=length) as comment_hist:
+        for v in comment_hist:
+            v.refresh()
+            author_reward = v.get_author_rewards()
+            if float(author_reward["total_payout_SBD"]) < 0.001:
+                continue
+            if v.permlink in c_list:
+                continue
+            if not v.is_pending():
+                continue
+            if not post and not v.is_comment():
+                continue
+            if not comment and v.is_comment():
+                continue
+            c_list[v.permlink] = 1
+            payout_SBD = author_reward["payout_SBD"]
+            sum_reward[0] += float(payout_SBD)
+            payout_SP = author_reward["payout_SP"]
+            sum_reward[1] += float(payout_SP)
+            liquid_USD = float(author_reward["payout_SBD"]) / float(latest) * float(median_price)
+            sum_reward[2] += liquid_USD
+            invested_USD = float(author_reward["payout_SP"]) * float(median_price)
+            sum_reward[3] += invested_USD
+            if v.is_comment():
+                title = v.parent_permlink
+            else:
+                title = v.permlink
+            rows.append([title,
+                         ((v["created"] - limit_time).total_seconds() / 60 / 60 / 24),
+                         (payout_SBD),
+                         (payout_SP),
+                         (liquid_USD),
+                         (invested_USD)])
+    if curation:
+        votes = AccountVotes(account, start=limit_time, steem_instance=stm)
+        for vote in votes:
+            c = Comment(vote["authorperm"])
+            rewards = c.get_curation_rewards()
+            if not rewards["pending_rewards"]:
+                continue
+            payout_SP = rewards["active_votes"][account["name"]]
+            liquid_USD = 0
+            invested_USD = float(payout_SP) * float(median_price)
+            sum_reward[1] += float(payout_SP)
+            sum_reward[3] += invested_USD
+            rows.append([c.permlink,
+                         ((c["created"] - limit_time).total_seconds() / 60 / 60 / 24),
+                         0.000,
+                         payout_SP,
+                         (liquid_USD),
+                         (invested_USD)])
+    sortedList = sorted(rows, key=lambda row: (row[1]), reverse=True)
+    for row in sortedList:
+        t.add_row([row[0][:30],
+                   "%.1f days" % row[1],
+                   "%.3f" % float(row[2]),
+                   "%.3f" % float(row[3]),
+                   "%.2f $" % (row[4]),
+                   "%.2f $" % (row[5])])
+
+    t.add_row(["", "", "", "", "", ""])
+    t.add_row(["Sum",
+               "-",
+               "%.2f SBD" % (sum_reward[0]),
+               "%.2f SP" % (sum_reward[1]),
+               "%.2f $" % (sum_reward[2]),
+               "%.2f $" % (sum_reward[3])])
+    print(t)
+
+
 @cli.command()
 @click.argument('account', nargs=1, required=False)
 @click.option('--reward_steem', help='Amount of STEEM you would like to claim', default="0 STEEM")
@@ -1616,6 +1881,8 @@ def claimreward(account, reward_steem, reward_sbd, reward_vests):
         By default, this will claim ``all`` outstanding balances.
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not account:
         account = stm.config["default_account"]
     acc = Account(account, steem_instance=stm)
@@ -1631,6 +1898,75 @@ def claimreward(account, reward_steem, reward_sbd, reward_vests):
     print(tx)
 
 
+@cli.command()
+@click.argument('blocknumber', nargs=1, required=False)
+@click.option('--trx', '-t', help='Show only one transaction number', default=None)
+@click.option('--use-api', '-u', help='Uses the get_potential_signatures api call', is_flag=True, default=False)
+def verify(blocknumber, trx, use_api):
+    """Returns the public signing keys for a block"""
+    stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
+    b = Blockchain(steem_instance=stm)
+    i = 0
+    if not blocknumber:
+        blocknumber = b.get_current_block_num()
+    try:
+        int(blocknumber)
+        block = Block(blocknumber, steem_instance=stm)
+        if trx is not None:
+            i = int(trx)
+            trxs = [block.transactions[int(trx)]]
+        else:
+            trxs = block.transactions
+    except Exception:
+        trxs = [b.get_transaction(blocknumber)]
+        blocknumber = trxs[0]["block_num"]
+    wallet = Wallet(steem_instance=stm)
+    t = PrettyTable(["trx", "Signer key", "Account"])
+    t.align = "l"
+    if not use_api:
+        from beembase.signedtransactions import Signed_Transaction
+    for trx in trxs:
+        if not use_api:
+            # trx is now identical to the output of get_transaction
+            # This is just for testing porpuse
+            if True:
+                signed_tx = Signed_Transaction(trx.copy())
+            else:
+                tx = b.get_transaction(trx["transaction_id"])
+                signed_tx = Signed_Transaction(tx)
+            public_keys = []
+            for key in signed_tx.verify(chain=stm.chain_params, recover_parameter=True):
+                public_keys.append(format(Base58(key, prefix=stm.prefix), stm.prefix))
+        else:
+            tx = TransactionBuilder(tx=trx, steem_instance=stm)
+            public_keys = tx.get_potential_signatures()
+        accounts = []
+        empty_public_keys = []
+        for key in public_keys:
+            account = wallet.getAccountFromPublicKey(key)
+            if account is None:
+                empty_public_keys.append(key)
+            else:
+                accounts.append(account)
+        new_public_keys = []
+        for key in public_keys:
+            if key not in empty_public_keys or use_api:
+                new_public_keys.append(key)
+        if isinstance(new_public_keys, list) and len(new_public_keys) == 1:
+            new_public_keys = new_public_keys[0]
+        else:
+            new_public_keys = json.dumps(new_public_keys, indent=4)
+        if isinstance(accounts, list) and len(accounts) == 1:
+            accounts = accounts[0]
+        else:
+            accounts = json.dumps(accounts, indent=4)
+        t.add_row(["%d" % i, new_public_keys, accounts])
+        i += 1
+    print(t)
+
+
 @cli.command()
 @click.argument('objects', nargs=-1)
 def info(objects):
@@ -1640,6 +1976,8 @@ def info(objects):
         a post/comment and a public key
     """
     stm = shared_steem_instance()
+    if stm.rpc:
+        stm.rpc.rpcconnect()
     if not objects:
         t = PrettyTable(["Key", "Value"])
         t.align = "l"
@@ -1647,8 +1985,8 @@ def info(objects):
         median_price = stm.get_current_median_history()
         steem_per_mvest = stm.get_steem_per_mvest()
         chain_props = stm.get_chain_properties()
-        price = (Amount(median_price["base"]).amount / Amount(
-            median_price["quote"]).amount)
+        price = (Amount(median_price["base"], steem_instance=stm).amount / Amount(
+            median_price["quote"], steem_instance=stm).amount)
         for key in info:
             t.add_row([key, info[key]])
         t.add_row(["steem per mvest", steem_per_mvest])
@@ -1662,11 +2000,11 @@ def info(objects):
             if re.match("^[0-9-]*:[0-9-]", obj):
                 obj, tran_nr = obj.split(":")
             if int(obj) < 1:
-                b = Blockchain()
+                b = Blockchain(steem_instance=stm)
                 block_number = b.get_current_block_num() + int(obj) - 1
             else:
                 block_number = obj
-            block = Block(block_number)
+            block = Block(block_number, steem_instance=stm)
             if block:
                 t = PrettyTable(["Key", "Value"])
                 t.align = "l"
@@ -1680,7 +2018,7 @@ def info(objects):
                         else:
                             tran_nr = int(tran_nr)
                         if len(value) > tran_nr - 1 and tran_nr > -1:
-                            t_value = json.dumps(value[tran_nr - 1], indent=4)
+                            t_value = json.dumps(value[tran_nr], indent=4)
                             t.add_row(["transaction %d/%d" % (tran_nr, len(value)), t_value])
                     elif key == "transaction_ids" and not bool(tran_nr):
                         t.add_row(["Nr. of transaction_ids", len(value)])
@@ -1690,7 +2028,7 @@ def info(objects):
                         else:
                             tran_nr = int(tran_nr)
                         if len(value) > tran_nr - 1 and tran_nr > -1:
-                            t.add_row(["transaction_id %d/%d" % (int(tran_nr), len(value)), value[tran_nr - 1]])
+                            t.add_row(["transaction_id %d/%d" % (int(tran_nr), len(value)), value[tran_nr]])
                     else:
                         t.add_row([key, value])
                 print(t)
@@ -1718,7 +2056,7 @@ def info(objects):
 
             # witness available?
             try:
-                witness = Witness(obj)
+                witness = Witness(obj, steem_instance=stm)
                 witness_json = witness.json()
                 t = PrettyTable(["Key", "Value"])
                 t.align = "l"
@@ -1742,13 +2080,16 @@ def info(objects):
                 print("Public Key not known" % obj)
         # Post identifier
         elif re.match(".*@.{3,16}/.*$", obj):
-            post = Comment(obj)
+            post = Comment(obj, steem_instance=stm)
             post_json = post.json()
             if post_json:
                 t = PrettyTable(["Key", "Value"])
                 t.align = "l"
                 for key in sorted(post_json):
-                    value = post_json[key]
+                    if key in ["body", "active_votes"]:
+                        value = "not shown"
+                    else:
+                        value = post_json[key]
                     if (key in ["json_metadata"]):
                         value = json.loads(value)
                         value = json.dumps(value, indent=4)
@@ -1763,4 +2104,7 @@ def info(objects):
 
 
 if __name__ == "__main__":
-    cli()
+    if getattr(sys, 'frozen', False):
+        cli(sys.argv[1:])
+    else:
+        cli()
diff --git a/beem/comment.py b/beem/comment.py
index fd8e0b3f..6c6352bb 100644
--- a/beem/comment.py
+++ b/beem/comment.py
@@ -4,18 +4,21 @@ from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
 from builtins import str
+import json
+import re
+import logging
+import pytz
+import math
+from datetime import datetime
 from .instance import shared_steem_instance
 from .account import Account
 from .amount import Amount
+from .price import Price
 from .utils import resolve_authorperm, construct_authorperm, derive_permlink, remove_from_dict, make_patch, formatTimeString
 from .blockchainobject import BlockchainObject
 from .exceptions import ContentDoesNotExistsException, VotingInvalidOnArchivedPost
 from beembase import operations
 from beemgraphenebase.py23 import py23_bytes, bytes_types, integer_types, string_types, text_type
-import json
-import re
-import logging
-from datetime import datetime
 log = logging.getLogger(__name__)
 
 
@@ -222,7 +225,233 @@ class Comment(BlockchainObject):
         """
         return self['depth'] > 0
 
+    @property
+    def reward(self):
+        """ Return the estimated total SBD reward.
+        """
+        a_zero = Amount("0 SBD", steem_instance=self.steem)
+        return self.get("total_payout_value", a_zero) + self.get("pending_payout_value", a_zero)
+
+    def is_pending(self):
+        """ Return if the payout is pending (the post/comment
+            is younger than 7 days)
+        """
+        return not float(self["total_payout_value"]) >= Amount("0.001 SBD", steem_instance=self.steem)
+
+    def time_elapsed(self):
+        """Return a timedelta on how old the post is.
+        """
+        utc = pytz.timezone('UTC')
+        return utc.localize(datetime.utcnow()) - self['created']
+
+    def curation_penalty_compensation_SBD(self):
+        """ Returns The required post payout amount after 30 minutes
+            which will compentsate the curation penalty, if voting earlier than 30 minutes
+        """
+        self.refresh()
+        return self.reward * 900. / ((self.time_elapsed()).total_seconds() / 60) ** 2
+
+    def estimate_curation_SBD(self, vote_value_SBD, estimated_value_SBD=None):
+        """ Estimates curation reward
+
+            :param float vote_value_SBD: The vote value in SBD for which the curation
+                should be calculated
+            :param float estimated_value_SBD: When set, this value is used for calculate
+                the curation. When not set, the current post value is used.
+        """
+        self.refresh()
+        if estimated_value_SBD is None:
+            estimated_value_SBD = float(self.reward)
+        t = self.get_curation_penalty() / 100.
+        k = vote_value_SBD / (vote_value_SBD + float(self.reward))
+        K = (1 - math.sqrt(1 - k)) / 4 / k
+        return K * vote_value_SBD * t * math.sqrt(estimated_value_SBD)
+
+    def get_curation_penalty(self, vote_time=None):
+        """ If post is less than 30 minutes old, it will incur a curation
+            reward penalty.
+
+            :param datetime vote_time: A vote time can be given and the curation
+                penalty is calculated regarding the given time (default is None)
+                When set to None, the current date is used.
+
+        """
+        if vote_time is None:
+            elapsed_seconds = self.time_elapsed().total_seconds()
+        elif isinstance(vote_time, str):
+            elapsed_seconds = (formatTimeString(vote_time) - self["created"]).total_seconds()
+        elif isinstance(vote_time, datetime):
+            elapsed_seconds = (vote_time - self["created"]).total_seconds()
+        else:
+            raise ValueError("vote_time must be a string or a datetime")
+        reward = (elapsed_seconds / 1800) * 100
+        if reward > 100:
+            reward = 100
+        return reward
+
+    def get_vote_with_curation(self, voter=None, raw_data=False, pending_payout_value=None):
+        """ Returns vote for voter. Returns None, if the voter cannot be found in `active_votes`.
+
+            :param str voter: Voter for which the vote should be returned
+            :param bool raw_data: If True, the raw data are returned
+            :param float/str pending_payout_SBD: When not None this value instead of the current
+                value is used for calculating the rewards
+        """
+        specific_vote = None
+        if voter is None:
+            voter = Account(self["author"], steem_instance=self.steem)
+        else:
+            voter = Account(voter, steem_instance=self.steem)
+        for vote in self["active_votes"]:
+            if voter["name"] == vote["voter"]:
+                specific_vote = vote
+        if specific_vote is not None and raw_data:
+            return specific_vote
+        elif specific_vote is not None:
+            curation_reward = self.get_curation_rewards(pending_payout_SBD=True, pending_payout_value=pending_payout_value)
+            specific_vote["curation_reward"] = curation_reward["active_votes"][voter["name"]]
+            specific_vote["ROI"] = float(curation_reward["active_votes"][voter["name"]]) / float(voter.get_voting_value_SBD(voting_weight=specific_vote["percent"] / 100)) * 100
+            return specific_vote
+        else:
+            return None
+
+    def get_beneficiaries_pct(self):
+        """ Returns the sum of all post beneficiaries in percentage
+        """
+        beneficiaries = self["beneficiaries"]
+        weight = 0
+        for b in beneficiaries:
+            weight += b["weight"]
+        return weight / 100.
+
+    def get_rewards(self):
+        """ Returns the total_payout, author_payout and the curator payout in SBD.
+            When the payout is still pending, the estimated payout is given out.
+
+            Example:::
+
+                {
+                    'total_payout': 9.956 SBD,
+                    'author_payout': 7.166 SBD,
+                    'curator_payout': 2.790 SBD
+                }
+
+        """
+        if self.is_pending():
+            total_payout = self["pending_payout_value"]
+            author_payout = self.get_author_rewards()["author_payout"]
+            curator_payout = total_payout - author_payout
+        else:
+            total_payout = self["total_payout_value"]
+            curator_payout = self["curator_payout_value"]
+            author_payout = total_payout - curator_payout
+        return {"total_payout": total_payout, "author_payout": author_payout, "curator_payout": curator_payout}
+
+    def get_author_rewards(self):
+        """ Returns the author rewards.
+
+            Example:::
+
+                {
+                    'pending_rewards': True,
+                    'payout_SP': 0.912 STEEM,
+                    'payout_SBD': 3.583 SBD,
+                    'total_payout_SBD': 7.166 SBD
+                }
+
+        """
+        if not self.is_pending():
+            total_payout = self["total_payout_value"]
+            curator_payout = self["curator_payout_value"]
+            author_payout = total_payout - curator_payout
+            return {'pending_rewards': False, "payout_SP": Amount("0 SBD", steem_instance=self.steem), "payout_SBD": Amount("0 SBD", steem_instance=self.steem), "total_payout_SBD": author_payout}
+
+        median_price = Price(self.steem.get_current_median_history(), steem_instance=self.steem)
+        beneficiaries_pct = self.get_beneficiaries_pct()
+        curation_tokens = self.reward * 0.25
+        author_tokens = self.reward - curation_tokens
+        curation_rewards = self.get_curation_rewards()
+        author_tokens += median_price * curation_rewards['unclaimed_rewards']
+
+        benefactor_tokens = author_tokens * beneficiaries_pct / 100.
+        author_tokens -= benefactor_tokens
+
+        sbd_steem = author_tokens * self["percent_steem_dollars"] / 20000.
+        vesting_steem = median_price.as_base("STEEM") * (author_tokens - sbd_steem)
+
+        return {'pending_rewards': True, "payout_SP": vesting_steem, "payout_SBD": sbd_steem, "total_payout_SBD": author_tokens}
+
+    def get_curation_rewards(self, pending_payout_SBD=False, pending_payout_value=None):
+        """ Returns the curation rewards.
+
+            :param bool pending_payout_SBD: If True, the rewards are returned in SBD and not in STEEM (default is False)
+            :param float/str pending_payout_value: When not None this value instead of the current
+                value is used for calculating the rewards
+
+            `pending_rewards` is True when
+            the post is younger than 7 days. `unclaimed_rewards` is the
+            amount of curation_rewards that goes to the author (self-vote or votes within
+            the first 30 minutes). `active_votes` contains all voter with their curation reward.
+
+            Example:::
+
+                {
+                    'pending_rewards': True, 'unclaimed_rewards': 0.245 STEEM,
+                    'active_votes': {
+                        'leprechaun': 0.006 STEEM, 'timcliff': 0.186 STEEM,
+                        'st3llar': 0.000 STEEM, 'crokkon': 0.015 STEEM, 'feedyourminnows': 0.003 STEEM,
+                        'isnochys': 0.003 STEEM, 'loshcat': 0.001 STEEM, 'greenorange': 0.000 STEEM,
+                        'qustodian': 0.123 STEEM, 'jpphotography': 0.002 STEEM, 'thinkingmind': 0.001 STEEM,
+                        'oups': 0.006 STEEM, 'mattockfs': 0.001 STEEM, 'holger80': 0.003 STEEM, 'michaelizer': 0.004 STEEM,
+                        'flugschwein': 0.010 STEEM, 'ulisessabeque': 0.000 STEEM, 'hakancelik': 0.002 STEEM, 'sbi2': 0.008 STEEM,
+                        'zcool': 0.000 STEEM, 'steemhq': 0.002 STEEM, 'rowdiya': 0.000 STEEM, 'qurator-tier-1-2': 0.012 STEEM
+                    }
+                }
+
+        """
+        median_price = Price(self.steem.get_current_median_history(), steem_instance=self.steem)
+        pending_rewards = False
+        total_vote_weight = self["total_vote_weight"]
+        if not self["allow_curation_rewards"]:
+            max_rewards = Amount("0 STEEM", steem_instance=self.steem)
+            unclaimed_rewards = max_rewards.copy()
+        elif not self.is_pending():
+            max_rewards = self["curator_payout_value"]
+            unclaimed_rewards = self["total_payout_value"] * 0.25 - max_rewards
+            total_vote_weight = 0
+            for vote in self["active_votes"]:
+                total_vote_weight += vote["weight"]
+        else:
+            if pending_payout_value is None:
+                pending_payout_value = self["pending_payout_value"]
+            elif isinstance(pending_payout_value, (float, int)):
+                pending_payout_value = Amount(pending_payout_value, "SBD", steem_instance=self.steem)
+            elif isinstance(pending_payout_value, str):
+                pending_payout_value = Amount(pending_payout_value, steem_instance=self.steem)
+            if pending_payout_SBD:
+                max_rewards = (pending_payout_value * 0.25)
+            else:
+                max_rewards = median_price.as_base("STEEM") * (pending_payout_value * 0.25)
+            unclaimed_rewards = max_rewards.copy()
+            pending_rewards = True
+
+        active_votes = {}
+        for vote in self["active_votes"]:
+            if total_vote_weight > 0:
+                claim = max_rewards * vote["weight"] / total_vote_weight
+            else:
+                claim = 0
+            if claim > 0 and pending_rewards:
+                unclaimed_rewards -= claim
+            if claim > 0:
+                active_votes[vote["voter"]] = claim
+            else:
+                active_votes[vote["voter"]] = 0
+
+        return {'pending_rewards': pending_rewards, 'unclaimed_rewards': unclaimed_rewards, "active_votes": active_votes}
+
     def get_reblogged_by(self, identifier=None):
+        """Shows in which blogs this post appears"""
         if not identifier:
             post_author = self["author"]
             post_permlink = self["permlink"]
@@ -234,6 +463,7 @@ class Comment(BlockchainObject):
             return self.steem.rpc.get_reblogged_by(post_author, post_permlink, api="follow")
 
     def get_votes(self):
+        """Returns all votes as ActiveVotes object"""
         from .vote import ActiveVotes
         return ActiveVotes(self, steem_instance=self.steem)
 
diff --git a/beemgraphenebase/signedtransactions.py b/beemgraphenebase/signedtransactions.py
index 74ffac61..9ca0156e 100644
--- a/beemgraphenebase/signedtransactions.py
+++ b/beemgraphenebase/signedtransactions.py
@@ -170,7 +170,7 @@ class Signed_Transaction(GrapheneObject):
                         phex = hexlify(p).decode('ascii')
                         pubKeysFound.append(phex)
                     except Exception:
-                        continue
+                        p = None
             else:
                 phex = hexlify(p).decode('ascii')
                 pubKeysFound.append(phex)
diff --git a/examples/memory_profiler1.py b/examples/memory_profiler1.py
index 50cc9086..fdda2b0c 100644
--- a/examples/memory_profiler1.py
+++ b/examples/memory_profiler1.py
@@ -37,7 +37,7 @@ def profiling(name_list):
     startBlockNumber = current_num - 20
     endBlockNumber = current_num
     block_elem = None
-    for o in blockchain_object.ops(start=startBlockNumber, stop=endBlockNumber):
+    for o in blockchain_object.stream(start=startBlockNumber, stop=endBlockNumber):
         print("block %d" % (o["block_num"]))
         block_elem = o
     print(block_elem)
diff --git a/tests/beem/test_block.py b/tests/beem/test_block.py
index 8f441456..993b920b 100644
--- a/tests/beem/test_block.py
+++ b/tests/beem/test_block.py
@@ -52,7 +52,31 @@ class Testcases(unittest.TestCase):
         self.assertTrue(isinstance(block.time(), datetime))
         self.assertTrue(isinstance(block, dict))
 
-        self.assertTrue(len(block.ops()))
+        self.assertTrue(len(block.operations))
+        self.assertTrue(isinstance(block.ops_statistics(), dict))
+
+        block2 = Block(self.test_block_id + 1, steem_instance=bts)
+        self.assertTrue(block2.time() > block.time())
+        with self.assertRaises(
+            exceptions.BlockDoesNotExistsException
+        ):
+            Block(0, steem_instance=bts)
+
+    @parameterized.expand([
+        ("non_appbase"),
+        ("appbase"),
+    ])
+    def test_block_only_ops(self, node_param):
+        if node_param == "non_appbase":
+            bts = self.bts
+        else:
+            bts = self.appbase
+        block = Block(self.test_block_id, only_ops=True, steem_instance=bts)
+        self.assertEqual(block.identifier, self.test_block_id)
+        self.assertTrue(isinstance(block.time(), datetime))
+        self.assertTrue(isinstance(block, dict))
+
+        self.assertTrue(len(block.operations))
         self.assertTrue(isinstance(block.ops_statistics(), dict))
 
         block2 = Block(self.test_block_id + 1, steem_instance=bts)
diff --git a/tests/beem/test_blockchain.py b/tests/beem/test_blockchain.py
index 12288866..8bb1b2c8 100644
--- a/tests/beem/test_blockchain.py
+++ b/tests/beem/test_blockchain.py
@@ -150,20 +150,6 @@ class Testcases(unittest.TestCase):
         self.assertEqual(op_stat["transfer"], op_stat2["transfer"])
         self.assertEqual(op_stat["vote"], op_stat2["vote"])
 
-        ops_ops = []
-        for op in b.ops(start=self.start, stop=self.stop):
-            ops_ops.append(op)
-        self.assertTrue(len(ops_ops) > 0)
-        op_stat3 = {"transfer": 0, "vote": 0}
-
-        for op in ops_ops:
-            if op["op"][0] in opNames:
-                op_stat3[op["op"][0]] += 1
-            self.assertTrue(op["block_num"] >= self.start)
-            self.assertTrue(op["block_num"] <= self.stop)
-        self.assertEqual(op_stat["transfer"], op_stat3["transfer"])
-        self.assertEqual(op_stat["vote"], op_stat3["vote"])
-
         ops_blocks = []
         for op in b.blocks(start=self.start, stop=self.stop):
             ops_blocks.append(op)
@@ -184,3 +170,22 @@ class Testcases(unittest.TestCase):
             ops_blocks.append(op)
             break
         self.assertTrue(len(ops_blocks) == 1)
+
+    @parameterized.expand([
+        ("non_appbase"),
+        ("appbase"),
+    ])
+    def test_wait_for_and_get_block(self, node_param):
+        if node_param == "non_appbase":
+            bts = self.bts
+        else:
+            bts = self.appbase
+        b = Blockchain(steem_instance=bts)
+        start_num = b.get_current_block_num()
+        blocknum = start_num
+        last_fetched_block_num = None
+        for i in range(3):
+            block = b.wait_for_and_get_block(blocknum)
+            last_fetched_block_num = block.block_num
+            blocknum = last_fetched_block_num + 2
+        self.assertEqual(last_fetched_block_num, start_num + 4)
diff --git a/tests/beem/test_blockchain_batch.py b/tests/beem/test_blockchain_batch.py
index 92c6e4d7..7746c04e 100644
--- a/tests/beem/test_blockchain_batch.py
+++ b/tests/beem/test_blockchain_batch.py
@@ -43,10 +43,10 @@ class Testcases(unittest.TestCase):
         b = Blockchain(steem_instance=bts)
         ops_stream = []
         opNames = ["transfer", "vote"]
-        for op in b.stream(opNames=opNames, start=self.start, stop=self.stop):
+        for op in b.stream(opNames=opNames, start=self.start, stop=self.stop, max_batch_size=self.max_batch_size, threading=False):
             ops_stream.append(op)
-        self.assertTrue(len(ops_stream) > 0)
         op_stat = b.ops_statistics(start=self.start, stop=self.stop)
+        self.assertEqual(op_stat["vote"] + op_stat["transfer"], len(ops_stream))
         ops_blocks = []
         for op in b.blocks(start=self.start, stop=self.stop, max_batch_size=self.max_batch_size, threading=False):
             ops_blocks.append(op)
diff --git a/tests/beem/test_blockchain_threading.py b/tests/beem/test_blockchain_threading.py
index 846125a8..16a4ea78 100644
--- a/tests/beem/test_blockchain_threading.py
+++ b/tests/beem/test_blockchain_threading.py
@@ -44,10 +44,11 @@ class Testcases(unittest.TestCase):
         b = Blockchain(steem_instance=bts)
         ops_stream = []
         opNames = ["transfer", "vote"]
-        for op in b.stream(opNames=opNames, start=self.start, stop=self.stop):
+        for op in b.stream(opNames=opNames, start=self.start, stop=self.stop, threading=True, thread_num=8):
             ops_stream.append(op)
         self.assertTrue(len(ops_stream) > 0)
         op_stat = b.ops_statistics(start=self.start, stop=self.stop)
+        self.assertEqual(op_stat["vote"] + op_stat["transfer"], len(ops_stream))
         ops_blocks = []
         for op in b.blocks(start=self.start, stop=self.stop, threading=True, thread_num=8):
             ops_blocks.append(op)
diff --git a/tests/beem/test_cli.py b/tests/beem/test_cli.py
index d3829d8e..c63ee46c 100644
--- a/tests/beem/test_cli.py
+++ b/tests/beem/test_cli.py
@@ -404,6 +404,27 @@ class Testcases(unittest.TestCase):
         runner.invoke(cli, ['-o', 'set', 'nodes', 'wss://testnet.steem.vc'])
         self.assertEqual(result.exit_code, 0)
 
+    def test_rewards(self):
+        runner = CliRunner()
+        runner.invoke(cli, ['-o', 'set', 'nodes', ''])
+        result = runner.invoke(cli, ['rewards', '--post', '--comment', '--curation', 'test'])
+        runner.invoke(cli, ['-o', 'set', 'nodes', 'wss://testnet.steem.vc'])
+        self.assertEqual(result.exit_code, 0)
+
+    def test_curation(self):
+        runner = CliRunner()
+        runner.invoke(cli, ['-o', 'set', 'nodes', ''])
+        result = runner.invoke(cli, ['curation', "@gtg/witness-gtg-log"])
+        runner.invoke(cli, ['-o', 'set', 'nodes', 'wss://testnet.steem.vc'])
+        self.assertEqual(result.exit_code, 0)
+
+    def test_verify(self):
+        runner = CliRunner()
+        runner.invoke(cli, ['-o', 'set', 'nodes', ''])
+        result = runner.invoke(cli, ['verify', '--trx', '0'])
+        runner.invoke(cli, ['-o', 'set', 'nodes', 'wss://testnet.steem.vc'])
+        self.assertEqual(result.exit_code, 0)
+
     def test_tradehistory(self):
         runner = CliRunner()
         runner.invoke(cli, ['-o', 'set', 'nodes', ''])
-- 
GitLab