Exemple #1
0
def start_harvester(self, check_gaps=False):

    substrate = SubstrateInterface(url=SUBSTRATE_RPC_URL,
                                   type_registry_preset=settings.TYPE_REGISTRY,
                                   runtime_config=RuntimeConfiguration())

    block_sets = []

    if check_gaps:
        # Check for gaps between already harvested blocks and try to fill them first
        remaining_sets_result = Block.get_missing_block_ids(self.session)

        for block_set in remaining_sets_result:

            # Get start and end block hash
            end_block_hash = substrate.get_block_hash(
                int(block_set['block_from']))
            start_block_hash = substrate.get_block_hash(
                int(block_set['block_to']))

            # Start processing task
            accumulate_block_recursive.delay(start_block_hash, end_block_hash)

            block_sets.append({
                'start_block_hash': start_block_hash,
                'end_block_hash': end_block_hash
            })

    # Start sequencer
    sequencer_task = start_sequencer.delay()

    # Continue from current (finalised) head
    if FINALIZATION_ONLY == 1:
        start_block_hash = substrate.get_chain_finalised_head()
    else:
        start_block_hash = substrate.get_chain_head()

    end_block_hash = None

    accumulate_block_recursive.delay(start_block_hash, end_block_hash)

    block_sets.append({
        'start_block_hash': start_block_hash,
        'end_block_hash': end_block_hash
    })

    return {
        'result': 'Harvester job started',
        'block_sets': block_sets,
        'sequencer_task_id': sequencer_task.task_id
    }
def start_init(self):
    if self.init:
        print('start_init  task is running : '.format(self.init))
        return {'result': 'waiting init task completed! '}

    n = Block.query(self.session).filter_by(bid=1).count()

    if n >= 4:
        print(' init task is completed! count().n: {} '.format(n))

        return {'result': ' init task is completed! '}
    self.init = True
    print("start_init")
    for shard in SHARDS_TABLE:
        substrate_url = SHARDS_TABLE[shard]
        substrate = SubstrateInterface(substrate_url)
        start_block_hash = substrate.get_block_hash(3)

        print('== start_init  substrate_url {} ==start_block_hash-{}'.format(
            substrate_url, start_block_hash))

        end_block_hash = None

        accumulate_block_recursive.delay(start_block_hash, end_block_hash,
                                         substrate_url)

    return {
        'result': 'start_init job started',
        'SHARDS_TABLE': SHARDS_TABLE,
        'init_task_id': self.request.id
    }
    def on_post(self, req, resp):

        block_hash = None

        if req.media.get('block_id'):
            substrate = SubstrateInterface(
                url=SUBSTRATE_RPC_URL,
                runtime_config=RuntimeConfiguration(),
                type_registry_preset=settings.TYPE_REGISTRY)
            block_hash = substrate.get_block_hash(req.media.get('block_id'))
        elif req.media.get('block_hash'):
            block_hash = req.media.get('block_hash')
        else:
            resp.status = falcon.HTTP_BAD_REQUEST
            resp.media = {
                'errors': ['Either block_hash or block_id should be supplied']
            }

        if block_hash:
            print('Processing {} ...'.format(block_hash))
            harvester = PolkascanHarvesterService(
                db_session=self.session,
                type_registry=TYPE_REGISTRY,
                type_registry_file=TYPE_REGISTRY_FILE + '-mst')

            block = Block.query(
                self.session).filter(Block.hash == block_hash).first()

            if block:
                resp.status = falcon.HTTP_200
                resp.media = {
                    'result': 'already exists',
                    'parentHash': block.parent_hash
                }
            else:

                amount = req.media.get('amount', 1)

                for nr in range(0, amount):
                    try:
                        block = harvester.add_block(block_hash)
                    except BlockAlreadyAdded as e:
                        print('Skipping {}'.format(block_hash))
                    block_hash = block.parent_hash
                    if block.id == 0:
                        break

                self.session.commit()

                resp.status = falcon.HTTP_201
                resp.media = {
                    'result': 'added',
                    'parentHash': block.parent_hash
                }

        else:
            resp.status = falcon.HTTP_404
            resp.media = {'result': 'Block not found'}
def start_harvester(self, check_gaps=False):

    print("---------- {}".format(check_gaps))
    substrate = SubstrateInterface(SUBSTRATE_RPC_URL)

    block_sets = []

    if check_gaps:
        # Check for gaps between already harvested blocks and try to fill them first
        remaining_sets_result = Block.get_missing_block_ids(self.session)

        for block_set in remaining_sets_result:

            # Get start and end block hash
            end_block_hash = substrate.get_block_hash(
                int(block_set['block_from']))
            start_block_hash = substrate.get_block_hash(
                int(block_set['block_to']))

            # Start processing task
            accumulate_block_recursive.delay(start_block_hash, end_block_hash)

            block_sets.append({
                'start_block_hash': start_block_hash,
                'end_block_hash': end_block_hash
            })

        # Start sequencer
        start_sequencer.delay()

    # Continue from current finalised head

    start_block_hash = substrate.get_chain_head()
    end_block_hash = None

    accumulate_block_recursive.delay(start_block_hash, end_block_hash)

    block_sets.append({
        'start_block_hash': start_block_hash,
        'end_block_hash': end_block_hash
    })

    return {'result': 'Harvester job started', 'block_sets': block_sets}
def result_handler(result):
    if result.get("method", "") == "chain_newHead":
        number = result['params']['result']['number']
        substrate_connection = SubstrateInterface(url=rpc_url)
        block_hash = substrate_connection.get_block_hash(number)
        block_events = substrate_connection.get_runtime_events(
            block_hash=block_hash).get('result')
        print(int(number, 16), block_hash)
        for block_event in block_events:
            module_id = block_event['module_id']
            event_id = block_event['event_id']
            if module_id == "Staking" and event_id == "EraPayout":
                print(f"{module_id}.{event_id}")
                publisher.publish(topic_path, b' ')
def get_extrinsic_events(extrinsic_time_point):
    substrate = SubstrateInterface(
        url=settings.NODE_URL,
        address_type=2,
        type_registry_preset="kusama",
    )
    extrinsic_events = []
    block_hash = substrate.get_block_hash(extrinsic_time_point[0])
    block_events = substrate.get_runtime_events(block_hash).get("result")
    for event in block_events:
        if event["extrinsic_idx"] == extrinsic_time_point[1]:
            # Todo, only get required event data
            # Perhaps just event_id and params?
            extrinsic_events.append(event)
    return extrinsic_events
Exemple #7
0
    def on_post(self, req, resp):
        blockHash = None
        substrate = SubstrateInterface(SUBSTRATE_RPC_URL)
        if req.media.get('block_id'):
            blockHash = substrate.get_block_hash(req.media.get('block_id'))
        elif req.media.get('block_hash'):
            blockHash = req.media.get('block_hash')
        else:
            resp.status = falcon.HTTP_BAD_REQUEST
            resp.media = {
                'errors': ['Either blockHash or block_id should be supplied']
            }

        metadata = substrate.get_block_metadata(blockHash)

        resp.media = {'status': 'success', 'data': metadata}
    def solve(self, block_id, end_block_id, substrate_url):
        print('== updatelog  ===substrate_url-{} ===start_block_hash-{} ===end-{}'.format(substrate_url, block_id,
                                                                                          end_block_id))
        substrate = SubstrateInterface(substrate_url)
        try:
            for nr in range(int(block_id), int(end_block_id)):
                block_hash = substrate.get_block_hash(hex(nr))
                json_block = substrate.get_chain_block(block_hash)
                l_block_id = json_block['block']['header'].pop('number')
                l_block_id = int(l_block_id, 16)
                digest_logs = json_block['block']['header'].get('digest', {}).pop('logs', None)
                print('== digest_logs  ===digest_logs-{} '.format(digest_logs))
                print('== l_block_id  ===l_block_id-{} '.format(l_block_id))

                self.log_processor(digest_logs, l_block_id)

        except BlockAlreadyAdded as e:
            print('. Skipped {} '.format(block_hash))
def start_harvester(self, check_gaps=False, shard=None):
    shard = self.request.args[0]
    if shard is None:
        raise HarvesterNotshardParamsError(
            'params shard is missing.. stopping harvester ')

    print("start_harvester")
    substrate_url = SHARDS_TABLE[shard]
    print('== start_harvester substrate_url {} =='.format(substrate_url))
    substrate = SubstrateInterface(substrate_url)

    n = Block.query(self.session).filter_by(bid=1).count()

    if n < 4:
        print('waiting init task completed! count().n: {} '.format(n))

        return {'result': 'waiting init task completed! '}

    block_sets = []

    harvester = PolkascanHarvesterService(self.session,
                                          type_registry=TYPE_REGISTRY)
    harvester.metadata_store = self.metadata_store

    start_block_hash = substrate.get_chain_head()
    end_block_hash = None
    r = 10
    block_nr = substrate.get_block_number(start_block_hash)

    max_block = Block.query(self.session).filter_by(
        shard_num=shard.split(".")[1]).order_by(Block.bid.desc()).first()

    print('start block_nr  {} =='.format(block_nr))
    print('start max_block  {} =='.format(max_block.bid))
    if block_nr - max_block.bid < 10:
        r = block_nr - max_block.bid

    print('current range r: {} =='.format(max_block.bid))

    try:
        for nr in range(1, r + 1):
            block_hash = substrate.get_block_hash(max_block.bid + nr)

            if harvester.add_block(block_hash, substrate_url):
                print('start_harvester+ Added {} '.format(block_hash))
                self.session.commit()

        # Update persistent metadata store in Celery task
        self.metadata_store = harvester.metadata_store

    except BlockAlreadyAdded as e:
        print('. Skipped {} '.format(block_hash))
    except IntegrityError as e:
        print('. Skipped duplicate {}=={} '.format(block_hash, e))
    except Exception as exc:
        print('! ERROR adding {}'.format(block_hash))
        raise HarvesterCouldNotAddBlock(block_hash) from exc

    block_sets.append({
        'start_block_hash': start_block_hash,
        'end_block_hash': end_block_hash
    })

    return {
        'result':
        'Yee data Synchronization job SUCCESS',
        'block_sets':
        block_sets,
        'result':
        'Synch data  from {} to {} blocks check by shardnum of {}'.format(
            max_block.bid + 1, r + max_block.bid + 1, shard)
    }
    def integrity_checks(self):

        # 1. Check finalized head
        substrate = SubstrateInterface(settings.SUBSTRATE_RPC_URL)

        if settings.FINALIZATION_BY_BLOCK_CONFIRMATIONS > 0:
            finalized_block_hash = substrate.get_chain_head()
            finalized_block_number = max(
                substrate.get_block_number(finalized_block_hash) - settings.FINALIZATION_BY_BLOCK_CONFIRMATIONS, 0
            )
        else:
            finalized_block_hash = substrate.get_chain_finalised_head()
            finalized_block_number = substrate.get_block_number(finalized_block_hash)

        # 2. Check integrity head
        integrity_head = Status.get_status(self.db_session, 'INTEGRITY_HEAD')

        if not integrity_head.value:
            # Only continue if block #1 exists
            if Block.query(self.db_session).filter_by(id=1).count() == 0:
                raise BlockIntegrityError('Chain not at genesis')

            integrity_head.value = 0
        else:
            integrity_head.value = int(integrity_head.value)

        start_block_id = max(integrity_head.value - 1, 0)
        end_block_id = finalized_block_number
        chunk_size = 1000
        parent_block = None

        if start_block_id < end_block_id:
            # Continue integrity check

            # print('== Start integrity checks from {} to {} =='.format(start_block_id, end_block_id))

            for block_nr in range(start_block_id, end_block_id, chunk_size):
                # TODO replace limit with filter_by block range
                block_range = Block.query(self.db_session).order_by('id')[block_nr:block_nr + chunk_size]
                for block in block_range:
                    if parent_block:
                        if block.id != parent_block.id + 1:

                            # Save integrity head if block hash of parent matches with hash in node
                            if parent_block.hash == substrate.get_block_hash(integrity_head.value):
                                integrity_head.save(self.db_session)
                                self.db_session.commit()

                            raise BlockIntegrityError('Block #{} is missing.. stopping check '.format(parent_block.id + 1))
                        elif block.parent_hash != parent_block.hash:

                            self.process_reorg_block(parent_block)
                            self.process_reorg_block(block)

                            self.remove_block(block.hash)
                            self.remove_block(parent_block.hash)
                            self.db_session.commit()

                            self.add_block(substrate.get_block_hash(block.id))
                            self.add_block(substrate.get_block_hash(parent_block.id))
                            self.db_session.commit()

                            integrity_head.value = parent_block.id - 1

                            # Save integrity head if block hash of parent matches with hash in node
                            #if parent_block.parent_hash == substrate.get_block_hash(integrity_head.value):
                            integrity_head.save(self.db_session)
                            self.db_session.commit()

                            raise BlockIntegrityError('Block #{} failed integrity checks, Re-adding #{}.. '.format(parent_block.id, block.id))
                        else:
                            integrity_head.value = block.id

                    parent_block = block

                    if block.id == end_block_id:
                        break

            if parent_block:
                if parent_block.hash == substrate.get_block_hash(int(integrity_head.value)):
                    integrity_head.save(self.db_session)
                    self.db_session.commit()

        return {'integrity_head': integrity_head.value}
