def _transaction_from_explorer_transaction( self, etxn, endpoint="/?", resp=None): # keyword parameters for error handling purposes only if resp == None: resp = jsobj.new_dict() # parse the transactions transaction = transactions.from_json(obj=etxn['rawtransaction'], id=etxn['id']) # add the parent (coin) outputs coininputoutputs = etxn.get_or('coininputoutputs', None) or [] if len(transaction.coin_inputs) != len(coininputoutputs): raise tferrors.ExplorerInvalidResponse( "amount of coin inputs and parent outputs are not matching: {} != {}" .format(len(transaction.coin_inputs), len(coininputoutputs)), endpoint, resp) for (idx, co) in enumerate(coininputoutputs): co = CoinOutput.from_json(obj=co) co.id = transaction.coin_inputs[idx].parentid transaction.coin_inputs[idx].parent_output = co # add the coin output ids coinoutputids = etxn.get_or('coinoutputids', None) or [] if len(transaction.coin_outputs) != len(coinoutputids): raise tferrors.ExplorerInvalidResponse( "amount of coin outputs and output identifiers are not matching: {} != {}" .format(len(transaction.coin_outputs), len(coinoutputids)), endpoint, resp) for (idx, id) in enumerate(coinoutputids): transaction.coin_outputs[idx].id = Hash.from_json(obj=id) # add the parent (blockstake) outputs blockstakeinputoutputs = etxn.get_or('blockstakeinputoutputs', None) or [] if len(transaction.blockstake_inputs) != len(blockstakeinputoutputs): raise tferrors.ExplorerInvalidResponse( "amount of blockstake inputs and parent outputs are not matching: {} != {}" .format(len(transaction.blockstake_inputs), len(blockstakeinputoutputs)), endpoint, resp) for (idx, bso) in enumerate(blockstakeinputoutputs): bso = BlockstakeOutput.from_json(obj=bso) bso.id = transaction.blockstake_inputs[idx].parentid transaction.blockstake_inputs[idx].parent_output = bso # add the blockstake output ids blockstakeoutputids = etxn.get_or('blockstakeoutputids', None) or [] if len(transaction.blockstake_outputs) != len(blockstakeoutputids): raise tferrors.ExplorerInvalidResponse( "amount of blokstake outputs and output identifiers are not matching: {} != {}" .format(len(transaction.blockstake_inputs), len(blockstakeoutputids)), endpoint, resp) for (idx, id) in enumerate(blockstakeoutputids): transaction.blockstake_outputs[idx].id = Hash.from_json(obj=id) # set the unconfirmed state transaction.unconfirmed = etxn.get_or('unconfirmed', False) # set the blockid and height of the transaction only if confirmed if not transaction.unconfirmed: transaction.height = int(etxn.get_or('height', -1)) transaction.blockid = etxn.get_or('parent', None) # return the transaction return transaction
def _block_get_parse_cb(self, result): endpoint, block = result try: # parse the transactions transactions = [] for etxn in block['transactions']: # parse the explorer transaction transaction = self._transaction_from_explorer_transaction( etxn, endpoint=endpoint, resp=block) # append the transaction to the list of transactions transactions.append(transaction) rawblock = block['rawblock'] # parse the parent id parentid = Hash.from_json(obj=rawblock['parentid']) # parse the miner payouts miner_payouts = [] minerpayoutids = block.get_or('minerpayoutids', None) or [] eminerpayouts = rawblock.get_or('minerpayouts', None) or [] if len(eminerpayouts) != len(minerpayoutids): raise tferrors.ExplorerInvalidResponse( "amount of miner payouts and payout ids are not matching: {} != {}" .format(len(eminerpayouts), len(minerpayoutids)), endpoint, block) for idx, mp in enumerate(eminerpayouts): id = Hash.from_json(minerpayoutids[idx]) value = Currency.from_json(mp['value']) unlockhash = UnlockHash.from_json(mp['unlockhash']) miner_payouts.append( ExplorerMinerPayout(id=id, value=value, unlockhash=unlockhash)) # get the timestamp and height height = int(block['height']) timestamp = int(rawblock['timestamp']) # get the block's identifier blockid = Hash.from_json(block['blockid']) # for all transactions assign these properties for transaction in transactions: _assign_block_properties_to_transacton(transaction, block) transaction.height = height transaction.blockid = blockid # return the block, as reported by the explorer return ExplorerBlock(id=blockid, parentid=parentid, height=height, timestamp=timestamp, transactions=transactions, miner_payouts=miner_payouts) except KeyError as exc: raise tferrors.ExplorerInvalidResponse(str(exc), endpoint, block) from exc
def get_block(result): used_addr, raw_block = result address = used_addr def get_block_with_tag(result): return ('b', (address, result)) blockid = Hash.from_json(obj=raw_block['blockid']) return jsasync.chain(self.block_get(blockid), get_block_with_tag)
def blockchain_info_get(self): """ Get the current blockchain info, using the last known block, as reported by an explorer. """ resp = self.explorer_get(endpoint="/explorer") resp = json_loads(resp) blockid = Hash.from_json(obj=resp['blockid']) last_block = self.block_get(blockid) return ExplorerBlockchainInfo(last_block=last_block)
def from_json(cls, obj): if not obj: return cls() if not isinstance(obj, str): raise TypeError( "expected JSON-encoded PublicKey to be a string, not {}". format(type(obj))) parts = jsstr.split(obj, ':', 2) if len(parts) != 2: raise ValueError("invalid JSON-encoded PublicKey: {}".format(obj)) pk = cls() pk._specifier = PublicKeySpecifier.from_json(parts[0]) pk._hash = Hash.from_json(parts[1]) return pk
def from_json(cls, obj): return cls( parentid=Hash.from_json(obj['parentid']), fulfillment=FulfillmentTypes.from_json(obj['fulfillment']))
def block_get(self, value): """ Get a block from an available explorer Node. @param value: the identifier or height that points to the desired block """ endpoint = "/explorer/?" resp = {} try: # get the explorer block if isinstance(value, int): endpoint = "/explorer/blocks/{}".format(int(value)) resp = self.explorer_get(endpoint=endpoint) resp = json_loads(resp) resp = resp['block'] else: blockid = self._normalize_id(value) endpoint = "/explorer/hashes/" + blockid resp = self.explorer_get(endpoint=endpoint) resp = json_loads(resp) if resp['hashtype'] != 'blockid': raise tfchain.errors.ExplorerInvalidResponse( "expected hash type 'blockid' not '{}'".format( resp['hashtype']), endpoint, resp) resp = resp['block'] if resp['blockid'] != blockid: raise tfchain.errors.ExplorerInvalidResponse( "expected block ID '{}' not '{}'".format( blockid, resp['blockid']), endpoint, resp) # parse the transactions transactions = [] for etxn in resp['transactions']: # parse the explorer transaction transaction = self._transaction_from_explorer_transaction( etxn, endpoint=endpoint, resp=resp) # append the transaction to the list of transactions transactions.append(transaction) rawblock = resp['rawblock'] # parse the parent id parentid = Hash.from_json(obj=rawblock['parentid']) # parse the miner payouts miner_payouts = [] minerpayoutids = resp.get('minerpayoutids', None) or [] eminerpayouts = rawblock.get('minerpayouts', None) or [] if len(eminerpayouts) != len(minerpayoutids): raise tfchain.errors.ExplorerInvalidResponse( "amount of miner payouts and payout ids are not matching: {} != {}" .format(len(eminerpayouts), len(minerpayoutids)), endpoint, resp) for idx, mp in enumerate(eminerpayouts): id = Hash.from_json(minerpayoutids[idx]) value = Currency.from_json(mp['value']) unlockhash = UnlockHash.from_json(mp['unlockhash']) miner_payouts.append( ExplorerMinerPayout(id=id, value=value, unlockhash=unlockhash)) # get the timestamp and height height = int(resp['height']) timestamp = int(rawblock['timestamp']) # get the block's identifier blockid = Hash.from_json(resp['blockid']) # return the block, as reported by the explorer return ExplorerBlock(id=blockid, parentid=parentid, height=height, timestamp=timestamp, transactions=transactions, miner_payouts=miner_payouts) except KeyError as exc: # return a KeyError as an invalid Explorer Response raise tfchain.errors.ExplorerInvalidResponse( str(exc), endpoint, resp) from exc