Exemple #1
0
def scrape_block_range(ctx, start_block_number, end_block_number, mode,
                       fill_gaps):
    """
    Pushes the data between start_block_number and end_block_number in the
    database. If no values are provided, the start_block_number is the last
    block_number+1 in sql and end_block_number is the current block_number in
    node. Also checks for missing blocks and adds them to the list of required
    block numbers

    :param int start_block_number: starting block number of scraping
    :param int end_block_number: end block number of scraping
    :param str mode: Mode of data sync 'parallel' or single
    :param bool fill_gaps: If switched on instructs to also fill missing blocks
    """

    current_session = get_current_session()
    with current_session.db_session_scope():
        sql_block_number = Blocks.get_max_block_number()
    if end_block_number is None:
        end_block_number = current_session.w3.eth.blockNumber
        logger.debug(end_block_number)
    if start_block_number is None:
        if sql_block_number is None:
            start_block_number = 0
        elif sql_block_number == end_block_number:
            start_block_number = sql_block_number
        else:
            start_block_number = sql_block_number + 1
    logger.debug(start_block_number)

    # casting numbers to integers
    if start_block_number == end_block_number:
        list_block_numbers = []
    else:
        start_block_number = int(start_block_number)
        end_block_number = int(end_block_number)
        list_block_numbers = list(
            range(start_block_number, end_block_number + 1))

    if fill_gaps and start_block_number != 0:
        missing_blocks = Blocks.missing_blocks(sql_block_number)
        logger.debug(missing_blocks)
        logger.info('{} missing blocks detected'.format(len(missing_blocks)))
        for missing in missing_blocks:
            logger.debug(missing.block_number)
            list_block_numbers.append(missing.block_number)

    logger.debug(list_block_numbers)
    if len(list_block_numbers) == 0:
        logger.warning('No blocks pushed in database')
    if mode == 'parallel':
        scrape_blocks(list_block_numbers=list_block_numbers, mode=mode)
    elif mode == 'single':
        scrape_blocks(list_block_numbers=list_block_numbers, mode=mode)
    else:
        raise ValueError('The mode: {} is not recognized'.format(mode))
Exemple #2
0
def fill_missing_blocks():
    session = get_current_session()
    runner = CliRunner()
    runner.invoke(cli, ['--settings', session.setting_name,
                        'scrape_block_range',
                        '--end_block_number', 10,
                        '--fill_gaps'])
    assert len(Blocks.missing_blocks(10)) == 0
Exemple #3
0
def final_missing_blocks(list_blocks=list_final_missing_blocks):
    final_missing_blocks = Blocks.missing_blocks(max_block_number=15)
    assert len(final_missing_blocks) == len(list_blocks)
    for index, result in enumerate(final_missing_blocks):
        assert result.block_number == list_blocks[index]
Exemple #4
0
def initial_missing_blocks(list_blocks=list_initial_missing_blocks):
    initial_missing_blocks = Blocks.missing_blocks()
    assert len(initial_missing_blocks) == len(list_blocks)
    for index, result in enumerate(initial_missing_blocks):
        assert result.block_number == list_blocks[index]
