def _send_request(self, suffix, data=None, content_type=None):
        if self.url.startswith("http://"):
            url = "{}/{}".format(self.url, suffix)
        else:
            url = "http://{}/{}".format(self.url, suffix)

        headers = {}

        if content_type is not None:
            headers['Content-Type'] = content_type

        try:
            if data is not None:
                result = requests.post(url, headers=headers, data=data)
            else:
                result = requests.get(url, headers=headers)

            if result.status_code == 404:
                raise KeyNotFound("404")

            elif not result.ok:
                raise ClientException("Error {}: {}".format(
                    result.status_code, result.reason))

        except requests.ConnectionError as err:
            raise ClientException(
                'Failed to connect to REST API: {}'.format(err))

        return result.text
示例#2
0
    def _handle_response(self, msg_type, resp_proto, req):
        self._stream.wait_for_ready()
        future = self._stream.send(message_type=msg_type,
                                   content=req.SerializeToString())

        resp = resp_proto()
        try:
            resp.ParseFromString(future.result().content)
        except (DecodeError, AttributeError):
            raise ClientException(
                'Failed to parse "content" string from validator')
        except ValidatorConnectionError as vce:
            LOGGER.error('Error: %s' % vce)
            raise ClientException(
                'Failed with ZMQ interaction: {0}'.format(vce))

        data = message_to_dict(resp)

        # NOTE: Not all protos have this status
        with suppress(AttributeError):
            if resp.status == resp_proto.NO_RESOURCE:
                raise KeyNotFound("404")

        if resp.status != resp_proto.OK:
            raise ClientException("Error: %s" % data)

        return data
示例#3
0
    def _handle_response(self, msg_type, resp_proto, req):
        self._stream.wait_for_ready()

        future = self._stream.send(message_type=msg_type,
                                   content=req.SerializeToString())

        resp = resp_proto()

        try:
            resp.ParseFromString(future.result(ZMQ_CONNECTION_TIMEOUT).content)
        except (DecodeError, AttributeError):
            raise ClientException(
                'Failed to parse "content" string from validator')
        except ValidatorConnectionError as vce:
            raise ClientException(
                'Failed with ZMQ interaction: {0}'.format(vce))
        except (asyncio.TimeoutError, FutureTimeoutError):
            raise ClientException('Validator connection timeout')
        except Exception as e:
            LOGGER.exception(e)
            raise ClientException('Unexpected validator error')

        data = message_to_dict(resp)

        with suppress(AttributeError):
            LOGGER.debug(f'The response parsed data: {data}')
            if resp.status == resp_proto.NO_RESOURCE:
                raise KeyNotFound('Resource not found')
            elif resp.status == resp_proto.NOT_READY:
                raise ValidatorNotReadyException('Validator is not ready yet')
            elif resp.status != resp_proto.OK:
                raise ClientException('Error occured')

        return data
示例#4
0
    def _text_request(self, suffix, data=None, content_type=None):
        url = f"{self.url}/{suffix}"

        if not url.startswith("http://"):
            url = f"http://{url}"

        headers = {}
        if content_type is not None:
            headers['Content-Type'] = content_type

        try:
            if data is not None:
                with suppress(AttributeError):
                    data = data.SerializeToString()

                result = requests.post(url, headers=headers, data=data)
            else:
                result = requests.get(url, headers=headers)

            if result.status_code == 404:
                raise KeyNotFound("404")

            elif not result.ok:
                raise ClientException("Error {}: {}".format(
                    result.status_code, result.reason))

        except requests.ConnectionError as err:
            raise ClientException(
                'Failed to connect to REST API: {}'.format(err))

        return json.loads(result.text)
示例#5
0
    def _send_transaction(self, method, data_pb, addresses_input,
                          addresses_output):
        '''
           Signs and sends transaction to the network using rest-api.

           :param str method: The method (defined in proto) for Transaction Processor to process the request.
           :param dict data: Dictionary that is required by TP to process the transaction.
           :param str addresses_input: list of addresses(keys) for which to get state.
           :param str addresses_output: list of addresses(keys) for which to save state.
        '''
        addresses_input_output = []
        addresses_input_output.extend(addresses_input)
        addresses_input_output.extend(addresses_output)
        addresses_input_output = list(set(addresses_input_output))
        # forward transaction to test helper
        if self.test_helper:
            self.test_helper.send_transaction(method, data_pb,
                                              addresses_input_output)
            return

        payload = TransactionPayload()
        payload.method = method
        payload.data = data_pb.SerializeToString()

        for address in addresses_input_output:
            if not is_address(address):
                raise ClientException(
                    'one of addresses_input_output {} is not an address'.
                    format(addresses_input_output))

        batch_list = self.make_batch_list(payload, addresses_input,
                                          addresses_output)

        return get_batch_id(self._send_request('batches', batch_list,
                                               'socket'))