class PolkascanHarvesterService(BaseService):

    def __init__(self, db_session, type_registry='default', type_registry_file=None):
        self.db_session = db_session

        if type_registry_file:
            custom_type_registry = load_type_registry_file(type_registry_file)
        else:
            custom_type_registry = None

        self.substrate = SubstrateInterface(
            url=settings.SUBSTRATE_RPC_URL,
            type_registry=custom_type_registry,
            type_registry_preset=type_registry
        )
        self.metadata_store = {}

    def process_genesis(self, block):

        # Set block time of parent block
        child_block = Block.query(self.db_session).filter_by(parent_hash=block.hash).first()
        block.set_datetime(child_block.datetime)

        # Retrieve genesis accounts
        if settings.get_versioned_setting('SUBSTRATE_STORAGE_INDICES', block.spec_version_id) == 'Accounts':

            # Get accounts from storage keys
            storage_key_prefix = self.substrate.generate_storage_hash(
                storage_module='System',
                storage_function='Account',
                metadata_version=settings.SUBSTRATE_METADATA_VERSION
            )

            rpc_result = self.substrate.rpc_request(
                'state_getKeys',
                [storage_key_prefix, block.hash]
            ).get('result')
            # Extract accounts from storage key
            genesis_accounts = [storage_key[-64:] for storage_key in rpc_result if len(storage_key) == 162]

            for account_id in genesis_accounts:
                account_audit = AccountAudit(
                    account_id=account_id,
                    block_id=block.id,
                    extrinsic_idx=None,
                    event_idx=None,
                    type_id=settings.ACCOUNT_AUDIT_TYPE_NEW
                )

                account_audit.save(self.db_session)

        elif settings.get_versioned_setting('SUBSTRATE_STORAGE_INDICES', block.spec_version_id) == 'EnumSet':

            genesis_account_page_count = self.substrate.get_runtime_state(
                module="Indices",
                storage_function="NextEnumSet",
                block_hash=block.hash
            ).get('result', 0)

            # Get Accounts on EnumSet
            block.count_accounts_new = 0
            block.count_accounts = 0

            for enum_set_nr in range(0, genesis_account_page_count + 1):

                genesis_accounts = self.substrate.get_runtime_state(
                    module="Indices",
                    storage_function="EnumSet",
                    params=[enum_set_nr],
                    block_hash=block.hash
                ).get('result')

                if genesis_accounts:
                    block.count_accounts_new += len(genesis_accounts)
                    block.count_accounts += len(genesis_accounts)

                    for idx, account_id in enumerate(genesis_accounts):
                        account_audit = AccountAudit(
                            account_id=account_id.replace('0x', ''),
                            block_id=block.id,
                            extrinsic_idx=None,
                            event_idx=None,
                            type_id=settings.ACCOUNT_AUDIT_TYPE_NEW
                        )

                        account_audit.save(self.db_session)

                        account_index_id = enum_set_nr * 64 + idx

                        account_index_audit = AccountIndexAudit(
                            account_index_id=account_index_id,
                            account_id=account_id.replace('0x', ''),
                            block_id=block.id,
                            extrinsic_idx=None,
                            event_idx=None,
                            type_id=settings.ACCOUNT_INDEX_AUDIT_TYPE_NEW
                        )

                        account_index_audit.save(self.db_session)

        block.save(self.db_session)

        # Add hardcoded account like treasury stored in settings
        for account_id in settings.SUBSTRATE_TREASURY_ACCOUNTS:
            account_audit = AccountAudit(
                account_id=account_id,
                block_id=block.id,
                extrinsic_idx=None,
                event_idx=None,
                data={'is_treasury': True},
                type_id=settings.ACCOUNT_AUDIT_TYPE_NEW
            )

            account_audit.save(self.db_session)

        # Check for sudo accounts
        try:
            # Update sudo key
            sudo_key = self.substrate.get_runtime_state(
                module='Sudo',
                storage_function='Key',
                block_hash=block.hash
            ).get('result')

            account_audit = AccountAudit(
                account_id=sudo_key.replace('0x', ''),
                block_id=block.id,
                extrinsic_idx=None,
                event_idx=None,
                data={'is_sudo': True},
                type_id=settings.ACCOUNT_AUDIT_TYPE_NEW
            )

            account_audit.save(self.db_session)
        except ValueError:
            pass

        # Create initial session
        initial_session_event = NewSessionEventProcessor(
            block=block, event=Event(), substrate=self.substrate
        )

        if settings.get_versioned_setting('NEW_SESSION_EVENT_HANDLER', block.spec_version_id):
            initial_session_event.add_session(db_session=self.db_session, session_id=0)
        else:
            initial_session_event.add_session_old(db_session=self.db_session, session_id=0)

    def process_metadata(self, spec_version, block_hash):

        # Check if metadata already in store
        if spec_version not in self.metadata_store:
            print('Metadata: CACHE MISS', spec_version)

            runtime_version_data = self.substrate.get_block_runtime_version(block_hash)

            runtime = Runtime.query(self.db_session).get(spec_version)

            if runtime:
                self.metadata_store[spec_version] = self.substrate.get_block_metadata(block_hash=block_hash)

            else:
                self.db_session.begin(subtransactions=True)
                try:

                    # Store metadata in database
                    runtime = Runtime(
                        id=spec_version,
                        impl_name=runtime_version_data["implName"],
                        impl_version=runtime_version_data["implVersion"],
                        spec_name=runtime_version_data["specName"],
                        spec_version=spec_version,
                        json_metadata=str(self.substrate.metadata_decoder.data),
                        json_metadata_decoded=self.substrate.metadata_decoder.value,
                        apis=runtime_version_data["apis"],
                        authoring_version=runtime_version_data["authoringVersion"],
                        count_call_functions=0,
                        count_events=0,
                        count_modules=len(self.substrate.metadata_decoder.metadata.modules),
                        count_storage_functions=0,
                        count_constants=0,
                        count_errors=0
                    )

                    runtime.save(self.db_session)

                    print('store version to db', self.substrate.metadata_decoder.version)

                    for module in self.substrate.metadata_decoder.metadata.modules:

                        # Check if module exists
                        if RuntimeModule.query(self.db_session).filter_by(
                            spec_version=spec_version,
                            module_id=module.get_identifier()
                        ).count() == 0:
                            module_id = module.get_identifier()
                        else:
                            module_id = '{}_1'.format(module.get_identifier())

                        # Storage backwards compt check
                        if module.storage and isinstance(module.storage, list):
                            storage_functions = module.storage
                        elif module.storage and isinstance(getattr(module.storage, 'value'), dict):
                            storage_functions = module.storage.items
                        else:
                            storage_functions = []

                        runtime_module = RuntimeModule(
                            spec_version=spec_version,
                            module_id=module_id,
                            prefix=module.prefix,
                            name=module.name,
                            count_call_functions=len(module.calls or []),
                            count_storage_functions=len(storage_functions),
                            count_events=len(module.events or []),
                            count_constants=len(module.constants or []),
                            count_errors=len(module.errors or []),
                        )
                        runtime_module.save(self.db_session)

                        # Update totals in runtime
                        runtime.count_call_functions += runtime_module.count_call_functions
                        runtime.count_events += runtime_module.count_events
                        runtime.count_storage_functions += runtime_module.count_storage_functions
                        runtime.count_constants += runtime_module.count_constants
                        runtime.count_errors += runtime_module.count_errors

                        if len(module.calls or []) > 0:
                            for idx, call in enumerate(module.calls):
                                runtime_call = RuntimeCall(
                                    spec_version=spec_version,
                                    module_id=module_id,
                                    call_id=call.get_identifier(),
                                    index=idx,
                                    name=call.name,
                                    lookup=call.lookup,
                                    documentation='\n'.join(call.docs),
                                    count_params=len(call.args)
                                )
                                runtime_call.save(self.db_session)

                                for arg in call.args:
                                    runtime_call_param = RuntimeCallParam(
                                        runtime_call_id=runtime_call.id,
                                        name=arg.name,
                                        type=arg.type
                                    )
                                    runtime_call_param.save(self.db_session)

                        if len(module.events or []) > 0:
                            for event_index, event in enumerate(module.events):
                                runtime_event = RuntimeEvent(
                                    spec_version=spec_version,
                                    module_id=module_id,
                                    event_id=event.name,
                                    index=event_index,
                                    name=event.name,
                                    lookup=event.lookup,
                                    documentation='\n'.join(event.docs),
                                    count_attributes=len(event.args)
                                )
                                runtime_event.save(self.db_session)

                                for arg_index, arg in enumerate(event.args):
                                    runtime_event_attr = RuntimeEventAttribute(
                                        runtime_event_id=runtime_event.id,
                                        index=arg_index,
                                        type=arg
                                    )
                                    runtime_event_attr.save(self.db_session)

                        if len(storage_functions) > 0:
                            for idx, storage in enumerate(storage_functions):

                                # Determine type
                                type_hasher = None
                                type_key1 = None
                                type_key2 = None
                                type_value = None
                                type_is_linked = None
                                type_key2hasher = None

                                if storage.type.get('PlainType'):
                                    type_value = storage.type.get('PlainType')

                                elif storage.type.get('MapType'):
                                    type_hasher = storage.type['MapType'].get('hasher')
                                    type_key1 = storage.type['MapType'].get('key')
                                    type_value = storage.type['MapType'].get('value')
                                    type_is_linked = storage.type['MapType'].get('isLinked', False)

                                elif storage.type.get('DoubleMapType'):
                                    type_hasher = storage.type['DoubleMapType'].get('hasher')
                                    type_key1 = storage.type['DoubleMapType'].get('key1')
                                    type_key2 = storage.type['DoubleMapType'].get('key2')
                                    type_value = storage.type['DoubleMapType'].get('value')
                                    type_key2hasher = storage.type['DoubleMapType'].get('key2Hasher')

                                runtime_storage = RuntimeStorage(
                                    spec_version=spec_version,
                                    module_id=module_id,
                                    index=idx,
                                    name=storage.name,
                                    lookup=None,
                                    default=storage.fallback,
                                    modifier=storage.modifier,
                                    type_hasher=type_hasher,
                                    storage_key=xxh128(module.prefix.encode()) + xxh128(storage.name.encode()),
                                    type_key1=type_key1,
                                    type_key2=type_key2,
                                    type_value=type_value,
                                    type_is_linked=type_is_linked,
                                    type_key2hasher=type_key2hasher,
                                    documentation='\n'.join(storage.docs)
                                )
                                runtime_storage.save(self.db_session)

                        if len(module.constants or []) > 0:
                            for idx, constant in enumerate(module.constants):

                                # Decode value
                                try:
                                    value_obj = ScaleDecoder.get_decoder_class(
                                        constant.type,
                                        ScaleBytes(constant.constant_value)
                                    )
                                    value_obj.decode()
                                    value = value_obj.serialize()
                                except ValueError:
                                    value = constant.constant_value
                                except RemainingScaleBytesNotEmptyException:
                                    value = constant.constant_value
                                except NotImplementedError:
                                    value = constant.constant_value

                                if type(value) is list or type(value) is dict:
                                    value = json.dumps(value)

                                runtime_constant = RuntimeConstant(
                                    spec_version=spec_version,
                                    module_id=module_id,
                                    index=idx,
                                    name=constant.name,
                                    type=constant.type,
                                    value=value,
                                    documentation='\n'.join(constant.docs)
                                )
                                runtime_constant.save(self.db_session)

                        if len(module.errors or []) > 0:
                            for idx, error in enumerate(module.errors):
                                runtime_error = RuntimeErrorMessage(
                                    spec_version=spec_version,
                                    module_id=module_id,
                                    index=idx,
                                    name=error.name,
                                    documentation='\n'.join(error.docs)
                                )
                                runtime_error.save(self.db_session)

                        runtime.save(self.db_session)

                    # Process types
                    for runtime_type_data in list(self.substrate.get_type_registry(block_hash=block_hash).values()):

                        runtime_type = RuntimeType(
                            spec_version=runtime_type_data["spec_version"],
                            type_string=runtime_type_data["type_string"],
                            decoder_class=runtime_type_data["decoder_class"],
                            is_primitive_core=runtime_type_data["is_primitive_core"],
                            is_primitive_runtime=runtime_type_data["is_primitive_runtime"]
                        )
                        runtime_type.save(self.db_session)

                    self.db_session.commit()

                    # Put in local store
                    self.metadata_store[spec_version] = self.substrate.metadata_decoder
                except SQLAlchemyError as e:
                    self.db_session.rollback()

    def add_block(self, block_hash):

        # Check if block is already process
        if Block.query(self.db_session).filter_by(hash=block_hash).count() > 0:
            raise BlockAlreadyAdded(block_hash)

        if settings.SUBSTRATE_MOCK_EXTRINSICS:
            self.substrate.mock_extrinsics = settings.SUBSTRATE_MOCK_EXTRINSICS

        json_block = self.substrate.get_chain_block(block_hash)

        parent_hash = json_block['block']['header'].pop('parentHash')
        block_id = json_block['block']['header'].pop('number')
        extrinsics_root = json_block['block']['header'].pop('extrinsicsRoot')
        state_root = json_block['block']['header'].pop('stateRoot')
        digest_logs = json_block['block']['header'].get('digest', {}).pop('logs', None)

        # Convert block number to numeric
        if not block_id.isnumeric():
            block_id = int(block_id, 16)

        # ==== Get block runtime from Substrate ==================

        self.substrate.init_runtime(block_hash=block_hash)

        self.process_metadata(self.substrate.runtime_version, block_hash)

        # ==== Get parent block runtime ===================

        if block_id > 0:
            json_parent_runtime_version = self.substrate.get_block_runtime_version(parent_hash)

            parent_spec_version = json_parent_runtime_version.get('specVersion', 0)

            self.process_metadata(parent_spec_version, parent_hash)
        else:
            parent_spec_version = self.substrate.runtime_version

        # ==== Set initial block properties =====================

        block = Block(
            id=block_id,
            parent_id=block_id - 1,
            hash=block_hash,
            parent_hash=parent_hash,
            state_root=state_root,
            extrinsics_root=extrinsics_root,
            count_extrinsics=0,
            count_events=0,
            count_accounts_new=0,
            count_accounts_reaped=0,
            count_accounts=0,
            count_events_extrinsic=0,
            count_events_finalization=0,
            count_events_module=0,
            count_events_system=0,
            count_extrinsics_error=0,
            count_extrinsics_signed=0,
            count_extrinsics_signedby_address=0,
            count_extrinsics_signedby_index=0,
            count_extrinsics_success=0,
            count_extrinsics_unsigned=0,
            count_sessions_new=0,
            count_contracts_new=0,
            count_log=0,
            range10000=math.floor(block_id / 10000),
            range100000=math.floor(block_id / 100000),
            range1000000=math.floor(block_id / 1000000),
            spec_version_id=self.substrate.runtime_version,
            logs=digest_logs
        )

        # Set temp helper variables
        block._accounts_new = []
        block._accounts_reaped = []

        # ==== Get block events from Substrate ==================
        extrinsic_success_idx = {}
        events = []

        try:
            events_decoder = self.substrate.get_block_events(block_hash, self.metadata_store[parent_spec_version])

            event_idx = 0

            for event in events_decoder.elements:

                event.value['module_id'] = event.value['module_id'].lower()

                model = Event(
                    block_id=block_id,
                    event_idx=event_idx,
                    phase=event.value['phase'],
                    extrinsic_idx=event.value['extrinsic_idx'],
                    type=event.value['type'],
                    spec_version_id=parent_spec_version,
                    module_id=event.value['module_id'],
                    event_id=event.value['event_id'],
                    system=int(event.value['module_id'] == 'system'),
                    module=int(event.value['module_id'] != 'system'),
                    attributes=event.value['params'],
                    codec_error=False
                )

                # Process event

                if event.value['phase'] == 0:
                    block.count_events_extrinsic += 1
                elif event.value['phase'] == 1:
                    block.count_events_finalization += 1

                if event.value['module_id'] == 'system':

                    block.count_events_system += 1

                    # Store result of extrinsic
                    if event.value['event_id'] == 'ExtrinsicSuccess':
                        extrinsic_success_idx[event.value['extrinsic_idx']] = True
                        block.count_extrinsics_success += 1

                    if event.value['event_id'] == 'ExtrinsicFailed':
                        extrinsic_success_idx[event.value['extrinsic_idx']] = False
                        block.count_extrinsics_error += 1
                else:

                    block.count_events_module += 1

                model.save(self.db_session)

                events.append(model)

                event_idx += 1

            block.count_events = len(events_decoder.elements)

        except SubstrateRequestException:
            block.count_events = 0

        # === Extract extrinsics from block ====

        extrinsics_data = json_block['block'].pop('extrinsics')

        block.count_extrinsics = len(extrinsics_data)

        extrinsic_idx = 0

        extrinsics = []

        for extrinsic in extrinsics_data:

            extrinsics_decoder = ExtrinsicsDecoder(
                data=ScaleBytes(extrinsic),
                metadata=self.metadata_store[parent_spec_version]
            )

            extrinsic_data = extrinsics_decoder.decode()

            # Lookup result of extrinsic
            extrinsic_success = extrinsic_success_idx.get(extrinsic_idx, False)

            model = Extrinsic(
                block_id=block_id,
                extrinsic_idx=extrinsic_idx,
                extrinsic_hash=extrinsics_decoder.extrinsic_hash,
                extrinsic_length=extrinsic_data.get('extrinsic_length'),
                extrinsic_version=extrinsic_data.get('version_info'),
                signed=extrinsics_decoder.contains_transaction,
                unsigned=not extrinsics_decoder.contains_transaction,
                signedby_address=bool(extrinsics_decoder.contains_transaction and extrinsic_data.get('account_id')),
                signedby_index=bool(extrinsics_decoder.contains_transaction and extrinsic_data.get('account_index')),
                address_length=extrinsic_data.get('account_length'),
                address=extrinsic_data.get('account_id'),
                account_index=extrinsic_data.get('account_index'),
                account_idx=extrinsic_data.get('account_idx'),
                signature=extrinsic_data.get('signature'),
                nonce=extrinsic_data.get('nonce'),
                era=extrinsic_data.get('era'),
                call=extrinsic_data.get('call_code'),
                module_id=extrinsic_data.get('call_module'),
                call_id=extrinsic_data.get('call_function'),
                params=extrinsic_data.get('params'),
                spec_version_id=parent_spec_version,
                success=int(extrinsic_success),
                error=int(not extrinsic_success),
                codec_error=False
            )
            model.save(self.db_session)

            extrinsics.append(model)

            extrinsic_idx += 1

            # Process extrinsic
            if extrinsics_decoder.contains_transaction:
                block.count_extrinsics_signed += 1

                if model.signedby_address:
                    block.count_extrinsics_signedby_address += 1
                if model.signedby_index:
                    block.count_extrinsics_signedby_index += 1

                # Add search index for signed extrinsics
                search_index = SearchIndex(
                    index_type_id=settings.SEARCH_INDEX_SIGNED_EXTRINSIC,
                    block_id=block.id,
                    extrinsic_idx=model.extrinsic_idx,
                    account_id=model.address
                )
                search_index.save(self.db_session)

            else:
                block.count_extrinsics_unsigned += 1

            # Process extrinsic processors
            for processor_class in ProcessorRegistry().get_extrinsic_processors(model.module_id, model.call_id):
                extrinsic_processor = processor_class(block, model, substrate=self.substrate)
                extrinsic_processor.accumulation_hook(self.db_session)
                extrinsic_processor.process_search_index(self.db_session)

        # Process event processors
        for event in events:
            extrinsic = None
            if event.extrinsic_idx is not None:
                try:
                    extrinsic = extrinsics[event.extrinsic_idx]
                except IndexError:
                    extrinsic = None

            for processor_class in ProcessorRegistry().get_event_processors(event.module_id, event.event_id):
                event_processor = processor_class(block, event, extrinsic,
                                                  metadata=self.metadata_store.get(block.spec_version_id),
                                                  substrate=self.substrate)
                event_processor.accumulation_hook(self.db_session)
                event_processor.process_search_index(self.db_session)

        # Process block processors
        for processor_class in ProcessorRegistry().get_block_processors():
            block_processor = processor_class(block, substrate=self.substrate, harvester=self)
            block_processor.accumulation_hook(self.db_session)

        # Debug info
        if settings.DEBUG:
            block.debug_info = json_block

        # ==== Save data block ==================================

        block.save(self.db_session)

        return block

    def remove_block(self, block_hash):
        # Retrieve block
        block = Block.query(self.db_session).filter_by(hash=block_hash).first()

        # Revert event processors
        for event in Event.query(self.db_session).filter_by(block_id=block.id):
            for processor_class in ProcessorRegistry().get_event_processors(event.module_id, event.event_id):
                event_processor = processor_class(block, event, None)
                event_processor.accumulation_revert(self.db_session)

        # Revert extrinsic processors
        for extrinsic in Extrinsic.query(self.db_session).filter_by(block_id=block.id):
            for processor_class in ProcessorRegistry().get_extrinsic_processors(extrinsic.module_id, extrinsic.call_id):
                extrinsic_processor = processor_class(block, extrinsic)
                extrinsic_processor.accumulation_revert(self.db_session)

        # Revert block processors
        for processor_class in ProcessorRegistry().get_block_processors():
            block_processor = processor_class(block)
            block_processor.accumulation_revert(self.db_session)

        # Delete events
        for item in Event.query(self.db_session).filter_by(block_id=block.id):
            self.db_session.delete(item)
        # Delete extrinsics
        for item in Extrinsic.query(self.db_session).filter_by(block_id=block.id):
            self.db_session.delete(item)

        # Delete block
        self.db_session.delete(block)

    def sequence_block(self, block, parent_block_data=None, parent_sequenced_block_data=None):

        sequenced_block = BlockTotal(
            id=block.id
        )

        # Process block processors
        for processor_class in ProcessorRegistry().get_block_processors():
            block_processor = processor_class(block, sequenced_block, substrate=self.substrate)
            block_processor.sequencing_hook(
                self.db_session,
                parent_block_data,
                parent_sequenced_block_data
            )

        extrinsics = Extrinsic.query(self.db_session).filter_by(block_id=block.id).order_by('extrinsic_idx')

        for extrinsic in extrinsics:
            # Process extrinsic processors
            for processor_class in ProcessorRegistry().get_extrinsic_processors(extrinsic.module_id, extrinsic.call_id):
                extrinsic_processor = processor_class(block, extrinsic, substrate=self.substrate)
                extrinsic_processor.sequencing_hook(
                    self.db_session,
                    parent_block_data,
                    parent_sequenced_block_data
                )

        events = Event.query(self.db_session).filter_by(block_id=block.id).order_by('event_idx')

        # Process event processors
        for event in events:
            extrinsic = None
            if event.extrinsic_idx is not None:
                try:
                    extrinsic = extrinsics[event.extrinsic_idx]
                except IndexError:
                    extrinsic = None

            for processor_class in ProcessorRegistry().get_event_processors(event.module_id, event.event_id):
                event_processor = processor_class(block, event, extrinsic, substrate=self.substrate)
                event_processor.sequencing_hook(
                    self.db_session,
                    parent_block_data,
                    parent_sequenced_block_data
                )

        sequenced_block.save(self.db_session)

        return sequenced_block

    def integrity_checks(self):

        # 1. Check finalized head
        substrate = SubstrateInterface(settings.SUBSTRATE_RPC_URL)

        if settings.FINALIZATION_BY_BLOCK_CONFIRMATIONS > 0:
            finalized_block_hash = substrate.get_chain_head()
            finalized_block_number = max(
                substrate.get_block_number(finalized_block_hash) - settings.FINALIZATION_BY_BLOCK_CONFIRMATIONS, 0
            )
        else:
            finalized_block_hash = substrate.get_chain_finalised_head()
            finalized_block_number = substrate.get_block_number(finalized_block_hash)

        # 2. Check integrity head
        integrity_head = Status.get_status(self.db_session, 'INTEGRITY_HEAD')

        if not integrity_head.value:
            # Only continue if block #1 exists
            if Block.query(self.db_session).filter_by(id=1).count() == 0:
                raise BlockIntegrityError('Chain not at genesis')

            integrity_head.value = 0
        else:
            integrity_head.value = int(integrity_head.value)

        start_block_id = max(integrity_head.value - 1, 0)
        end_block_id = finalized_block_number
        chunk_size = 1000
        parent_block = None

        if start_block_id < end_block_id:
            # Continue integrity check

            # print('== Start integrity checks from {} to {} =='.format(start_block_id, end_block_id))

            for block_nr in range(start_block_id, end_block_id, chunk_size):
                # TODO replace limit with filter_by block range
                block_range = Block.query(self.db_session).order_by('id')[block_nr:block_nr + chunk_size]
                for block in block_range:
                    if parent_block:
                        if block.id != parent_block.id + 1:

                            # Save integrity head if block hash of parent matches with hash in node
                            if parent_block.hash == substrate.get_block_hash(integrity_head.value):
                                integrity_head.save(self.db_session)
                                self.db_session.commit()

                            raise BlockIntegrityError('Block #{} is missing.. stopping check '.format(parent_block.id + 1))
                        elif block.parent_hash != parent_block.hash:

                            self.process_reorg_block(parent_block)
                            self.process_reorg_block(block)

                            self.remove_block(block.hash)
                            self.remove_block(parent_block.hash)
                            self.db_session.commit()

                            self.add_block(substrate.get_block_hash(block.id))
                            self.add_block(substrate.get_block_hash(parent_block.id))
                            self.db_session.commit()

                            integrity_head.value = parent_block.id - 1

                            # Save integrity head if block hash of parent matches with hash in node
                            #if parent_block.parent_hash == substrate.get_block_hash(integrity_head.value):
                            integrity_head.save(self.db_session)
                            self.db_session.commit()

                            raise BlockIntegrityError('Block #{} failed integrity checks, Re-adding #{}.. '.format(parent_block.id, block.id))
                        else:
                            integrity_head.value = block.id

                    parent_block = block

                    if block.id == end_block_id:
                        break

            if parent_block:
                if parent_block.hash == substrate.get_block_hash(int(integrity_head.value)):
                    integrity_head.save(self.db_session)
                    self.db_session.commit()

        return {'integrity_head': integrity_head.value}

    def start_sequencer(self):
        integrity_status = self.integrity_checks()
        self.db_session.commit()

        block_nr = None

        integrity_head = Status.get_status(self.db_session, 'INTEGRITY_HEAD')

        if not integrity_head.value:
            integrity_head.value = 0

        # 3. Check sequence head
        sequencer_head = self.db_session.query(func.max(BlockTotal.id)).one()[0]

        if sequencer_head is None:
            sequencer_head = -1

        # Start sequencing process

        sequencer_parent_block = BlockTotal.query(self.db_session).filter_by(id=sequencer_head).first()
        parent_block = Block.query(self.db_session).filter_by(id=sequencer_head).first()

        for block_nr in range(sequencer_head + 1, int(integrity_head.value) + 1):

            if block_nr == 0:
                # No block ever sequenced, check if chain is at genesis state
                assert (not sequencer_parent_block)

                block = Block.query(self.db_session).order_by('id').first()

                if not block:
                    self.db_session.commit()
                    return {'error': 'Chain not at genesis'}

                if block.id == 1:
                    # Add genesis block
                    block = self.add_block(block.parent_hash)

                if block.id != 0:
                    self.db_session.commit()
                    return {'error': 'Chain not at genesis'}

                self.process_genesis(block)

                sequencer_parent_block_data = None
                parent_block_data = None
            else:
                block_id = sequencer_parent_block.id + 1

                assert (block_id == block_nr)

                block = Block.query(self.db_session).get(block_nr)

                if not block:
                    self.db_session.commit()
                    return {'result': 'Finished at #{}'.format(sequencer_parent_block.id)}

                sequencer_parent_block_data = sequencer_parent_block.asdict()
                parent_block_data = parent_block.asdict()

            sequenced_block = self.sequence_block(block, parent_block_data, sequencer_parent_block_data)
            self.db_session.commit()

            parent_block = block
            sequencer_parent_block = sequenced_block

        if block_nr:
            return {'result': 'Finished at #{}'.format(block_nr)}
        else:
            return {'result': 'Nothing to sequence'}

    def process_reorg_block(self, block):

        # Check if reorg already exists
        if ReorgBlock.query(self.db_session).filter_by(hash=block.hash).count() == 0:

            model = ReorgBlock(**block.asdict())
            model.save(self.db_session)

            for extrinsic in Extrinsic.query(self.db_session).filter_by(block_id=block.id):
                model = ReorgExtrinsic(block_hash=block.hash, **extrinsic.asdict())
                model.save(self.db_session)

            for event in Event.query(self.db_session).filter_by(block_id=block.id):
                model = ReorgEvent(block_hash=block.hash, **event.asdict())
                model.save(self.db_session)

            for log in Log.query(self.db_session).filter_by(block_id=block.id):
                model = ReorgLog(block_hash=block.hash, **log.asdict())
                model.save(self.db_session)

    def rebuild_search_index(self):

        self.db_session.execute('truncate table {}'.format(SearchIndex.__tablename__))

        for block in Block.query(self.db_session).order_by('id').yield_per(1000):

            extrinsic_lookup = {}
            block._accounts_new = []
            block._accounts_reaped = []

            for extrinsic in Extrinsic.query(self.db_session).filter_by(block_id=block.id).order_by('extrinsic_idx'):
                extrinsic_lookup[extrinsic.extrinsic_idx] = extrinsic

                # Add search index for signed extrinsics
                if extrinsic.address:
                    search_index = SearchIndex(
                        index_type_id=settings.SEARCH_INDEX_SIGNED_EXTRINSIC,
                        block_id=block.id,
                        extrinsic_idx=extrinsic.extrinsic_idx,
                        account_id=extrinsic.address
                    )
                    search_index.save(self.db_session)

                # Process extrinsic processors
                for processor_class in ProcessorRegistry().get_extrinsic_processors(extrinsic.module_id, extrinsic.call_id):
                    extrinsic_processor = processor_class(block=block, extrinsic=extrinsic, substrate=self.substrate)
                    extrinsic_processor.process_search_index(self.db_session)

            for event in Event.query(self.db_session).filter_by(block_id=block.id).order_by('event_idx'):
                extrinsic = None
                if event.extrinsic_idx is not None:
                    try:
                        extrinsic = extrinsic_lookup[event.extrinsic_idx]
                    except (IndexError, KeyError):
                        extrinsic = None

                for processor_class in ProcessorRegistry().get_event_processors(event.module_id, event.event_id):
                    event_processor = processor_class(block, event, extrinsic,
                                                      metadata=self.metadata_store.get(block.spec_version_id),
                                                      substrate=self.substrate)
                    event_processor.process_search_index(self.db_session)

            self.db_session.commit()

    def create_full_balance_snaphot(self, block_id):

        block_hash = self.substrate.get_block_hash(block_id)

        # Determine if keys have Blake2_128Concat format so AccountId is stored in storage key
        storage_method = self.substrate.get_metadata_storage_function(
            module_name="System",
            storage_name="Account",
            block_hash=block_hash
        )

        if storage_method:
            if storage_method.get("type_hasher_key1") == "Blake2_128Concat":

                # get balances storage prefix
                storage_key_prefix = self.substrate.generate_storage_hash(
                    storage_module='System',
                    storage_function='Account',
                    metadata_version=settings.SUBSTRATE_METADATA_VERSION
                )

                rpc_result = self.substrate.rpc_request(
                    'state_getKeys',
                    [storage_key_prefix, block_hash]
                ).get('result')
                # Extract accounts from storage key
                accounts = [storage_key[-64:] for storage_key in rpc_result if len(storage_key) == 162]
            else:
                # Retrieve accounts from database for legacy blocks
                accounts = [account[0] for account in self.db_session.query(distinct(Account.id))]

            for account_id in accounts:

                self.create_balance_snapshot(block_id=block_id, account_id=account_id, block_hash=block_hash)

    def create_balance_snapshot(self, block_id, account_id, block_hash=None):

        if not block_hash:
            block_hash = self.substrate.get_block_hash(block_id)

        # Get balance for account
        try:
            account_info_data = self.substrate.get_runtime_state(
                module='System',
                storage_function='Account',
                params=['0x{}'.format(account_id)],
                block_hash=block_hash
            ).get('result')

            # Make sure no rows inserted before processing this record
            AccountInfoSnapshot.query(self.db_session).filter_by(block_id=block_id, account_id=account_id).delete()

            if account_info_data:
                account_info_obj = AccountInfoSnapshot(
                    block_id=block_id,
                    account_id=account_id,
                    account_info=account_info_data,
                    balance_free=account_info_data["data"]["free"],
                    balance_reserved=account_info_data["data"]["reserved"],
                    balance_total=account_info_data["data"]["free"] + account_info_data["data"]["reserved"],
                    nonce=account_info_data["nonce"]
                )
            else:
                account_info_obj = AccountInfoSnapshot(
                    block_id=block_id,
                    account_id=account_id,
                    account_info=None,
                    balance_free=None,
                    balance_reserved=None,
                    balance_total=None,
                    nonce=None
                )

            account_info_obj.save(self.db_session)
        except ValueError:
            pass

    def update_account_balances(self):
        # set balances according to most recent snapshot
        account_info = self.db_session.execute("""
                        select
                           a.account_id, 
                           a.balance_total,
                           a.balance_free,
                           a.balance_reserved,
                           a.nonce
                    from
                         data_account_info_snapshot as a
                    inner join (
                        select 
                            account_id, max(block_id) as max_block_id 
                        from data_account_info_snapshot 
                        group by account_id
                    ) as b
                    on a.account_id = b.account_id and a.block_id = b.max_block_id
                    """)

        for account_id, balance_total, balance_free, balance_reserved, nonce in account_info:
            Account.query(self.db_session).filter_by(id=account_id).update(
                {
                    Account.balance_total: balance_total,
                    Account.balance_free: balance_free,
                    Account.balance_reserved: balance_reserved,
                    Account.nonce: nonce,
                }, synchronize_session='fetch'
            )
