예제 #1
0
    async def async_getheaders(self, param):
        """
        Async. Return 20 latest block headers.

        :param param: (string) empty: last 20 blocks headers. start_height,count or ,count (last N headers)
        :return:
        """
        try:
            protocmd = commands_pb2.Command()
            protocmd.Clear()
            protocmd.command = commands_pb2.Command.getheaders
            if param is None or param == 'None':
                param = ''
            if param == '':
                blocks = await self.async_fetchall(SQL_BLOCKS_LAST, (config.BLOCK_SYNC_COUNT,))
            else:
                start, count = param.split(',')
                if '' == start:
                    blocks = await self.async_fetchall(SQL_BLOCKS_LAST, (count,))
                else:
                    blocks = await self.async_fetchall(SQL_BLOCKS_SYNC, (start, count))
            for block in blocks:
                block = PosBlock().from_dict(dict(block))
                block.add_to_proto(protocmd)
            return protocmd
        except Exception as e:
            self.app_log.error("SRV: async_getheaders: Error {}".format(e))
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            self.app_log.error('detail {} {} {}'.format(exc_type, fname, exc_tb.tb_lineno))
            raise
예제 #2
0
    async def async_getblock(self, a_height):
        """
        Command id 14
        returns the block of the given height.

        :param a_height: int
        :return: protocmd with the block if exists or None
        """
        try:
            protocmd = commands_pb2.Command()
            protocmd.Clear()
            protocmd.command = commands_pb2.Command.getblock

            block = await self.async_fetchone(SQL_HEIGHT_BLOCK, (a_height,), as_dict=True)
            if not block:
                return protocmd
            block = PosBlock().from_dict(dict(block))
            # Add the block txs
            txs = await self.async_fetchall(SQL_TXS_FOR_HEIGHT, (block.height,))
            for tx in txs:
                tx = PosMessage().from_dict(dict(tx))
                block.txs.append(tx)
            block.add_to_proto(protocmd)
            return protocmd

        except Exception as e:
            self.app_log.error("SRV: async_getblock: Error {}".format(e))
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            self.app_log.error('detail {} {} {}'.format(exc_type, fname, exc_tb.tb_lineno))
            raise
예제 #3
0
    async def async_roundblocks(self, a_round):
        """
        Command id 13
        returns all blocks of the given round.

        FR: Harmonize. this one needs a proto as output (proto command with list of blocks)

        :param a_round:
        :return: protocmd with all blocks
        """
        try:
            protocmd = commands_pb2.Command()
            protocmd.Clear()
            protocmd.command = commands_pb2.Command.roundblocks

            blocks = await self.async_fetchall(SQL_ROUND_BLOCKS, (a_round, ))
            for block in blocks:
                block = PosBlock().from_dict(dict(block))
                # Add the block txs
                txs = await self.async_fetchall(SQL_TXS_FOR_HEIGHT,
                                                (block.height, ))
                for tx in txs:
                    tx = PosMessage().from_dict(dict(tx))
                    block.txs.append(tx)
                block.add_to_proto(protocmd)
                # check block integrity
                if len(txs) != block.msg_count:
                    self.app_log.error(
                        "Only {} tx for block {} instead of {} announced".
                        format(len(txs), block.height, block.msg_count))
                    com_helpers.MY_NODE.stop()

            return protocmd

        except Exception as e:
            self.app_log.error("SRV: async_roundblocks: Error {}".format(e))
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            self.app_log.error('detail {} {} {}'.format(
                exc_type, fname, exc_tb.tb_lineno))
            raise