示例#6
0
 def _send_request(self, suffix, data=None, conn_protocol='text', **kwargs):
     if conn_protocol == 'text':
         return self._text_request(suffix, data, **kwargs)
     elif conn_protocol == 'socket':
         return self._socket_request(suffix, data, **kwargs)
     raise ClientException('Unsupported connection protocol "%s"' %
                           conn_protocol)
 def _get_status(self, batch_id, wait):
     try:
         result = self._send_request(
             'batch_statuses?id={}&wait={}'.format(batch_id, wait), )
         return yaml.safe_load(result)['data'][0]['status']
     except BaseException as err:
         raise ClientException(err)
    def _send_transaction(self, method, data_pb, addresses_input_output):
        '''
           Signs and sends transaction to the network using rest-api.

           :param str method: The method (defined in proto) for Transaction Processor to process the request.
           :param dict data: Dictionary that is required by TP to process the transaction.
           :param str addresses_input_output: list of addresses(keys) for which to get and save state.
        '''
        payload = TransactionPayload()
        payload.method = method
        payload.data = data_pb.SerializeToString()

        for address in addresses_input_output:
            if not self.is_address(address):
                raise ClientException(
                    'one of addresses_input_output {} is not an address'.
                    format(addresses_input_output))

        batch_list = self.make_batch_list(payload, addresses_input_output)

        return self._send_request(
            "batches",
            batch_list.SerializeToString(),
            'application/octet-stream',
        )
示例#9
0
    def get_signer_priv_key_from_file(keyfile):
        try:
            with open(keyfile) as fd:
                private_key_str = fd.read().strip()
        except OSError as err:
            raise ClientException('Failed to read private key: {}'.format(
                str(err)))

        try:
            private_key = Secp256k1PrivateKey.from_hex(private_key_str)
        except ParseError as e:
            raise ClientException('Unable to load private key: {}'.format(
                str(e)))

        context = create_context('secp256k1')
        return CryptoFactory(context).new_signer(private_key)
示例#10
0
 def generate_signer(keyfile=None):
     context = create_context('secp256k1')
     private_key = context.new_random_private_key()
     if keyfile:
         try:
             with open(keyfile, 'w') as fd:
                 fd.write(private_key.as_hex())
         except OSError as err:
             raise ClientException(f'Failed to write private key: {err}')
     return CryptoFactory(context).new_signer(private_key)
示例#11
0
async def unsubscribe(request):
    ws = request.ws
    if ws is None:
        raise ClientException(
            message='Subscription available only through websocket')

    request.params = request.params or {}
    try:
        event_type = request.params['event_type']
    except KeyError as e:
        raise RpcInvalidParamsError(message='Missed event_type')

    async with event_lock:
        subsevt = request.rpc._subsevt.get(ws, {})
        try:
            del subsevt[event_type]
        except KeyError:
            raise ClientException(message='Subscription not found')

    return 'UNSUBSCRIBED'
示例#12
0
    def __init__(self, family_handler, keyfile=PRIV_KEY_FILE):
        self.url = REST_API_URL
        self._family_handler = family_handler

        try:
            with open(keyfile) as fd:
                private_key_str = fd.read().strip()
                fd.close()
        except OSError as err:
            raise ClientException('Failed to read private key: {}'.format(
                str(err)))

        try:
            private_key = Secp256k1PrivateKey.from_hex(private_key_str)
        except ParseError as e:
            raise ClientException('Unable to load private key: {}'.format(
                str(e)))

        self._signer = CryptoFactory(
            create_context('secp256k1')).new_signer(private_key)
示例#13
0
 def _socket_request(self, suffix, data=None):
     if suffix == 'batches':
         return self.submit_batches({'batches': data.batches})
     elif 'batch_statuses?id=' in suffix:
         _, batch_id = suffix.split('?id=')
         return self.get_batch_statuses({'batch_ids': [batch_id]})
     elif 'state/' in suffix:
         _, address = suffix.split('/')
         _, root = self.get_root_block()
         return self.fetch_state({'state_root': root, 'address': address})
     else:
         raise ClientException('Suffix "%s" not supported' % suffix)
