diff --git a/bitshares/blockchain.py b/bitshares/blockchain.py index 61eff3a3..e6bd46de 100644 --- a/bitshares/blockchain.py +++ b/bitshares/blockchain.py @@ -12,13 +12,16 @@ class Blockchain(object): instance :param str mode: (default) Irreversible block (``irreversible``) or actual head block (``head``) + :param int max_block_wait_repetition: (default) 3 maximum wait time for next block + is max_block_wait_repetition * block_interval This class let's you deal with blockchain related data and methods. """ def __init__( self, bitshares_instance=None, - mode="irreversible" + mode="irreversible", + max_block_wait_repetition=None ): self.bitshares = bitshares_instance or shared_bitshares_instance() @@ -29,6 +32,14 @@ def __init__( else: raise ValueError("invalid value for 'mode'!") + if max_block_wait_repetition: + self.max_block_wait_repetition = max_block_wait_repetition + else: + self.max_block_wait_repetition = 3 + + def is_irreversible_mode(self): + return self.mode == 'last_irreversible_block_num' + def info(self): """ This call returns the *dynamic global properties* """ @@ -109,7 +120,7 @@ def blocks(self, start=None, stop=None): confirmed by 2/3 of all block producers and is thus irreversible) """ # Let's find out how often blocks are generated! - block_interval = self.chainParameters().get("block_interval") + self.block_interval = self.chainParameters().get("block_interval") if not start: start = self.get_current_block_num() @@ -126,7 +137,7 @@ def blocks(self, start=None, stop=None): # Blocks from start until head block for blocknum in range(start, head_block + 1): # Get full block - block = self.bitshares.rpc.get_block(blocknum) + block = self.wait_for_and_get_block(blocknum) block.update({"block_num": blocknum}) yield block # Set new start @@ -137,7 +148,36 @@ def blocks(self, start=None, stop=None): return # Sleep for one block - time.sleep(block_interval) + time.sleep(self.block_interval) + + def wait_for_and_get_block(self, block_number, blocks_waiting_for=None): + """ 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 + """ + 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) + while self.get_current_block_num() < block_number: + repetition += 1 + time.sleep(self.block_interval) + 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 = self.bitshares.rpc.get_block(block_number) + repetition = 0 + while not block: + repetition += 1 + time.sleep(self.block_interval) + if repetition > self.max_block_wait_repetition: + raise Exception("Wait time for new block exceeded, aborting") + block = self.bitshares.rpc.get_block(block_number) + return block def ops(self, start=None, stop=None, **kwargs): """ Yields all operations (including virtual operations) starting from