예제 #4
0
    async def async_blocksync(self, height):
        """
        returns N blocks starting with the given height.

        FR: Harmonize. this one needs a proto as output (proto command with list of blocks)

        :param height:
        :return:
        """
        try:
            protocmd = commands_pb2.Command()
            protocmd.Clear()
            protocmd.command = commands_pb2.Command.blocksync

            blocks = await self.async_fetchall(
                SQL_BLOCKS_SYNC, (height, config.BLOCK_SYNC_COUNT))
            for block in blocks:
                block = PosBlock().from_dict(dict(block))
                # Add the block txs
                txs = await self.async_fetchall(SQL_TXS_FOR_HEIGHT,
                                                (block.height, ))
                for tx in txs:
                    tx = PosMessage().from_dict(dict(tx))
                    block.txs.append(tx)
                # check block integrity
                if len(txs) != block.msg_count:
                    self.app_log.error(
                        "Only {} tx for block {} instead of {} announced".
                        format(len(txs), height, block.msg_count))
                    com_helpers.MY_NODE.stop()
                block.add_to_proto(protocmd)
            #print(protocmd)
            return protocmd
        except Exception as e:
            self.app_log.error("SRV: async_blocksync: Error {}".format(e))
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            self.app_log.error('detail {} {} {}'.format(
                exc_type, fname, exc_tb.tb_lineno))
            raise
예제 #5
0
    def genesis_block(self):
        """
        Build up genesis block info

        :return:
        """
        # No tx for genesis
        txids = []
        block_dict = {'height': 0, 'round': 0, 'sir': 0, 'timestamp': config.ORIGIN_OF_TIME,
                      'previous_hash': poscrypto.blake(config.GENESIS_SEED.encode('utf-8')).digest(),
                      'msg_count': 0, 'uniques_sources': 0, 'txs': txids, 'forger': config.GENESIS_ADDRESS,
                      'block_hash': b'', 'signature': b''}
        # print(block_dict)
        block = PosBlock().from_dict(block_dict)
        # print(block.to_json())
        block.sign()
        print(block.to_json())
        if self.verbose:
            print(block.to_json())
        return block
예제 #6
0
    async def digest_block(self, proto_block, from_miner=False, relaxed_checks=False):
        """
        Checks if the block is valid and saves it

        :param proto_block: a protobuf 'block' object
        :param from_miner: True if came from a live miner (current slot)
        :param relaxed_checks: True if we want light checks (like our own block)

        :return:
        """
        try:
            block_from = 'from Peer'
            if from_miner:
                block_from = 'from Miner'
            if relaxed_checks:
                block_from += ' (Relaxed checks)'
            # Avoid re-entrance
            if self.inserting_block:
                self.app_log.warning("Digestion of block {} aborted".format(block_from))
                return
            # print(">> protoblock", proto_block)
            block = PosBlock().from_proto(proto_block)
            # print(">> dictblock", block.to_dict())
            if 'txdigest' in config.LOG:
                self.app_log.warning("Digesting block {} {} : {}".format(block.height, block_from, block.to_json()))
            else:
                self.app_log.warning("Digesting block {} {} : {} txs, {} uniques sources.".format(block.height, block_from, len(block.txs), block.uniques_sources))
            # Good height? - FR: harmonize, use objects everywhere?
            if block.height != self.block['height'] + 1:
                self.app_log.warning("Digesting block {} : bad height, our current height is {}"
                                     .format(block.height, self.block['height']))
                return False
            # Good hash?
            if block.previous_hash != self.block['block_hash']:
                self.app_log.warning("Digesting block {} : bad hash {} vs our {}"
                                     .format(block.height, block.previous_hash, self.block['block_hash']))
                return False
            if block.msg_count != len(block.txs):
                self.app_log.warning("Digesting block {} : {} txs reported but {} included"
                                     .format(block.height, block.msg_count, len(block.txs)))
                return False
            # TODO: more checks
            # TODO: if from miner, make sure we refreshed the round first.
            # timestamp of blocks
            # fits with current round?
            # TODO : only tx from valid HNs (registered for that round?)
            # recount uniques_sources?
            # msg_count = tx# ?
            # right juror?
            # Checks will depend on from_miner (state = sync) or not (relaxed checks when catching up)
            # see also tx checks from mempool. Maybe lighter
            self.inserting_block = True
            await self._insert_block(block)
            self.app_log.warning("Digested block {}".format(block.height))
            return True
        except Exception as e:
            self.app_log.error("digest_block Error {}".format(e))
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            self.app_log.error('detail {} {} {}'.format(exc_type, fname, exc_tb.tb_lineno))
            return False
        finally:
            self.inserting_block = False