def accumulate_block_recursive(self, block_hash, end_block_hash=None):

    harvester = PolkascanHarvesterService(self.session,
                                          type_registry=TYPE_REGISTRY)
    harvester.metadata_store = self.metadata_store

    # If metadata store isn't initialized yet, perform some tests
    if not harvester.metadata_store:
        print('Init: create entrypoints')
        # Check if blocks exists
        max_block_id = self.session.query(func.max(Block.id)).one()[0]

        if not max_block_id:
            # Speed up accumulating by creating several entry points
            substrate = SubstrateInterface(SUBSTRATE_RPC_URL)
            block_nr = substrate.get_block_number(block_hash)
            if block_nr > 100:
                for entry_point in range(0, block_nr, block_nr // 4)[1:-1]:
                    entry_point_hash = substrate.get_block_hash(entry_point)
                    accumulate_block_recursive.delay(entry_point_hash)

    block = None
    max_sequenced_block_id = False

    add_count = 0

    try:

        for nr in range(0, 10):
            if not block or block.id > 0:
                # Process block
                block = harvester.add_block(block_hash)

                print('+ Added {} '.format(block_hash))

                add_count += 1

                self.session.commit()

                # Break loop if targeted end block hash is reached
                if block_hash == end_block_hash or block.id == 0:
                    break

                # Continue with parent block hash
                block_hash = block.parent_hash

        # Update persistent metadata store in Celery task
        self.metadata_store = harvester.metadata_store

        if block_hash != end_block_hash and block and block.id > 0:
            accumulate_block_recursive.delay(block.parent_hash, end_block_hash)

    except BlockAlreadyAdded as e:
        print('. Skipped {} '.format(block_hash))
        start_sequencer.delay()
    except IntegrityError as e:
        print('. Skipped duplicate {} '.format(block_hash))
    except Exception as exc:
        print('! ERROR adding {}'.format(block_hash))
        raise HarvesterCouldNotAddBlock(block_hash) from exc

    return {
        'result': '{} blocks added'.format(add_count),
        'lastAddedBlockHash': block_hash,
        'sequencerStartedFrom': max_sequenced_block_id
    }
Exemple #13
0
import json
from scalecodec.type_registry import load_type_registry_file
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(
    url='wss://ws.sora2.soramitsu.co.jp',
    type_registry_preset='default',
    type_registry=load_type_registry_file('custom_types.json'),
)

block_hash = substrate.get_block_hash(block_id=184001)
print(block_hash)

# Retrieve extrinsics in block
result = substrate.get_runtime_block(block_hash=block_hash,
                                     ignore_decoding_errors=True)
events = substrate.get_events(block_hash)

groupedEvents = {}
for event in events:
    event = str(event)
    eventdict = eval(event)
    idx = eventdict['extrinsic_idx']

    if idx in groupedEvents.keys():
        groupedEvents[idx].append(eventdict)
    else:
        groupedEvents[idx] = [eventdict]

# print(len(result['block']['extrinsics']))
# print(len(groupedEvents))
Exemple #14
0
    def on_post(self, req, resp):
        blockHash = None
        if req.media.get('block_id'):
            substrate = SubstrateInterface(url=SUBSTRATE_RPC_URL,
                                           address_type=SUBSTRATE_ADDRESS_TYPE,
                                           type_registry_preset=TYPE_REGISTRY)
            blockHash = substrate.get_block_hash(req.media.get('block_id'))
        elif req.media.get('block_hash'):
            blockHash = req.media.get('block_hash')
        else:
            resp.status = falcon.HTTP_BAD_REQUEST
            resp.media = {
                'errors': ['Either blockHash or block_id should be supplied']
            }

        if blockHash:
            resp.status = falcon.HTTP_200
            block = Block.query(
                self.session).filter(Block.hash == blockHash).first()
            blockTotal = BlockTotal.query(
                self.session).filter(BlockTotal.id == block.id).first()
            author = ss58_encode(
                blockTotal.author.replace('0x', '')
            ) if blockTotal is not None and blockTotal.author is not None else None

            if block:
                blockInfo = {}
                blockInfo["timestamp"] = block.datetime.strftime(
                    "%Y-%m-%d %H:%M:%S")
                blockInfo["block_hash"] = block.hash
                blockInfo["block_id"] = block.id
                blockInfo["parent_id"] = block.id - 1 if block.id > 0 else 0
                blockInfo["child_id"] = block.id + 1
                blockInfo["parent_hash"] = block.parent_hash
                blockInfo["state_root"] = block.state_root
                blockInfo["extrinsic_root"] = block.extrinsics_root
                blockInfo["validator"] = author
                blockInfo["count_extrinsic"] = block.count_extrinsics
                blockInfo["count_event"] = block.count_events
                blockInfo["count_log"] = block.count_log
                # blockInfo["age"] = time.mktime(block.datetime.timetuple())
                blockInfo["age"] = block.datetime.strftime("%Y-%m-%d %H:%M:%S")

                # 获取和区块相关的交易信息
                extrinsics = Extrinsic.query(
                    self.session).filter(Extrinsic.block_id == block.id).all()
                extrinsicsObj = [
                    {
                        "extrinsic_id":
                        '{}-{}'.format(block.id, extrinsic.extrinsic_idx),
                        "hash":
                        extrinsic.extrinsic_hash
                        if extrinsic.extrinsic_hash else None,
                        # "age": time.mktime(block.datetime.timetuple()),
                        "age":
                        block.datetime.strftime("%Y-%m-%d %H:%M:%S"),
                        "result":
                        extrinsic.success,
                        # "address": extrinsic.address if extrinsic.address else None,
                        # "module": extrinsic.module_id,
                        # "fee": None,
                        # "nonce": extrinsic.nonce if extrinsic.nonce else None,
                        # "call": extrinsic.call_id,
                        "operation":
                        '{}({})'.format(extrinsic.module_id,
                                        extrinsic.call_id),
                        "params":
                        extrinsic.params
                        # "signature": extrinsic.signature if extrinsic.signature else None
                    } for extrinsic in extrinsics
                ]

                # 获取和区块相关的日志信息
                logs = Log.query(
                    self.session).filter(Log.block_id == block.id).all()
                logsObj = [{
                    "log_id": '{}-{}'.format(block.id, log.log_idx),
                    "block_id": block.id,
                    "type": log.type,
                    "data": log.data['value']
                } for log in logs]

                # 获取和区块相关的事件信息
                events = Event.query(
                    self.session).filter(Event.block_id == block.id).all()
                eventObj = [{
                    "id":
                    '{}-{}'.format(block.id, event.event_idx),
                    "block_id":
                    block.id,
                    "block_hash":
                    block.hash,
                    "module_id":
                    event.module_id,
                    "event_id":
                    event.event_id,
                    "attributes":
                    event.attributes,
                    "operation":
                    '{}({})'.format(event.module_id, event.event_id),
                    "desc":
                    self.getEventDesc(event.module_id, event.event_id),
                    "hash":
                    self.getEventHash(block.id, event.extrinsic_idx)
                } for event in events]

                resp.media = {
                    'status': 'success',
                    'data': {
                        "block_info": blockInfo,
                        "extrinsics": extrinsicsObj,
                        "logs": logsObj,
                        "events": eventObj
                    }
                }
        else:
            resp.status = falcon.HTTP_404
            resp.media = {'result': 'Block not found'}
import json
from scalecodec.type_registry import load_type_registry_file
from substrateinterface import SubstrateInterface

substrate = SubstrateInterface(
    url='wss://mof3.sora.org/',
    type_registry_preset='default',
    type_registry=load_type_registry_file(
        '../harvester/app/type_registry/custom_types.json'),
)

block_hash = substrate.get_block_hash(block_id=3332543)

extrinsics = substrate.get_block(block_hash=block_hash)['extrinsics']

print('Extrinsincs:', json.dumps([e.value for e in extrinsics], indent=4))

events = substrate.get_events(block_hash)

print("Events:", json.dumps([e.value for e in events], indent=4))
def dealWithForks(self, shard_num, bid=None, substrate_url=None):
    shard_num = str(shard_num)
    if len(shard_num) == 2:
        shard_num = int(shard_num[1:2])
        print(shard_num)
    else:
        shard_num = int(shard_num)
    print(
        '== dealWithForks  substrate_url* {} *shardnum=*{} *==start_block_num=*{}*'
        .format(substrate_url, shard_num, bid))
    substrate = SubstrateInterface(substrate_url)
    # self.session.execute('delete  from data_block where shard_num = %(shard_num)s ',shard_num=shard_num)
    harvester = PolkascanHarvesterService(self.session,
                                          type_registry=TYPE_REGISTRY)
    harvester.metadata_store = self.metadata_store
    try:
        nr = 0
        min_bid = find(bid, shard_num, substrate)
        # min_bid = 5
        # bid = 7
        if (bid - min_bid) <= 1:
            return {
                'result':
                'dealWithForks from {} to {} blocks check by shardnum of {}'.
                format(min_bid, bid, shard_num),
                'status':
                '(bid - min_bid) <= 1,do nothing!'
            }
        self.session.query(Block).filter(Block.shard_num == shard_num,
                                         Block.bid > min_bid,
                                         Block.bid < bid).delete()
        self.session.query(Extrinsic).filter(
            Extrinsic.shard_num == shard_num, Extrinsic.block_id > min_bid,
            Extrinsic.block_id < bid).delete()
        self.session.query(Log).filter(Log.shard_num == shard_num,
                                       Log.block_id > min_bid,
                                       Log.block_id < bid).delete()
        self.session.query(Event).filter(Event.shard_num == shard_num,
                                         Event.block_id > min_bid,
                                         Event.block_id < bid).delete()

        for nr in range(min_bid + 1, bid):
            blocka = harvester.add_block(substrate.get_block_hash(nr),
                                         substrate_url)
            if blocka:
                print(
                    '== Added sucess dealWithForks  substrate_url* {} *shardnum=*{} *==start_block_num=*{}*'
                    .format(substrate_url, shard_num, nr))
                self.session.commit()

    except BlockAlreadyAdded as e:
        print('. dealWithForks Skipped {} '.format(nr))
    except IntegrityError as e:
        print('.dealWithForks Skipped duplicate {}=={} '.format(nr, e))
    except Exception as exc:
        print('!dealWithForks ERROR adding {}'.format(nr))
        raise self.retry(exc=exc, countdown=60, max_retries=5)

    return {
        'result':
        'dealWithForks from {} to {} blocks check by shardnum of {}'.format(
            min_bid, bid, shard_num),
        'dealWithForks_status':
        'true'
    }
Exemple #17
0
class JSONRPCResource(BaseResource):

    def __init__(self, cache_region):

        self.cache_region = cache_region

        # Check for custom types in Redis

        self.substrate = None

        custom_type_registry = self.cache_region.get('CUSTOM_TYPE_REGISTRY')
        self.init_type_registry(custom_type_registry)

        self.block_hash = None
        self.metadata_decoder = None
        self.runtime_version = None

        self.metadata_cache = {}

        self.methods = [
            'rpc_methods',
            'runtime_decodeScale',
            'runtime_encodeScale',
            'runtime_getMetadata',
            'runtime_getMetadataModules',
            'runtime_getMetadataCallFunctions',
            'runtime_getMetadataCallFunction',
            'runtime_getMetadataEvents',
            'runtime_getMetadataEvent',
            'runtime_getMetadataConstants',
            'runtime_getMetadataConstant',
            'runtime_getMetadataStorageFunctions',
            'runtime_getMetadataStorageFunction',
            'runtime_getMetadataErrors',
            'runtime_getMetadataError',
            'runtime_getState',
            'runtime_getTypeRegistry',
            'runtime_getType',
            'runtime_getCustomTypes',
            'runtime_addCustomType',
            'runtime_setCustomTypes',
            'runtime_removeCustomType',
            'runtime_resetCustomTypes',
            'runtime_getBlock',
            'runtime_createSignaturePayload',
            'runtime_createExternalSignerPayload',
            'runtime_createExtrinsic',
            'runtime_submitExtrinsic',
            'runtime_getPaymentInfo',
            'keypair_create',
            'keypair_inspect',
            'keypair_sign',
            'keypair_verify'
        ]

    def get_request_param(self, params):
        try:
            return params.pop(0)
        except IndexError:
            raise ValueError("Not enough parameters provided")

    def init_type_registry(self, custom_type_registry=None):

        if settings.TYPE_REGISTRY_FILE:
            type_registry = load_type_registry_file(settings.TYPE_REGISTRY_FILE)
        else:
            type_registry = {}

        if custom_type_registry:
            type_registry.update(custom_type_registry)

        self.substrate = SubstrateInterface(
            url=settings.SUBSTRATE_RPC_URL,
            ss58_format=settings.SUBSTRATE_ADDRESS_TYPE,
            type_registry_preset=settings.TYPE_REGISTRY,
            type_registry=custom_type_registry
        )

        if settings.DEBUG:
            print('Custom types at init: ', custom_type_registry)
            self.substrate.debug = True

    def init_request(self, params=None):

        if params:
            self.block_hash = self.get_request_param(params)
            if type(self.block_hash) is int:
                self.block_hash = self.substrate.get_block_hash(self.block_hash)

    def on_post(self, req, resp):
        self.block_hash = None
        self.metadata_decoder = None
        self.runtime_version = None

        self.substrate.request_id = req.media.get('id')

        method = req.media.get('method')
        params = req.media.get('params', [])

        # Check request requirements
        if not req.media.get('jsonrpc'):
            resp.media = {
                "error": {
                    "code": -32600,
                    "message": "Unsupported JSON-RPC protocol version"
                },
                "id": req.media.get('id')
            }
        elif not method:
            resp.media = {
                "error": {
                    "code": -32601,
                    "message": "Method not found"
                },
                "id": req.media.get('id')
            }
        elif method not in self.methods:
            # Default pass through request to Substrate RPC
            resp.media = self.substrate.rpc_request(method, params)
        else:
            resp.status = falcon.HTTP_200
            try:

                # Process methods
                if method == 'runtime_getBlock':
                    self.init_request(params)

                    block = self.substrate.get_block(block_hash=self.block_hash)

                    if block:

                        block['extrinsics'] = [extrinsic.value for extrinsic in block['extrinsics']]
                        block['header']["digest"]["logs"] = [log.value for log in block['header']["digest"]["logs"]]

                    response = {
                        "jsonrpc": "2.0",
                        "result": block,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getState':
                    # Init params
                    storage_params = None

                    # Process params
                    module = self.get_request_param(params)
                    storage_function = self.get_request_param(params)
                    if params:
                        storage_params = self.get_request_param(params)

                    self.init_request(params)

                    # Get response
                    obj = self.substrate.query(
                        module=module,
                        storage_function=storage_function,
                        params=storage_params,
                        block_hash=self.block_hash
                    )
                    response = {'result': obj.value if obj else None}

                elif method == 'runtime_getMetadata':
                    # Process params
                    self.init_request(params)

                    # Get response
                    response = self.substrate.get_runtime_metadata(block_hash=self.block_hash)

                elif method in ['runtime_createSignaturePayload', 'runtime_createExternalSignerPayload']:
                    account = self.get_request_param(params)
                    call_module = self.get_request_param(params)
                    call_function = self.get_request_param(params)
                    call_params = self.get_request_param(params)
                    tip = self.get_request_param(params) or 0
                    era = self.get_request_param(params)

                    self.init_request(params)

                    try:
                        # Create call
                        call = self.substrate.compose_call(
                            call_module=call_module,
                            call_function=call_function,
                            call_params=call_params,
                            block_hash=self.block_hash
                        )

                        nonce = self.substrate.get_account_nonce(account) or 0

                        if isinstance(era, dict) and 'current' not in era and 'phase' not in era:
                            # Retrieve current block id
                            era['current'] = self.substrate.get_block_number(self.substrate.get_chain_head())

                        if method == 'runtime_createExternalSignerPayload':
                            include_call_length = True
                        else:
                            include_call_length = False

                        # Generate signature payload
                        signature_payload = self.substrate.generate_signature_payload(
                            call=call,
                            nonce=nonce,
                            tip=tip,
                            era=era,
                            include_call_length=include_call_length
                        )

                        response = {
                            "jsonrpc": "2.0",
                            "result": {
                                'signature_payload': str(signature_payload),
                                'nonce': nonce,
                                'era': era
                            },
                            "id": req.media.get('id')
                        }
                    except ValueError as e:
                        response = {
                            "jsonrpc": "2.0",
                            "error": {
                                "code": -999,
                                "message": str(e)
                            },
                            "id": req.media.get('id')
                        }
                elif method in ['runtime_submitExtrinsic', 'runtime_createExtrinsic']:
                    account = self.get_request_param(params)
                    call_module = self.get_request_param(params)
                    call_function = self.get_request_param(params)
                    call_params = self.get_request_param(params)
                    tip = self.get_request_param(params) or 0
                    era = self.get_request_param(params)
                    crypto_type = int(self.get_request_param(params) or 1)
                    signature = self.get_request_param(params)

                    self.init_request(params)

                    try:
                        # Create call
                        call = self.substrate.compose_call(
                            call_module=call_module,
                            call_function=call_function,
                            call_params=call_params,
                            block_hash=self.block_hash
                        )

                        nonce = self.substrate.get_account_nonce(account) or 0

                        # Create keypair with only public given given in request
                        keypair = Keypair(ss58_address=account, crypto_type=crypto_type)

                        if isinstance(era, dict) and 'current' in era:
                            era['current'] = int(era['current'])

                        # Create extrinsic
                        extrinsic = self.substrate.create_signed_extrinsic(
                            call=call,
                            keypair=keypair,
                            nonce=nonce,
                            signature=signature,
                            tip=tip,
                            era=era
                        )

                        if method == 'runtime_createExtrinsic':
                            result = str(extrinsic.data)
                        else:
                            # Submit extrinsic to the node
                            extrinsic_result = self.substrate.submit_extrinsic(
                                extrinsic=extrinsic
                            )
                            result = {
                                "extrinsic_hash": extrinsic_result.extrinsic_hash,
                                "block_hash": extrinsic_result.block_hash,
                                "finalized": extrinsic_result.finalized,
                            }

                        response = {
                            "jsonrpc": "2.0",
                            "result": result,
                            "id": req.media.get('id')
                        }
                    except ValueError as e:
                        response = {
                            "jsonrpc": "2.0",
                            "error": {
                                "code": -999,
                                "message": str(e)
                            },
                            "id": req.media.get('id')
                        }
                    except SubstrateRequestException as e:
                        response = {
                            "jsonrpc": "2.0",
                            "error": e.args[0],
                            "id": req.media.get('id')
                        }
                elif method == 'runtime_getPaymentInfo':

                    account = self.get_request_param(params)
                    call_module = self.get_request_param(params)
                    call_function = self.get_request_param(params)
                    call_params = self.get_request_param(params)

                    # Create call
                    call = self.substrate.compose_call(
                        call_module=call_module,
                        call_function=call_function,
                        call_params=call_params
                    )

                    # Create keypair with only public given given in request
                    keypair = Keypair(ss58_address=account)

                    response = {
                        "jsonrpc": "2.0",
                        "result": self.substrate.get_payment_info(call=call, keypair=keypair),
                        "id": req.media.get('id')
                    }

                elif method == 'runtime_getMetadataModules':

                    self.init_request(params)

                    response = {
                        "jsonrpc": "2.0",
                        "result": self.substrate.get_metadata_modules(block_hash=self.block_hash),
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataCallFunctions':

                    self.init_request(params)

                    call_list = self.substrate.get_metadata_call_functions(block_hash=self.block_hash)

                    response = {
                        "jsonrpc": "2.0",
                        "result": call_list,
                        "id": req.media.get('id')
                    }

                elif method == 'runtime_getMetadataCallFunction':

                    param_call_module = self.get_request_param(params)
                    param_call_module_function = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.get_metadata_call_function(
                        module_name=param_call_module,
                        call_function_name=param_call_module_function,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result.value,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataEvents':

                    self.init_request(params)

                    event_list = self.substrate.get_metadata_events(block_hash=self.block_hash)

                    response = {
                        "jsonrpc": "2.0",
                        "result": event_list,
                        "id": req.media.get('id')
                    }

                elif method == 'runtime_getMetadataEvent':

                    param_call_module = self.get_request_param(params)
                    param_call_module_event = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.get_metadata_event(
                        module_name=param_call_module,
                        event_name=param_call_module_event,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataConstants':

                    self.init_request(params)

                    constant_list = self.substrate.get_metadata_constants(block_hash=self.block_hash)

                    response = {
                        "jsonrpc": "2.0",
                        "result": constant_list,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataConstant':

                    module_name = self.get_request_param(params)
                    constant_name = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.get_metadata_constant(
                        module_name=module_name,
                        constant_name=constant_name,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataStorageFunctions':
                    self.init_request(params)

                    storage_list = self.substrate.get_metadata_storage_functions(block_hash=self.block_hash)

                    response = {
                        "jsonrpc": "2.0",
                        "result": storage_list,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataStorageFunction':

                    module_name = self.get_request_param(params)
                    storage_name = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.get_metadata_storage_function(
                        module_name=module_name,
                        storage_name=storage_name,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result.value,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataErrors':

                    self.init_request(params)

                    error_list = self.substrate.get_metadata_errors(block_hash=self.block_hash)

                    response = {
                        "jsonrpc": "2.0",
                        "result": error_list,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getMetadataError':

                    module_name = self.get_request_param(params)
                    error_name = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.get_metadata_error(
                        module_name=module_name,
                        error_name=error_name,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getTypeRegistry':

                    self.init_request(params)

                    result = self.substrate.get_type_registry(block_hash=self.block_hash)

                    if result:
                        result = list(result.values())

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getType':

                    type_string = self.get_request_param(params)
                    self.init_request(params)

                    response = {
                        "jsonrpc": "2.0",
                        "result": self.substrate.get_type_definition(type_string, block_hash=self.block_hash),
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_addCustomType':

                    type_string = self.get_request_param(params)
                    type_definition = self.get_request_param(params)

                    # Retrieve current custom type registry
                    custom_type_registry = self.cache_region.get('CUSTOM_TYPE_REGISTRY')

                    if not custom_type_registry:
                        custom_type_registry = {
                            'types': {

                            }
                        }

                    custom_type_registry['types'][type_string] = type_definition

                    # TODO Try to decode given type definition

                    # Store updated custom type registry
                    self.cache_region.set('CUSTOM_TYPE_REGISTRY', custom_type_registry)

                    if settings.DEBUG:
                        print('Custom types updated to: ', custom_type_registry)

                    # Update runtime configuration
                    RuntimeConfiguration().update_type_registry(custom_type_registry)

                    response = {
                        "jsonrpc": "2.0",
                        "result": "Type registry updated",
                        "id": req.media.get('id')
                    }

                elif method == 'runtime_setCustomTypes':

                    custom_types = self.get_request_param(params)

                    if type(custom_types) is not dict:
                        raise ValueError('custom types must be in format: {"type_string": "type_definition"}')

                    custom_type_registry = {
                        'types': custom_types
                    }

                    # Store updated custom type registry
                    self.cache_region.set('CUSTOM_TYPE_REGISTRY', custom_type_registry)

                    # Reset runtime configuration
                    RuntimeConfiguration().clear_type_registry()
                    self.init_type_registry(custom_type_registry)

                    if settings.DEBUG:
                        print('Custom types updated to: ', custom_type_registry)

                    response = {
                        "jsonrpc": "2.0",
                        "result": "Type registry updated",
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_resetCustomTypes':

                    custom_type_registry = None

                    # Store updated custom type registry
                    self.cache_region.set('CUSTOM_TYPE_REGISTRY', custom_type_registry)

                    # Reset runtime configuration
                    RuntimeConfiguration().clear_type_registry()
                    self.init_type_registry()

                    if settings.DEBUG:
                        print('Custom types cleared')

                    response = {
                        "jsonrpc": "2.0",
                        "result": "Custom types cleared",
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_removeCustomType':

                    type_string = self.get_request_param(params)

                    # Retrieve current custom type registry
                    custom_type_registry = self.cache_region.get('CUSTOM_TYPE_REGISTRY')

                    if custom_type_registry and type_string in custom_type_registry.get('types', {}):
                        del custom_type_registry['types'][type_string]

                        # Store updated custom type registry
                        self.cache_region.set('CUSTOM_TYPE_REGISTRY', custom_type_registry)

                        # Reset runtime configuration
                        RuntimeConfiguration().clear_type_registry()
                        self.init_type_registry(custom_type_registry)

                        result = '"{}" removed from custom type registry'.format(type_string)

                    else:
                        result = '"{}" not found in custom type registry'.format(type_string)

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_getCustomTypes':

                    custom_type_registry = self.cache_region.get('CUSTOM_TYPE_REGISTRY')

                    if custom_type_registry:
                        result = custom_type_registry.get('types')
                    else:
                        result = {}

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_decodeScale':

                    type_string = self.get_request_param(params)
                    scale_hex_bytes = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.decode_scale(
                        type_string=type_string,
                        scale_bytes=scale_hex_bytes,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'runtime_encodeScale':

                    type_string = self.get_request_param(params)
                    value = self.get_request_param(params)

                    self.init_request(params)

                    result = self.substrate.encode_scale(
                        type_string=type_string,
                        value=value,
                        block_hash=self.block_hash
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": result,
                        "id": req.media.get('id')
                    }
                elif method == 'keypair_create':

                    word_count = self.get_request_param(params) or 0
                    crypto_type = int(self.get_request_param(params) or 1)

                    mnemonic = Keypair.generate_mnemonic(word_count)

                    keypair = Keypair.create_from_mnemonic(
                        mnemonic=mnemonic, ss58_format=settings.SUBSTRATE_ADDRESS_TYPE, crypto_type=crypto_type
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": {
                            'ss58_address': keypair.ss58_address,
                            'public_key': keypair.public_key,
                            'private_key': keypair.private_key,
                            'mnemonic': keypair.mnemonic,
                        },
                        "id": req.media.get('id')
                    }
                elif method == 'keypair_inspect':

                    mnemonic = self.get_request_param(params)
                    crypto_type = int(self.get_request_param(params) or 1)

                    keypair = Keypair.create_from_mnemonic(
                        mnemonic=mnemonic, ss58_format=settings.SUBSTRATE_ADDRESS_TYPE, crypto_type=crypto_type
                    )

                    response = {
                        "jsonrpc": "2.0",
                        "result": {
                            'ss58_address': keypair.ss58_address,
                            'public_key': keypair.public_key,
                            'private_key': keypair.private_key,
                            'mnemonic': keypair.mnemonic,
                        },
                        "id": req.media.get('id')
                    }
                elif method == 'keypair_sign':
                    mnemonic = self.get_request_param(params)
                    data = self.get_request_param(params)
                    crypto_type = int(self.get_request_param(params) or 1)

                    keypair = Keypair.create_from_mnemonic(
                        mnemonic=mnemonic, ss58_format=settings.SUBSTRATE_ADDRESS_TYPE, crypto_type=crypto_type
                    )
                    signature = keypair.sign(data)

                    response = {
                        "jsonrpc": "2.0",
                        "result": {'signature': signature},
                        "id": req.media.get('id')
                    }

                elif method == 'keypair_verify':
                    account_address = self.get_request_param(params)
                    data = self.get_request_param(params)
                    signature = self.get_request_param(params)
                    crypto_type = int(self.get_request_param(params) or 1)

                    keypair = Keypair(
                        ss58_address=account_address,
                        ss58_format=settings.SUBSTRATE_ADDRESS_TYPE,
                        crypto_type=crypto_type
                    )
                    result = keypair.verify(data, signature)

                    response = {
                        "jsonrpc": "2.0",
                        "result": {'verified': result},
                        "id": req.media.get('id')
                    }

                elif method == 'rpc_methods':

                    response = self.substrate.rpc_request(method, params)

                    # Add additional implemented method
                    response['result']['methods'] = sorted(response['result']['methods'] + self.methods)

                else:
                    raise NotImplementedError('Method \'{}\' not implemented yet'.format(method))
            except (ValueError, NotImplementedError) as e:
                response = {
                    "error": {
                        "code": -999,
                        "message": str(e)
                    },
                    "id": req.media.get('id')
                }
            except (InvalidScaleTypeValueException, RemainingScaleBytesNotEmptyException) as e:
                response = {
                    "error": {
                        "code": -998,
                        "message": "Decoding error, given SCALE-value or type registry might be invalid "
                    },
                    "id": req.media.get('id')
                }
            resp.media = response
Exemple #18
0
class Subtensor:
    """
    Handles interactions with the subtensor chain.
    """
    custom_type_registry = {
        "runtime_id": 2,
        "types": {
            "NeuronMetadataOf": {
                "type":
                "struct",
                "type_mapping": [["ip", "u128"], ["port", "u16"],
                                 ["ip_type", "u8"], ["uid", "u64"],
                                 ["modality", "u8"], ["hotkey", "AccountId"],
                                 ["coldkey", "AccountId"]]
            }
        }
    }

    def __init__(self,
                 config: 'Munch' = None,
                 network: str = None,
                 chain_endpoint: str = None):
        r""" Initializes a subtensor chain interface.
            Args:
                config (:obj:`Munch`, `optional`): 
                    metagraph.Metagraph.config()
                network (default='akira', type=str)
                    The subtensor network flag. The likely choices are:
                            -- akira (testing network)
                            -- kusanagi (main network)
                    If this option is set it overloads subtensor.chain_endpoint with 
                    an entry point node from that network.
                chain_endpoint (default=None, type=str)
                    The subtensor endpoint flag. If set, overrides the --network flag.
        """
        if config == None:
            config = Subtensor.default_config()
        config.subtensor.network = network if network != None else config.subtensor.network
        config.subtensor.chain_endpoint = chain_endpoint if chain_endpoint != None else config.subtensor.chain_endpoint
        Subtensor.check_config(config)
        self.config = copy.deepcopy(config)

        chain_endpoint = "ws://subtensor.rawatech.com:9944" if not chain_endpoint else "ws://" + chain_endpoint
        # chain_endpoint = "ws://feynman.kusanagi.bittensor.com:9944" if not chain_endpoint else "ws://" + chain_endpoint
        self.substrate = SubstrateInterface(
            ss58_format=42,
            type_registry_preset='substrate-node-template',
            type_registry=self.custom_type_registry,
            url=chain_endpoint)

    @staticmethod
    def default_config() -> Munch:
        # Parses and returns a config Munch for this object.
        parser = argparse.ArgumentParser()
        Subtensor.add_args(parser)
        config = bittensor.config.Config.to_config(parser)
        return config

    @staticmethod
    def add_args(parser: argparse.ArgumentParser):
        try:
            parser.add_argument(
                '--subtensor.network',
                default='kusanagi',
                type=str,
                help='''The subtensor network flag. The likely choices are:
                                        -- akira (testing network)
                                        -- kusanagi (main network)
                                    If this option is set it overloads subtensor.chain_endpoint with 
                                    an entry point node from that network.
                                    ''')
            parser.add_argument(
                '--subtensor.chain_endpoint',
                default=None,
                type=str,
                help=
                '''The subtensor endpoint flag. If set, overrides the --network flag.
                                    ''')
        except:
            pass

    @staticmethod
    def check_config(config: Munch):
        pass

    def endpoint_for_network(self, blacklist: List[str] = []) -> str:
        r""" Returns a chain endpoint based on config.subtensor.network.
            Returns None if there are no available endpoints.
        Raises:
            endpoint (str):
                Websocket endpoint or None if there are none available.
        """

        # Chain endpoint overrides the --network flag.
        if self.config.subtensor.chain_endpoint != None:
            if self.config.subtensor.chain_endpoint in blacklist:
                return None
            else:
                return self.config.subtensor.chain_endpoint

        # Else defaults to networks.
        # TODO(const): this should probably make a DNS lookup.
        if self.config.subtensor.network == "akira":
            akira_available = [
                item for item in bittensor.__akira_entrypoints__
                if item not in blacklist
            ]
            if len(akira_available) == 0:
                return None
            return random.choice(akira_available)

        elif self.config.subtensor.network == "boltzmann":
            boltzmann_available = [
                item for item in bittensor.__boltzmann_entrypoints__
                if item not in blacklist
            ]
            if len(boltzmann_available) == 0:
                return None
            return random.choice(boltzmann_available)

        elif self.config.subtensor.network == "kusanagi":
            kusanagi_available = [
                item for item in bittensor.__kusanagi_entrypoints__
                if item not in blacklist
            ]
            if len(kusanagi_available) == 0:
                return None
            return random.choice(kusanagi_available)

        elif self.config.subtensor.network == "local":
            local_available = [
                item for item in bittensor.__local_entrypoints__
                if item not in blacklist
            ]
            if len(local_available) == 0:
                return None
            return random.choice(local_available)

        else:
            akira_available = [
                item for item in bittensor.__akira_entrypoints__
                if item not in blacklist
            ]
            if len(akira_available) == 0:
                return None
            return random.choice(akira_available)

    # def is_connected(self) -> bool:
    #     r""" Returns true if the connection state as a boolean.
    #     Raises:
    #         success (bool):
    #             True is the websocket is connected to the chain endpoint.
    #     """
    #     loop = asyncio.get_event_loop()
    #     loop.set_debug(enabled=True)
    #     return loop.run_until_complete(self.async_is_connected())

    # async def async_is_connected(self) -> bool:
    #     r""" Returns the connection state as a boolean.
    #     Raises:
    #         success (bool):
    #             True is the websocket is connected to the chain endpoint.
    #     """
    #     return self.substrate.async_is_connected()

    # def check_connection(self) -> bool:
    #     r""" Checks if substrate_old websocket backend is connected, connects if it is not.
    #     """
    #     loop = asyncio.get_event_loop()
    #     loop.set_debug(enabled=True)
    #     return loop.run_until_complete(self.async_check_connection())

    # async def async_check_connection(self) -> bool:
    #     r""" Checks if substrate_old websocket backend is connected, connects if it is not.
    #     """
    #     if not self.async_is_connected():
    #         return self.async_connect()
    #     return True

    # def connect( self, timeout: int = 10, failure = True ) -> bool:
    #     r""" Attempts to connect the substrate_old interface backend.
    #     If the connection fails, attemps another endpoint until a timeout.
    #     Args:
    #         timeout (int):
    #             Time to wait before subscription times out.
    #         failure (bool):
    #             This connection attempt raises an error an a failed attempt.
    #     Returns:
    #         success (bool):
    #             True on success.
    #     """
    #     loop = asyncio.get_event_loop()
    #     loop.set_debug(enabled=True)
    #     return loop.run_until_complete(self.async_connect(timeout, failure))

    #     async def async_connect( self, timeout: int = 10, failure = True ) -> bool:
    #         r""" Attempts to connect the substrate_old interface backend.
    #         If the connection fails, attemps another endpoint until a timeout.
    #         Args:
    #             timeout (int):
    #                 Time to wait before subscription times out.
    #             failure (bool):
    #                 This connection attempt raises an error an a failed attempt.
    #         Returns:
    #             success (bool):
    #                 True on success.
    #         """
    #         start_time = time.time()
    #         attempted_endpoints = []
    #         while True:
    #             def connection_error_message():
    #                 print('''
    # Check that your internet connection is working and the chain endpoints are available: <cyan>{}</cyan>
    # The subtensor.network should likely be one of the following choices:
    #     -- local - (your locally running node)
    #     -- akira - (testnet)
    #     -- kusanagi - (mainnet)
    # Or you may set the endpoint manually using the --subtensor.chain_endpoint flag
    # To run a local node (See: docs/running_a_validator.md) \n
    #                               '''.format( attempted_endpoints) )
    #
    #             # ---- Get next endpoint ----
    #             ws_chain_endpoint = self.endpoint_for_network( blacklist = attempted_endpoints )
    #             if ws_chain_endpoint == None:
    #                 logger.error("No more endpoints available for subtensor.network: <cyan>{}</cyan>, attempted: <cyan>{}</cyan>".format(self.config.subtensor.network, attempted_endpoints))
    #                 connection_error_message()
    #                 if failure:
    #                     logger.critical('Unable to connect to network:<cyan>{}</cyan>.\nMake sure your internet connection is stable and the network is properly set.'.format(self.config.subtensor.network))
    #                 else:
    #                     return False
    #             attempted_endpoints.append(ws_chain_endpoint)
    #
    #             # --- Attempt connection ----
    #             if self.substrate.async_connect( ws_chain_endpoint, timeout = 5 ):
    #                 logger.success("Connected to network:<cyan>{}</cyan> at endpoint:<cyan>{}</cyan>".format(self.config.subtensor.network, ws_chain_endpoint))
    #                 return True
    #
    #             # ---- Timeout ----
    #             elif (time.time() - start_time) > timeout:
    #                 logger.error( "Error while connecting to network:<cyan>{}</cyan> at endpoint: <cyan>{}</cyan>".format(self.config.subtensor.network, ws_chain_endpoint))
    #                 connection_error_message()
    #                 if failure:
    #                     raise RuntimeError('Unable to connect to network:<cyan>{}</cyan>.\nMake sure your internet connection is stable and the network is properly set.'.format(self.config.subtensor.network))
    #                 else:
    #                     return False

    # async def _submit_and_check_extrinsic(
    #         self,
    #         extrinsic,
    #         wait_for_inclusion:bool = False,
    #         wait_for_finalization: bool = False,
    #         timeout: int = bittensor.__blocktime__ * 3
    #     ) -> bool:
    #     r""" Makes an extrinsic call to the chain, returns true if the extrinsic send was a success.
    #     If wait_for_inclusion or wait_for_finalization are true, the call will return true iff the
    #     extrinsic enters or finalizes in a block.
    #     Args:
    #         extrinsic (substrate_old extrinsic):
    #             Extrinsic to send to the chain.
    #         wait_for_inclusion (bool):
    #             If set, waits for the extrinsic to enter a block before returning true,
    #             or returns false if the extrinsic fails to enter the block within the timeout.
    #         wait_for_finalization (bool):
    #             If set, waits for the extrinsic to be finalized on the chain before returning true,
    #             or returns false if the extrinsic fails to be finalized within the timeout.
    #         timeout (int):
    #             Time that this call waits for either finalization of inclusion.
    #     Returns:
    #         success (bool):
    #             flag is true if extrinsic was finalized or uncluded in the block.
    #             If we did not wait for finalization / inclusion, the response is true.
    #     """
    #     # Send extrinsic
    #     try:
    #         response = self.substrate.submit_extrinsic(
    #                                 extrinsic,
    #                                 wait_for_inclusion = wait_for_inclusion,
    #                                 wait_for_finalization = wait_for_finalization,
    #                                 timeout = timeout
    #                         )
    #     except SubstrateRequestException as e:
    #         logger.error('Extrinsic exception with error {}', e)
    #         return False
    #     except Exception as e:
    #         logger.error('Error submitting extrinsic with error {}', e)
    #         return False
    #
    #     # Check timeout.
    #     if response == None:
    #         logger.error('Error in extrinsic: No response within timeout')
    #         return False
    #
    #     # Check result.
    #     if not wait_for_inclusion and not wait_for_finalization:
    #         return True
    #     else:
    #         if 'error' in response:
    #             logger.error('Error in extrinsic: {}', response['error'])
    #         elif 'finalized' in response and response['finalized'] == True:
    #             return True
    #         elif 'inBlock' in response and response['inBlock'] == True:
    #             return True
    #         else:
    #             return False

    def is_subscribed(self, wallet: 'bittensor.wallet.Wallet', ip: str,
                      port: int, modality: int) -> bool:
        r""" Returns true if the bittensor endpoint is already subscribed with the wallet and metadata.
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            ip (str):
                endpoint host port i.e. 192.122.31.4
            port (int):
                endpoint port number i.e. 9221
            modality (int):
                int encoded endpoint modality i.e 0 for TEXT
            coldkeypub (str):
                string encoded coldekey pub.
        """

        uid = self.get_uid_for_pubkey(wallet.hotkey.public_key)
        if uid is None:
            return False

        neuron = self.get_neuron_for_uid(uid)
        if neuron['ip'] == net.ip_to_int(ip) and neuron['port'] == port:
            return True
        else:
            return False

    def subscribe(
        self,
        wallet: 'bittensor.wallet.Wallet',
        ip: str,
        port: int,
        modality: int,
        wait_for_inclusion: bool = False,
        wait_for_finalization=True,
        timeout: int = 3 * bittensor.__blocktime__,
    ) -> bool:
        r""" Subscribes an bittensor endpoint to the substensor chain.
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            ip (str):
                endpoint host port i.e. 192.122.31.4
            port (int):
                endpoint port number i.e. 9221
            modality (int):
                int encoded endpoint modality i.e 0 for TEXT
            wait_for_inclusion (bool):
                if set, waits for the extrinsic to enter a block before returning true, 
                or returns false if the extrinsic fails to enter the block within the timeout.   
            wait_for_finalization (bool):
                if set, waits for the extrinsic to be finalized on the chain before returning true,
                or returns false if the extrinsic fails to be finalized within the timeout.
            timeout (int):
                time that this call waits for either finalization of inclusion.
        Returns:
            success (bool):
                flag is true if extrinsic was finalized or uncluded in the block. 
                If we did not wait for finalization / inclusion, the response is true.
        """

        if self.is_subscribed(wallet, ip, port, modality):
            logger.success(
                "Already subscribed with:\n<cyan>[\n  ip: {},\n  port: {},\n  modality: {},\n  hotkey: {},\n  coldkey: {}\n]</cyan>"
                .format(ip, port, modality, wallet.hotkey.public_key,
                        wallet.coldkeypub))
            return True

        ip_as_int = net.ip_to_int(ip)
        params = {
            'ip': ip_as_int,
            'port': port,
            'ip_type': 4,
            'modality': modality,
            'coldkey': wallet.coldkeypub,
        }
        call = self.substrate.compose_call(call_module='SubtensorModule',
                                           call_function='subscribe',
                                           call_params=params)
        # TODO (const): hotkey should be an argument here not assumed. Either that or the coldkey pub should also be assumed.
        extrinsic = self.substrate.create_signed_extrinsic(
            call=call, keypair=wallet.hotkey)
        result = self.substrate.submit_extrinsic(
            extrinsic, wait_for_inclusion, wait_for_finalization).is_success
        if result:
            logger.success(
                "Successfully subscribed with:\n<cyan>[\n  ip: {},\n  port: {},\n  modality: {},\n  hotkey: {},\n  coldkey: {}\n]</cyan>"
                .format(ip, port, modality, wallet.hotkey.public_key,
                        wallet.coldkeypub))
        else:
            logger.error("Failed to subscribe")
        return result

    def add_stake(
        self,
        wallet: 'bittensor.wallet.Wallet',
        amount: Balance,
        hotkey_id: int,
        wait_for_inclusion: bool = False,
        wait_for_finalization: bool = False,
        timeout: int = 3 * bittensor.__blocktime__,
    ) -> bool:
        r""" Adds the specified amount of stake to passed hotkey uid.
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            amount (bittensor.utils.balance.Balance):
                amount to stake as bittensor balance
            hotkey_id (int):
                uid of hotkey to stake into.
            wait_for_inclusion (bool):
                if set, waits for the extrinsic to enter a block before returning true, 
                or returns false if the extrinsic fails to enter the block within the timeout.   
            wait_for_finalization (bool):
                if set, waits for the extrinsic to be finalized on the chain before returning true,
                or returns false if the extrinsic fails to be finalized within the timeout.
            timeout (int):
                time that this call waits for either finalization of inclusion.
        Returns:
            success (bool):
                flag is true if extrinsic was finalized or uncluded in the block. 
                If we did not wait for finalization / inclusion, the response is true.
        """
        call = self.substrate.compose_call(call_module='SubtensorModule',
                                           call_function='add_stake',
                                           call_params={
                                               'hotkey': hotkey_id,
                                               'ammount_staked': amount.rao
                                           })
        extrinsic = self.substrate.create_signed_extrinsic(
            call=call, keypair=wallet.coldkey)
        return self.substrate.submit_extrinsic(
            extrinsic, wait_for_inclusion, wait_for_finalization).is_success

    def transfer(
        self,
        wallet: 'bittensor.wallet.Wallet',
        dest: str,
        amount: Balance,
        wait_for_inclusion: bool = False,
        wait_for_finalization: bool = False,
        timeout: int = 3 * bittensor.__blocktime__,
    ) -> bool:
        r""" Transfers funds from this wallet to the destination public key address
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            dest (str):
                destination public key address of reciever. 
            amount (bittensor.utils.balance.Balance):
                amount to stake as bittensor balance
            wait_for_inclusion (bool):
                if set, waits for the extrinsic to enter a block before returning true, 
                or returns false if the extrinsic fails to enter the block within the timeout.   
            wait_for_finalization (bool):
                if set, waits for the extrinsic to be finalized on the chain before returning true,
                or returns false if the extrinsic fails to be finalized within the timeout.
            timeout (int):
                time that this call waits for either finalization of inclusion.
        Returns:
            success (bool):
                flag is true if extrinsic was finalized or uncluded in the block. 
                If we did not wait for finalization / inclusion, the response is true.
        """
        call = self.substrate.compose_call(call_module='Balances',
                                           call_function='transfer',
                                           call_params={
                                               'dest': dest,
                                               'value': amount.rao
                                           })
        extrinsic = self.substrate.create_signed_extrinsic(
            call=call, keypair=wallet.coldkey)
        return self.substrate.submit_extrinsic(
            extrinsic, wait_for_inclusion, wait_for_finalization).is_success

    def unstake(
        self,
        wallet: 'bittensor.wallet.Wallet',
        amount: Balance,
        hotkey_id: int,
        wait_for_inclusion: bool = False,
        wait_for_finalization: bool = False,
        timeout: int = 3 * bittensor.__blocktime__,
    ) -> bool:
        r""" Removes stake into the wallet coldkey from the specified hotkey uid.
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            amount (bittensor.utils.balance.Balance):
                amount to stake as bittensor balance
            hotkey_id (int):
                uid of hotkey to unstake from.
            wait_for_inclusion (bool):
                if set, waits for the extrinsic to enter a block before returning true, 
                or returns false if the extrinsic fails to enter the block within the timeout.   
            wait_for_finalization (bool):
                if set, waits for the extrinsic to be finalized on the chain before returning true,
                or returns false if the extrinsic fails to be finalized within the timeout.
            timeout (int):
                time that this call waits for either finalization of inclusion.
        Returns:
            success (bool):
                flag is true if extrinsic was finalized or uncluded in the block. 
                If we did not wait for finalization / inclusion, the response is true.
        """
        call = self.substrate.compose_call(call_module='SubtensorModule',
                                           call_function='remove_stake',
                                           call_params={
                                               'ammount_unstaked': amount.rao,
                                               'hotkey': hotkey_id
                                           })
        extrinsic = self.substrate.create_signed_extrinsic(
            call=call, keypair=wallet.coldkey)
        return self.substrate.submit_extrinsic(
            extrinsic, wait_for_inclusion, wait_for_finalization).is_success

    def set_weights(self,
                    wallet: 'bittensor.wallet.Wallet',
                    destinations,
                    values,
                    wait_for_inclusion: bool = False,
                    wait_for_finalization: bool = False,
                    timeout: int = 3 * bittensor.__blocktime__) -> bool:
        r""" Sets the given weights and values on chain for wallet hotkey account.
        Args:
            wallet (bittensor.wallet.Wallet):
                bittensor wallet object.
            destinations (List[int]):
                uint64 uids of destination neurons.
            values (List[int]):
                u32 max encoded floating point weights.
            wait_for_inclusion (bool):
                if set, waits for the extrinsic to enter a block before returning true, 
                or returns false if the extrinsic fails to enter the block within the timeout.
            wait_for_finalization (bool):
                if set, waits for the extrinsic to be finalized on the chain before returning true,
                or returns false if the extrinsic fails to be finalized within the timeout.
            timeout (int):
                time that this call waits for either finalization of inclusion.
        Returns:
            success (bool):
                flag is true if extrinsic was finalized or uncluded in the block. 
                If we did not wait for finalization / inclusion, the response is true.
        """
        call = self.substrate.compose_call(call_module='SubtensorModule',
                                           call_function='set_weights',
                                           call_params={
                                               'dests': destinations,
                                               'weights': values
                                           })
        extrinsic = self.substrate.create_signed_extrinsic(
            call=call, keypair=wallet.hotkey)
        return self.substrate.submit_extrinsic(
            extrinsic,
            wait_for_inclusion=wait_for_inclusion,
            wait_for_finalization=wait_for_finalization).is_success

    def get_balance(self, address: str) -> Balance:
        r""" Returns the token balance for the passed ss58_address address
        Args:
            address (Substrate address format, default = 42):
                ss58 chain address.
        Return:
            balance (bittensor.utils.balance.Balance):
                account balance
        """
        result = self.substrate.get_runtime_state(module='System',
                                                  storage_function='Account',
                                                  params=[address],
                                                  block_hash=None)
        balance_info = result.get('result')
        if not balance_info:
            return Balance(0)
        balance = balance_info['data']['free']
        return Balance(balance)

    def get_current_block(self) -> int:
        r""" Returns the current block number on the chain.
        Returns:
            block_number (int):
                Current chain blocknumber.
        """
        return self.substrate.get_block_number(None)

    def get_block_hash(self, block_nr):
        return self.substrate.get_block_hash(block_nr)

    def get_active(self) -> List[Tuple[str, int]]:
        r""" Returns a list of (public key, uid) pairs one for each active peer on chain.
        Returns:
            active (List[Tuple[str, int]]):
                List of active peers.
        """
        result = self.substrate.iterate_map(
            module='SubtensorModule',
            storage_function='Active',
        )
        return result

    def get_stake(self, hash=None) -> List[Tuple[int, int]]:
        r""" Returns a list of (uid, stake) pairs one for each active peer on chain.
        Returns:
            stake (List[Tuple[int, int]]):
                List of stake values.
        """
        result = self.substrate.iterate_map(module='SubtensorModule',
                                            storage_function='Stake',
                                            block_hash=hash)
        return result

    def get_last_emit(self, hash=None) -> List[Tuple[int, int]]:
        r""" Returns a list of (uid, last emit) pairs for each active peer on chain.
        Returns:
            last_emit (List[Tuple[int, int]]):
                List of last emit values.
        """
        result = self.substrate.iterate_map(module='SubtensorModule',
                                            storage_function='LastEmit',
                                            block_hash=hash)
        return result

    def get_weight_vals(self, hash=None) -> List[Tuple[int, List[int]]]:
        r""" Returns a list of (uid, weight vals) pairs for each active peer on chain.
        Returns:
            weight_vals (List[Tuple[int, List[int]]]):
                List of weight val pairs.
        """
        result = self.substrate.iterate_map(module='SubtensorModule',
                                            storage_function='WeightVals',
                                            block_hash=hash)
        return result

    def get_weight_uids(self, hash=None) -> List[Tuple[int, int]]:
        r""" Returns a list of (uid, weight uids) pairs for each active peer on chain.
        Returns:
            weight_uids (List[Tuple[int, List[int]]]):
                List of weight uid pairs
        """
        result = self.substrate.iterate_map(module='SubtensorModule',
                                            storage_function='WeightUids',
                                            block_hash=hash)
        return result

    def neurons(self, hash=None) -> List[Tuple[int, dict]]:
        r""" Returns a list of neuron from the chain. 
        Returns:
            neuron (List[Tuple[int, dict]]):
                List of neuron objects.
        """
        neurons = self.substrate.iterate_map(module='SubtensorModule',
                                             storage_function='Neurons',
                                             block_hash=hash)
        return neurons

    # def __convert_neuron(self, data) -> dict:
    #
    #     return dict({
    #         'coldkey': data['coldkey'],
    #         'hotkey': data['hotkey'],
    #         'ip_type': int(data['ip_type']),
    #         'ip': int(data['ip']),
    #         'port': int(data['port']),
    #         'modality': int(data['modality']),
    #         'uid': int(data['uid'])
    #     })

    def get_uid_for_pubkey(self, pubkey=str) -> Optional[int]:
        """ Returns the uid of the peer given passed public key string.
        Args:
            pubkey (str):
                String encoded public key.
        Returns:
            uid (int):
                uid of peer with hotkey equal to passed public key.
        """
        result = self.substrate.get_runtime_state(module='SubtensorModule',
                                                  storage_function='Active',
                                                  params=[pubkey])

        if result['result'] is None:
            return None
        return int(result['result'])

    def get_neuron_for_uid(self, uid) -> dict:
        """ Returns the neuron metadata of the peer with the passed uid.
        Args:
            uid (int):
                Uid to query for metadata.
        Returns:
            metadata (Dict):
                Dict in list form containing metadata of associated uid.
        """
        result = self.substrate.get_runtime_state(module='SubtensorModule',
                                                  storage_function='Neurons',
                                                  params=[uid])
        return result['result']

    def get_stake_for_uid(self, uid) -> Balance:
        r""" Returns the staked token amount of the peer with the passed uid.
        Args:
            uid (int):
                Uid to query for metadata.
        Returns:
            stake (int):
                Amount of staked token.
        """
        stake = self.substrate.get_runtime_state(module='SubtensorModule',
                                                 storage_function='Stake',
                                                 params=[uid])
        result = stake['result']
        if not result:
            return Balance(0)
        return Balance(result)

    def weight_uids_for_uid(self, uid) -> List[int]:
        r""" Returns the weight uids of the peer with the passed uid.
        Args:
            uid (int):
                Uid to query for metadata.
        Returns:
            weight_uids (List[int]):
                Weight uids for passed uid.
        """
        result = self.substrate.get_runtime_state(
            module='SubtensorModule',
            storage_function='WeightUids',
            params=[uid])
        return result['result']

    def weight_vals_for_uid(self, uid) -> List[int]:
        r""" Returns the weight vals of the peer with the passed uid.
        Args:
            uid (int):
                Uid to query for metadata.
        Returns:
            weight_vals (List[int]):
                Weight vals for passed uid.
        """
        result = self.substrate.get_runtime_state(
            module='SubtensorModule',
            storage_function='WeightVals',
            params=[uid])
        return result['result']

    def get_last_emit_data_for_uid(self, uid) -> int:
        r""" Returns the last emit of the peer with the passed uid.
        Args:
            uid (int):
                Uid to query for metadata.
        Returns:
            last_emit (int):
                Last emit block numebr
        """
        result = self.substrate.get_runtime_state(module='SubtensorModule',
                                                  storage_function='LastEmit',
                                                  params=[uid])
        return result['result']