Exemple #5
0
def add_block_number(block_number):
    """
    Adds the block, transactions, uncles, logs and traces of a given block
    number into the db_session

    :param int block_number: The block number to add to the database
    """
    current_session = get_current_session()
    block_task_meta = BlockTaskMeta.update_block_task_meta_from_block_number(
        block_number=block_number, state='STARTED')

    # getting the block_data from the node
    block_data = current_session.w3.eth.getBlock(block_identifier=block_number,
                                                 full_transactions=True)
    timestamp = to_int(block_data['timestamp'])
    iso_timestamp = datetime.utcfromtimestamp(timestamp).isoformat()
    block = Blocks.add_block(block_data=block_data,
                             iso_timestamp=iso_timestamp)
    block_hash = block.block_hash

    if current_session.settings.PARSE_TRACE and \
       current_session.settings.PARSE_STATE_DIFF and \
       block_number != 0:
        block_trace_list = current_session.w3.parity.\
            traceReplayBlockTransactions(block_number,
                                         mode=['trace', 'stateDiff'])
    elif block_number != 0:
        if current_session.settings.PARSE_TRACE:
            block_trace_list = current_session.w3.parity.\
                traceReplayBlockTransactions(block_number,
                                             mode=['trace'])

        if current_session.settings.PARSE_STATE_DIFF:
            block_trace_list = current_session.w3.parity.\
                traceReplayBlockTransactions(block_number,
                                             mode=['stateDiff'])

    # added the block data in the db session
    with current_session.db_session_scope():
        current_session.db_session.add(block)

        uncle_hashes = block_data['uncles']
        uncle_list = []
        for i in range(0, len(uncle_hashes)):
            # Unfortunately there is no command eth_getUncleByHash
            uncle_data = current_session.w3.eth.getUncleByBlock(
                block_number, i)
            uncle = Uncles.add_uncle(uncle_data=uncle_data,
                                     block_number=block_number,
                                     iso_timestamp=iso_timestamp)
            current_session.db_session.add(uncle)
            uncle_list.append(uncle)

        transaction_list = block_data['transactions']
        # loop to get the transaction, receipts, logs and traces of the block
        for index, transaction_data in enumerate(transaction_list):
            transaction = Transactions.add_transaction(
                transaction_data,
                block_number=block_number,
                iso_timestamp=iso_timestamp)
            # added the transaction in the db session
            current_session.db_session.add(transaction)

            receipt_data = current_session.w3.eth.getTransactionReceipt(
                transaction_data['hash'])
            receipt = Receipts.add_receipt(receipt_data,
                                           block_number=block_number,
                                           timestamp=iso_timestamp)
            current_session.db_session.add(receipt)
            fees = int(transaction.gas_price) * int(receipt.gas_used)

            log_list = receipt_data['logs']
            Logs.add_log_list(current_session=current_session,
                              log_list=log_list,
                              block_number=block_number,
                              timestamp=transaction.timestamp)

            if current_session.settings.PARSE_TRACE:
                trace_list = block_trace_list[index]['trace']
                Traces.add_trace_list(
                    current_session=current_session,
                    trace_list=trace_list,
                    transaction_hash=transaction.transaction_hash,
                    transaction_index=transaction.transaction_index,
                    block_number=transaction.block_number,
                    timestamp=transaction.timestamp)

            if current_session.settings.PARSE_STATE_DIFF:
                state_diff_dict = block_trace_list[index]['stateDiff']
                if state_diff_dict is not None:
                    StateDiff.add_state_diff_dict(
                        current_session=current_session,
                        state_diff_dict=state_diff_dict,
                        transaction_hash=transaction.transaction_hash,
                        transaction_index=transaction.transaction_index,
                        block_number=transaction.block_number,
                        timestamp=transaction.timestamp,
                        miner=block.miner,
                        fees=fees)
        if block_number == 0:
            StateDiff.parse_genesis_rewards(current_session=current_session,
                                            block=block)
        else:
            StateDiff.add_mining_rewards(current_session=current_session,
                                         block=block,
                                         uncle_list=uncle_list)
        # updating the meta info table
        MetaInfo.set_last_pushed_block(current_session, block_number)
    logger.info("Commiting block: {} to sql".format(block_number))
    return block_hash
Exemple #6
0
def blockNumber(ctx):
    """ Gives the current highest block in database"""
    click.echo(Blocks.get_max_block_number())