示例#14
0
    def validate(self, msg_id, params):

        from_block = params.get('from_block')
        swap_id = params.get('id')

        if from_block and not isinstance(from_block, str):
            raise ClientException(message='Invalid "from_block" type',
                                  msg_id=swap_id)

        if from_block and not BLOCK_ID_REGEXP.match(from_block):
            raise RpcInvalidParamsError(
                message='Incorrect atomic swap from block identifier.',
                msg_id=msg_id)

        if swap_id and not SWAP_ID_REGEXP.match(swap_id):
            raise RpcInvalidParamsError(
                message='Incorrect atomic swap identifier.', msg_id=msg_id)

        return {
            'from_block': from_block,
            'id': swap_id,
        }
示例#15
0
    def get_root_block(self):
        resp = self._handle_response(
            Message.CLIENT_BLOCK_LIST_REQUEST, ClientBlockListResponse,
            ClientBlockListRequest(paging=ClientPagingControls(limit=1)))
        block = resp['blocks'][0]
        header = BlockHeader()
        try:
            header_bytes = base64.b64decode(block['header'])
            header.ParseFromString(header_bytes)
        except (KeyError, TypeError, ValueError, DecodeError):
            header = block.get('header', None)
            LOGGER.error(
                'The validator sent a resource with %s %s',
                'a missing header' if header is None else 'an invalid header:',
                header or '')
            raise ClientException()

        block['header'] = message_to_dict(header)

        return (
            block['header_signature'],
            block['header']['state_root_hash'],
        )
示例#16
0
async def subscribe(request):
    ws = request.ws
    if ws is None:
        raise ClientException(
            message='Subscription available only through websocket')

    msg_id = request.msg.data['id']

    request.params = request.params or {}
    try:
        event_type = request.params['event_type']
    except KeyError as e:
        raise RpcInvalidParamsError(message='Missed event_type')

    try:
        evt_tr = EVENT_HANDLERS[event_type]
    except KeyError:
        raise ClientException(message=f'Event "{event_type}" not defined')

    async with event_lock:
        subsevt = request.rpc._subsevt.setdefault(ws, {})
        if event_type in subsevt:
            raise ClientException(
                message=f'Already subscribed to event "{event_type}"')

        router = ws.stream.router

        event_types = set(subsevt.keys())
        event_types.add(event_type)

        LOGGER.debug(f'Events to re-subsribe: {event_types}')

        validated_data = evt_tr.validate(msg_id, request.params)
        from_block = validated_data.get('from_block')
        if not from_block:
            from_block = (await router.list_blocks(limit=1))['head']

        req_msg = evt_tr.prepare_subscribe_message(event_types, from_block)

        LOGGER.debug(f'Request message: {req_msg}')

        msg = await ws.stream.send(
            message_type=Message.CLIENT_EVENTS_SUBSCRIBE_REQUEST,
            message_content=req_msg.SerializeToString(),
            timeout=ZMQ_CONNECTION_TIMEOUT)

        LOGGER.debug(f'Message type: {msg.message_type}')

        # Validate the response type
        if msg.message_type != Message.CLIENT_EVENTS_SUBSCRIBE_RESPONSE:
            raise ClientException(
                message=f'Unexpected message type {msg.message_type}')

        # Parse the response
        response = ClientEventsSubscribeResponse()
        response.ParseFromString(msg.content)

        # Validate the response status
        if response.status != ClientEventsSubscribeResponse.OK:
            if response.status == ClientEventsSubscribeResponse.UNKNOWN_BLOCK:
                raise ClientException(message=f'Unknown block "{from_block}"')
            raise ClientException(message='Subscription failed: Couldn\'t '
                                  'send multipart')

        if ws not in request.rpc._evthashes:
            request.rpc._evthashes[ws] = set()

        LOGGER.debug(f'Create cosumer task for {ws}')
        request.rpc.loop.create_task(_consumer(request))

        if event_type == 'batch':
            LOGGER.debug(f'Create producer task for {ws}')
            request.rpc.loop.create_task(_producer(request))

        subsevt[event_type] = {
            'msg_id': msg_id,
            'validated_data': validated_data,
        }

    return 'SUBSCRIBED'
示例#17
0
 def _get_address(self, pub_key):
     if len(pub_key) > 64:
         raise ClientException("Wrong pub_key size: {}".format(pub_key))
     prefix = self._get_prefix()
     return prefix + pub_key