Exemple #7
0
def add_block_number(block_number, ether_sql_session):
    """
    Adds the block, transactions, uncles, logs and traces of a given block
    number into the db_session

    :param int block_number: The block number to add to the database
    """
    # getting the block_data from the node
    block_data = ether_sql_session.w3.eth.getBlock(
        block_identifier=block_number, full_transactions=True)
    timestamp = to_int(block_data['timestamp'])
    iso_timestamp = datetime.utcfromtimestamp(timestamp).isoformat()
    block = Blocks.add_block(block_data=block_data,
                             iso_timestamp=iso_timestamp)
    ether_sql_session.db_session.add(
        block)  # added the block data in the db session

    logger.debug('Reached this spot')
    transaction_list = block_data['transactions']
    # loop to get the transaction, receipts, logs and traces of the block
    for transaction_data in transaction_list:
        transaction = Transactions.add_transaction(transaction_data,
                                                   block_number=block_number,
                                                   iso_timestamp=iso_timestamp)
        # added the transaction in the db session
        ether_sql_session.db_session.add(transaction)

        receipt_data = ether_sql_session.w3.eth.getTransactionReceipt(
            transaction_data['hash'])
        receipt = Receipts.add_receipt(receipt_data,
                                       block_number=block_number,
                                       timestamp=iso_timestamp)

        ether_sql_session.db_session.add(
            receipt)  # added the receipt in the database

        logs_list = receipt_data['logs']
        for dict_log in logs_list:
            log = Logs.add_log(dict_log,
                               block_number=block_number,
                               iso_timestamp=iso_timestamp)
            ether_sql_session.db_session.add(
                log)  # adding the log in db session

        if ether_sql_session.settings.PARSE_TRACE:
            dict_trace_list = ether_sql_session.w3.parity.traceTransaction(
                to_hex(transaction_data['hash']))
            if dict_trace_list is not None:
                for dict_trace in dict_trace_list:
                    trace = Traces.add_trace(dict_trace,
                                             block_number=block_number,
                                             timestamp=iso_timestamp)
                    ether_sql_session.db_session.add(
                        trace)  # added the trace in the db session

    uncle_list = block_data['uncles']
    for i in range(0, len(uncle_list)):
        # Unfortunately there is no command eth_getUncleByHash
        uncle_data = ether_sql_session.w3.eth.getUncleByBlock(block_number, i)
        uncle = Uncles.add_uncle(uncle_data=uncle_data,
                                 block_number=block_number,
                                 iso_timestamp=iso_timestamp)
        ether_sql_session.db_session.add(uncle)

    return ether_sql_session
Exemple #8
0
    def get_state_at_block(cls, block_number=None):
        """
        Updates the state with either specified `block_number` or the maximum
        ``block_number`` in the database. Also, checks if there are any missing
        blocks in the database, if yes then stops the calculation of state
        prematurely.

        : param int block_number: Block number of desired state, if none then
        constructs the state for the highest available state_diff
        """
        # checks if there are any gaps in the block_numbers
        if block_number is None:
            block_number = Blocks.get_max_block_number()

        missing_block_numbers = Blocks.missing_blocks(block_number)
        if len(missing_block_numbers) > 0:
            raise MissingBlocksError('Cannot construct state at block {}, \
                    {} blocks are missing'.format(block_number,
                                                  len(missing_block_numbers)))

        if MetaInfo.get_current_state_block == block_number:
            logger.info('State is already at block {}'.format(block_number))
            return

        current_session = get_current_session()
        with current_session.db_session_scope():
            current_session.db_session.query(Storage).delete()
            current_session.db_session.query(cls).delete()
            # query to get the balance
            query_balance = current_session.db_session.query(
                            StateDiff.address,
                            func.sum(StateDiff.balance_diff).label('balance'),
                            func.sum(StateDiff.nonce_diff).label('nonce')).\
                filter(StateDiff.block_number <= block_number).\
                group_by(StateDiff.address)
            subquery_balance = query_balance.subquery()
            # query to get the code
            row_number_column = func.row_number().over(
                partition_by=StateDiff.address,
                order_by=[StateDiff.block_number.desc(),
                          StateDiff.transaction_index.desc()])\
                .label('row_number')
            query_code = current_session.db_session.query(
                StateDiff.address.label('address'),
                StateDiff.code_to.label('code'))
            query_code = query_code.add_column(row_number_column)
            query_code = query_code.filter(
                or_(StateDiff.code_from != None, StateDiff.code_to != None))
            query_code = query_code.filter(
                StateDiff.block_number <= block_number)
            query_code = query_code.from_self().filter(row_number_column == 1)
            query_code = query_code.filter(StateDiff.code_to != '0x')
            subquery_code = query_code.subquery()
            # joining the two queries
            query_state = current_session.db_session.query(
                subquery_balance.c.address, subquery_balance.c.balance,
                subquery_balance.c.nonce, subquery_code.c.code)
            query_state = query_state.outerjoin(
                subquery_code,
                subquery_balance.c.address == subquery_code.c.address)

            # updating the state table with query results
            for row in query_state:
                state = cls.add_state(address=row.address,
                                      balance=row.balance,
                                      nonce=row.nonce,
                                      code=row.code)
                current_session.db_session.add(state)
            # update the meta_info.current_state_block
            MetaInfo.set_current_state_block(current_session, block_number)

        with current_session.db_session_scope():
            # update the storage table
            Storage.get_storage_at_block(current_session, block_number)