class RestAPI: """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.address_list_schema = AddressListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def get_our_address(self): return api_response(result=dict( our_address=address_encoder(self.raiden_api.address))) def register_token(self, token_address): manager_address = self.raiden_api.manager_address_if_token_registered( token_address) if manager_address is not None: return api_error(errors='Token is already registered', status_code=HTTPStatus.CONFLICT) if manager_address is None: manager_address = self.raiden_api.register_token(token_address) return api_response(result=dict( channel_manager_address=address_encoder(manager_address)), status_code=HTTPStatus.CREATED) def open(self, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None): try: raiden_service_result = self.raiden_api.open( token_address, partner_address, settle_timeout, reveal_timeout, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, NoTokenManager, DuplicatedChannelError) as e: return api_error(errors=str(e), status_code=HTTPStatus.CONFLICT) if balance: # make initial deposit try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, balance) except EthNodeCommunicationError as e: return api_error(errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT) except InsufficientFunds as e: return api_error(errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data, status_code=HTTPStatus.CREATED) def deposit(self, token_address, partner_address, amount): try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount) except EthNodeCommunicationError as e: return api_error(errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT) except InsufficientFunds as e: return api_error(errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close(token_address, partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def connect(self, token_address, funds, initial_channel_target=None, joinable_funds_target=None): try: self.raiden_api.connect_token_network(token_address, funds, initial_channel_target, joinable_funds_target) except EthNodeCommunicationError as e: return api_error(errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT) except InsufficientFunds as e: return api_error(errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED) return api_response(result=dict(), status_code=HTTPStatus.NO_CONTENT) def leave(self, token_address, only_receiving): closed_channels = self.raiden_api.leave_token_network( token_address, only_receiving) closed_channels = [ channel.channel_address for channel in closed_channels ] channel_addresses_list = AddressList(closed_channels) result = self.address_list_schema.dump(channel_addresses_list) return api_response(result=result.data) def get_channel_list(self, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( token_address, partner_address) assert isinstance(raiden_service_result, list) channel_list = ChannelList(raiden_service_result) result = self.channel_list_schema.dump(channel_list) return api_response(result=result.data) def get_tokens_list(self): raiden_service_result = self.raiden_api.get_tokens_list() assert isinstance(raiden_service_result, list) tokens_list = AddressList(raiden_service_result) result = self.address_list_schema.dump(tokens_list) return api_response(result=result.data) def get_network_events(self, from_block, to_block): raiden_service_result = self.raiden_api.get_network_events( from_block, to_block) return api_response( result=normalize_events_list(raiden_service_result)) def get_token_network_events(self, token_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block) return api_response( result=normalize_events_list(raiden_service_result)) except UnknownTokenAddress as e: return api_error(str(e), status_code=HTTPStatus.NOT_FOUND) def get_channel_events(self, channel_address, from_block, to_block): raiden_service_result = self.raiden_api.get_channel_events( channel_address, from_block, to_block) return api_response( result=normalize_events_list(raiden_service_result)) def get_channel(self, channel_address): channel = self.raiden_api.get_channel(channel_address) result = self.channel_schema.dump(channel_to_api_dict(channel)) return api_response(result=result.data) def get_partners_by_token(self, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list(token_address) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebychanneladdress', channel_address=result.channel_address), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return api_response(result=result.data) def initiate_transfer(self, token_address, target_address, amount, identifier): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( token_address=token_address, target=target_address, amount=amount, identifier=identifier) except (InvalidAmount, InvalidAddress, NoPathError) as e: return api_error(errors=str(e), status_code=HTTPStatus.CONFLICT) except InsufficientFunds as e: return api_error(errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED) if transfer_result is False: return api_error(errors="Payment couldn't be completed " "(insufficient funds or no route to target).", status_code=HTTPStatus.CONFLICT) transfer = { 'initiator_address': self.raiden_api.raiden.address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return api_response(result=result.data) def _deposit(self, channel, balance): if channel.state != CHANNEL_STATE_OPENED: return api_error( errors="Can't deposit on a closed channel", status_code=HTTPStatus.CONFLICT, ) try: raiden_service_result = self.raiden_api.deposit( channel.token_address, channel.partner_address, balance) except InsufficientFunds as e: return api_error(errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def _close(self, channel): if channel.state != CHANNEL_STATE_OPENED: return api_error( errors='Attempted to close an already closed channel', status_code=HTTPStatus.CONFLICT, ) raiden_service_result = self.raiden_api.close(channel.token_address, channel.partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def _settle(self, channel): if channel.state != CHANNEL_STATE_CLOSED: return api_error( errors='Attempted to settle a channel at its {} state'.format( channel.state, ), status_code=HTTPStatus.CONFLICT, ) try: raiden_service_result = self.raiden_api.settle( channel.token_address, channel.partner_address) except InvalidState: return api_error( errors='Settlement period is not yet over', status_code=HTTPStatus.CONFLICT, ) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def patch_channel(self, channel_address, balance=None, state=None): if balance is not None and state is not None: return api_error( errors= 'Can not update balance and change channel state at the same time', status_code=HTTPStatus.CONFLICT, ) if balance is None and state is None: return api_error( errors= "Nothing to do. Should either provide 'balance' or 'state' argument", status_code=HTTPStatus.BAD_REQUEST, ) try: channel = self.raiden_api.get_channel(channel_address) except ChannelNotFound: return api_error( errors='Requested channel {} not found'.format( address_encoder(channel_address)), status_code=HTTPStatus.CONFLICT, ) if balance is not None: result = self._deposit(channel, balance) elif state == CHANNEL_STATE_CLOSED: result = self._close(channel) elif state == CHANNEL_STATE_SETTLED: result = self._settle(channel) else: # should never happen, channel_state is validated in the schema result = api_error( errors='Provided invalid channel state {}'.format(state), status_code=HTTPStatus.BAD_REQUEST, ) return result
class RestAPI(object): """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.tokens_list_schema = TokensListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def open(self, partner_address, token_address, settle_timeout, balance=None): raiden_service_result = self.raiden_api.open(token_address, partner_address, settle_timeout) if balance: # make initial deposit raiden_service_result = self.raiden_api.deposit( token_address, partner_address, balance) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) def deposit(self, token_address, partner_address, amount): raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close(token_address, partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) def connect(self, token_address, funds, initial_channel_target=None, joinable_funds_target=None): self.raiden_api.connect_token_network(token_address, funds, initial_channel_target, joinable_funds_target) def leave(self, token_address, wait_for_settle=None, timeout=None): self.raiden_api.leave_token_network(token_address, wait_for_settle, timeout) def get_channel_list(self, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( token_address, partner_address) assert isinstance(raiden_service_result, list) channel_list = ChannelList(raiden_service_result) result = self.channel_list_schema.dump(channel_list) return jsonify(result.data) def get_tokens_list(self): raiden_service_result = self.raiden_api.get_tokens_list() assert isinstance(raiden_service_result, list) new_list = [] for result in raiden_service_result: new_list.append({'address': result}) tokens_list = TokensList(new_list) result = self.tokens_list_schema.dump(tokens_list) return jsonify(result.data) def get_network_events(self, from_block, to_block): raiden_service_result = self.raiden_api.get_network_events( from_block, to_block) return normalize_events_list(raiden_service_result) def get_token_network_events(self, token_address, from_block, to_block): raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block) return normalize_events_list(raiden_service_result) def get_channel_events(self, channel_address, from_block, to_block): raiden_service_result = self.raiden_api.get_channel_events( channel_address, from_block, to_block) return normalize_events_list(raiden_service_result) def get_channel(self, channel_address): channel = self.raiden_api.get_channel(channel_address) result = self.channel_schema.dump(channel_to_api_dict(channel)) return jsonify(result.data) def get_partners_by_token(self, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list(token_address) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebychanneladdress', channel_address=result.channel_address), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return jsonify(result.data) def initiate_transfer(self, token_address, target_address, amount, identifier): if identifier is None: identifier = self.raiden_api.create_default_identifier( target_address, token_address) try: self.raiden_api.transfer(token_address=token_address, target=target_address, amount=amount, identifier=identifier) except (InvalidAmount, InvalidAddress, NoPathError) as e: return make_response(str(e), httplib.CONFLICT) transfer = { 'initiator_address': self.raiden_api.raiden.address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return jsonify(result.data) def patch_channel(self, channel_address, balance=None, state=None): if balance is not None and state is not None: return make_response( 'Can not update balance and change channel state at the same time', httplib.CONFLICT, ) elif balance is None and state is None: return make_response( 'Nothing to do. Should either provide \'balance\' or \'state\' argument', httplib.BAD_REQUEST, ) # find the channel channel = self.raiden_api.get_channel(channel_address) current_state = channel.state # if we patch with `balance` it's a deposit if balance is not None: if current_state != CHANNEL_STATE_OPENED: return make_response( "Can't deposit on a closed channel", httplib.CONFLICT, ) raiden_service_result = self.raiden_api.deposit( channel.token_address, channel.partner_address, balance) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) if state == CHANNEL_STATE_CLOSED: if current_state != CHANNEL_STATE_OPENED: return make_response( httplib.CONFLICT, 'Attempted to close an already closed channel') raiden_service_result = self.raiden_api.close( channel.token_address, channel.partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) if state == CHANNEL_STATE_SETTLED: if current_state == CHANNEL_STATE_SETTLED or current_state == CHANNEL_STATE_OPENED: return make_response( 'Attempted to settle a channel at its {} state'.format( current_state), httplib.CONFLICT, ) raiden_service_result = self.raiden_api.settle( channel.token_address, channel.partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) # should never happen, channel_state is validated in the schema return make_response( 'Provided invalid channel state {}'.format(state), httplib.BAD_REQUEST, ) def token_swap(self, target_address, identifier, role, sending_token, sending_amount, receiving_token, receiving_amount): if role == 'maker': self.raiden_api.token_swap( from_token=sending_token, from_amount=sending_amount, to_token=receiving_token, to_amount=receiving_amount, target_address=target_address, ) elif role == 'taker': self.raiden_api.expect_token_swap( identifier=identifier, from_token=sending_token, from_amount=sending_amount, to_token=receiving_token, to_amount=receiving_amount, target_address=target_address, ) else: # should never happen, role is validated in the schema return make_response( 'Provided invalid token swap role {}'.format(role), httplib.BAD_REQUEST, )
class RestAPI: """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.address_list_schema = AddressListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def get_our_address(self): return api_response(result=dict( our_address=to_checksum_address(self.raiden_api.address)), ) def register_token(self, registry_address, token_address): try: manager_address = self.raiden_api.token_network_register( registry_address, token_address, ) except (InvalidAddress, AlreadyRegisteredTokenAddress, TransactionThrew) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) return api_response( result=dict( channel_manager_address=to_checksum_address(manager_address)), status_code=HTTPStatus.CREATED, ) def open( self, registry_address, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None, ): try: self.raiden_api.channel_open( registry_address, token_address, partner_address, settle_timeout, reveal_timeout, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, NoTokenManager, DuplicatedChannelError) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) if balance: # make initial deposit try: self.raiden_api.channel_deposit( registry_address, token_address, partner_address, balance, ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) channel_state = views.get_channelstate_for( views.state_from_raiden(self.raiden_api.raiden), registry_address, token_address, partner_address, ) result = self.channel_schema.dump( channelstate_to_api_dict(channel_state)) return api_response( result=checksummed_response_dict(result.data), status_code=HTTPStatus.CREATED, ) def deposit(self, registry_address, token_address, partner_address, amount): try: raiden_service_result = self.raiden_api.channel_deposit( registry_address, token_address, partner_address, amount, ) except ChannelBusyError as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) result = self.channel_schema.dump( channelstate_to_api_dict(raiden_service_result)) return api_response(result=checksummed_response_dict(result.data)) def close(self, registry_address, token_address, partner_address): try: raiden_service_result = self.raiden_api.channel_close( registry_address, token_address, partner_address, ) except ChannelBusyError as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) result = self.channel_schema.dump( channelstate_to_api_dict(raiden_service_result)) return api_response(result=checksummed_response_dict(result.data)) def connect( self, registry_address, token_address, funds, initial_channel_target=None, joinable_funds_target=None, ): try: self.raiden_api.token_network_connect( registry_address, token_address, funds, initial_channel_target, joinable_funds_target, ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) return api_response( result=dict(), status_code=HTTPStatus.NO_CONTENT, ) def leave(self, registry_address, token_address, only_receiving): closed_channels = self.raiden_api.token_network_leave( registry_address, token_address, only_receiving, ) closed_channels = [ channel_state.identifier for channel_state in closed_channels ] channel_addresses_list = AddressList(closed_channels) result = self.address_list_schema.dump(channel_addresses_list) return api_response(result=checksummed_response_dict(result.data)) def get_connection_managers_info(self, registry_address): """Get a dict whose keys are token addresses and whose values are open channels, funds of last request, sum of deposits and number of channels""" connection_managers = dict() for token in self.raiden_api.get_tokens_list(registry_address): try: connection_manager = self.raiden_api.raiden.connection_manager_for_token( registry_address, token, ) except InvalidAddress: connection_manager = None open_channels = views.get_channelstate_open( views.state_from_raiden(self.raiden_api.raiden), registry_address, token, ) if connection_manager is not None and open_channels: connection_managers[to_checksum_address( connection_manager.token_address)] = { 'funds': connection_manager.funds, 'sum_deposits': views.get_our_capacity_for_token_network( views.state_from_raiden(self.raiden_api.raiden), registry_address, token, ), 'channels': len(open_channels), } return connection_managers def get_channel_list(self, registry_address, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, partner_address, ) assert isinstance(raiden_service_result, list) channel_list = ChannelList(raiden_service_result) result = self.channel_list_schema.dump(channel_list) return api_response(result=checksummed_response_list(result.data)) def get_tokens_list(self, registry_address): raiden_service_result = self.raiden_api.get_tokens_list( registry_address) assert isinstance(raiden_service_result, list) tokens_list = AddressList(raiden_service_result) result = self.address_list_schema.dump(tokens_list) return api_response(result=checksummed_response_list(result.data)) def get_network_events(self, registry_address, from_block, to_block): raiden_service_result = self.raiden_api.get_network_events( registry_address, from_block, to_block, ) return api_response( result=normalize_events_list(raiden_service_result)) def get_token_network_events(self, token_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block, ) return api_response( result=normalize_events_list(raiden_service_result)) except UnknownTokenAddress as e: return api_error(str(e), status_code=HTTPStatus.NOT_FOUND) def get_channel_events(self, channel_address, from_block, to_block): raiden_service_result = self.raiden_api.get_channel_events( channel_address, from_block, to_block, ) return api_response( result=normalize_events_list(raiden_service_result)) def get_channel(self, registry_address, channel_address): channel_state = self.raiden_api.get_channel(registry_address, channel_address) result = self.channel_schema.dump( channelstate_to_api_dict(channel_state)) return api_response(result=checksummed_response_dict(result.data)) def get_partners_by_token(self, registry_address, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, ) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_state.address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebychanneladdress', channel_address=result.identifier, ), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return api_response(result=checksummed_response_list(result.data)) def initiate_transfer( self, registry_address, token_address, target_address, amount, identifier, ): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( registry_address=registry_address, token_address=token_address, target=target_address, amount=amount, identifier=identifier, ) except (InvalidAmount, InvalidAddress) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) if transfer_result is False: return api_error( errors="Payment couldn't be completed " "(insufficient funds or no route to target).", status_code=HTTPStatus.CONFLICT, ) transfer = { 'initiator_address': self.raiden_api.address, 'registry_address': registry_address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return api_response(result=checksummed_response_dict(result.data)) def _deposit(self, registry_address, channel_state, balance): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors="Can't deposit on a closed channel", status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.channel_deposit( registry_address, channel_state.token_address, channel_state.partner_state.address, balance, ) except ChannelBusyError as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.identifier, ) result = self.channel_schema.dump( channelstate_to_api_dict(updated_channel_state)) return api_response(result=checksummed_response_dict(result.data)) def _close(self, registry_address, channel_state): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors='Attempted to close an already closed channel', status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.channel_close( registry_address, channel_state.token_address, channel_state.partner_state.address, ) except ChannelBusyError as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.identifier, ) result = self.channel_schema.dump( channelstate_to_api_dict(updated_channel_state)) return api_response(result=checksummed_response_dict(result.data)) def patch_channel(self, registry_address, channel_address, balance=None, state=None): if balance is not None and state is not None: return api_error( errors= 'Can not update balance and change channel state at the same time', status_code=HTTPStatus.CONFLICT, ) if balance is None and state is None: return api_error( errors= "Nothing to do. Should either provide 'balance' or 'state' argument", status_code=HTTPStatus.BAD_REQUEST, ) try: channel_state = self.raiden_api.get_channel( registry_address, channel_address, ) except ChannelNotFound: return api_error( errors='Requested channel {} not found'.format( to_checksum_address(channel_address), ), status_code=HTTPStatus.CONFLICT, ) if balance is not None: result = self._deposit(registry_address, channel_state, balance) elif state == CHANNEL_STATE_CLOSED: result = self._close(registry_address, channel_state) else: # should never happen, channel_state is validated in the schema result = api_error( errors='Provided invalid channel state {}'.format(state), status_code=HTTPStatus.BAD_REQUEST, ) return result
class RestAPI: """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.address_list_schema = AddressListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def get_our_address(self): return api_response(result=dict(our_address=address_encoder(self.raiden_api.address))) def register_token(self, token_address): manager_address = self.raiden_api.manager_address_if_token_registered(token_address) if manager_address is not None: return api_error( errors='Token is already registered', status_code=HTTPStatus.CONFLICT ) if manager_address is None: manager_address = self.raiden_api.register_token(token_address) return api_response( result=dict(channel_manager_address=address_encoder(manager_address)), status_code=HTTPStatus.CREATED ) def open( self, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None): try: raiden_service_result = self.raiden_api.open( token_address, partner_address, settle_timeout, reveal_timeout, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, NoTokenManager, DuplicatedChannelError) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT ) if balance: # make initial deposit try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, balance ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response( result=result.data, status_code=HTTPStatus.CREATED ) def deposit(self, token_address, partner_address, amount): try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close( token_address, partner_address ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def connect( self, token_address, funds, initial_channel_target=None, joinable_funds_target=None): try: self.raiden_api.connect_token_network( token_address, funds, initial_channel_target, joinable_funds_target ) except EthNodeCommunicationError as e: return api_error( errors=str(e), status_code=HTTPStatus.REQUEST_TIMEOUT ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED ) return api_response( result=dict(), status_code=HTTPStatus.NO_CONTENT ) def leave(self, token_address, only_receiving): closed_channels = self.raiden_api.leave_token_network(token_address, only_receiving) closed_channels = [channel.channel_address for channel in closed_channels] channel_addresses_list = AddressList(closed_channels) result = self.address_list_schema.dump(channel_addresses_list) return api_response(result=result.data) def get_connection_managers_info(self): raiden_service_result = self.raiden_api.get_connection_managers_info() assert isinstance(raiden_service_result, dict) # encode token addresses indexes result = { address_encoder(token_address): info for token_address, info in raiden_service_result.items() } return api_response(result=result) def get_channel_list(self, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list(token_address, partner_address) assert isinstance(raiden_service_result, list) channel_list = ChannelList(raiden_service_result) result = self.channel_list_schema.dump(channel_list) return api_response(result=result.data) def get_tokens_list(self): raiden_service_result = self.raiden_api.get_tokens_list() assert isinstance(raiden_service_result, list) tokens_list = AddressList(raiden_service_result) result = self.address_list_schema.dump(tokens_list) return api_response(result=result.data) def get_network_events(self, from_block, to_block): raiden_service_result = self.raiden_api.get_network_events( from_block, to_block ) return api_response(result=normalize_events_list(raiden_service_result)) def get_token_network_events(self, token_address, from_block, to_block): raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block ) return api_response(result=normalize_events_list(raiden_service_result)) def get_channel_events(self, channel_address, from_block, to_block): raiden_service_result = self.raiden_api.get_channel_events( channel_address, from_block, to_block ) return api_response(result=normalize_events_list(raiden_service_result)) def get_channel(self, channel_address): channel = self.raiden_api.get_channel(channel_address) result = self.channel_schema.dump(channel_to_api_dict(channel)) return api_response(result=result.data) def get_partners_by_token(self, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list(token_address) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebychanneladdress', channel_address=result.channel_address ), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return api_response(result=result.data) def initiate_transfer(self, token_address, target_address, amount, identifier): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( token_address=token_address, target=target_address, amount=amount, identifier=identifier ) except (InvalidAmount, InvalidAddress, NoPathError) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED ) if transfer_result is False: return api_error( errors="Payment couldn't be completed " "(insufficient funds or no route to target).", status_code=HTTPStatus.CONFLICT ) transfer = { 'initiator_address': self.raiden_api.raiden.address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return api_response(result=result.data) def _deposit(self, channel, balance): if channel.state != CHANNEL_STATE_OPENED: return api_error( errors="Can't deposit on a closed channel", status_code=HTTPStatus.CONFLICT, ) try: raiden_service_result = self.raiden_api.deposit( channel.token_address, channel.partner_address, balance ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def _close(self, channel): if channel.state != CHANNEL_STATE_OPENED: return api_error( errors='Attempted to close an already closed channel', status_code=HTTPStatus.CONFLICT, ) raiden_service_result = self.raiden_api.close( channel.token_address, channel.partner_address ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def _settle(self, channel): if channel.state != CHANNEL_STATE_CLOSED: return api_error( errors='Attempted to settle a channel at its {} state'.format( channel.state, ), status_code=HTTPStatus.CONFLICT, ) try: raiden_service_result = self.raiden_api.settle( channel.token_address, channel.partner_address ) except InvalidState: return api_error( errors='Settlement period is not yet over', status_code=HTTPStatus.CONFLICT, ) result = self.channel_schema.dump(channel_to_api_dict(raiden_service_result)) return api_response(result=result.data) def patch_channel(self, channel_address, balance=None, state=None): if balance is not None and state is not None: return api_error( errors='Can not update balance and change channel state at the same time', status_code=HTTPStatus.CONFLICT, ) if balance is None and state is None: return api_error( errors="Nothing to do. Should either provide 'balance' or 'state' argument", status_code=HTTPStatus.BAD_REQUEST, ) try: channel = self.raiden_api.get_channel(channel_address) except ChannelNotFound: return api_error( errors='Requested channel {} not found'.format(address_encoder(channel_address)), status_code=HTTPStatus.CONFLICT, ) if balance is not None: result = self._deposit(channel, balance) elif state == CHANNEL_STATE_CLOSED: result = self._close(channel) elif state == CHANNEL_STATE_SETTLED: result = self._settle(channel) else: # should never happen, channel_state is validated in the schema result = api_error( errors='Provided invalid channel state {}'.format(state), status_code=HTTPStatus.BAD_REQUEST, ) return result def token_swap( self, target_address, identifier, role, sending_token, sending_amount, receiving_token, receiving_amount): if role == 'maker': self.raiden_api.token_swap( identifier=identifier, maker_token=sending_token, maker_amount=sending_amount, maker_address=self.raiden_api.address, taker_token=receiving_token, taker_amount=receiving_amount, taker_address=target_address, ) elif role == 'taker': self.raiden_api.expect_token_swap( identifier=identifier, maker_token=receiving_token, maker_amount=receiving_amount, maker_address=target_address, taker_token=sending_token, taker_amount=sending_amount, taker_address=self.raiden_api.address ) else: # should never happen, role is validated in the schema return api_error( errors='Provided invalid token swap role {}'.format(role), status_code=HTTPStatus.BAD_REQUEST, ) return api_response(result=dict(), status_code=HTTPStatus.CREATED)
class RestAPI: """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelStateSchema() self.address_list_schema = AddressListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() #####sqlite_demo self.crosstransaction_schema = CrossTransactionSchema() self.crosstransaction_sql_schema = Crosstransaction_sql_schema() def get_our_address(self): return api_response(result=dict( our_address=to_checksum_address(self.raiden_api.address)), ) def register_token(self, registry_address, token_address): try: token_network_address = self.raiden_api.token_network_register( registry_address, token_address, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except (InvalidAddress, AlreadyRegisteredTokenAddress, TransactionThrew) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) return api_response( result=dict(token_network_address=to_checksum_address( token_network_address)), status_code=HTTPStatus.CREATED, ) def open( self, registry_address, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None, ): try: self.raiden_api.channel_open( registry_address, token_address, partner_address, settle_timeout, reveal_timeout, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, DuplicatedChannelError, TokenNotRegistered) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) if balance: # make initial deposit try: self.raiden_api.set_total_channel_deposit( registry_address, token_address, partner_address, balance, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) except DepositOverLimit as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except DepositMismatch as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) channel_state = views.get_channelstate_for( views.state_from_raiden(self.raiden_api.raiden), registry_address, token_address, partner_address, ) result = self.channel_schema.dump(channel_state) return api_response( result=result.data, status_code=HTTPStatus.CREATED, ) def connect( self, registry_address, token_address, funds, initial_channel_target=None, joinable_funds_target=None, ): try: self.raiden_api.token_network_connect( registry_address, token_address, funds, initial_channel_target, joinable_funds_target, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) except InvalidAmount as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) return api_response( result=dict(), status_code=HTTPStatus.NO_CONTENT, ) def leave(self, registry_address, token_address): closed_channels = self.raiden_api.token_network_leave( registry_address, token_address, ) closed_channels = [ self.channel_schema.dump(channel_state).data for channel_state in closed_channels ] return api_response(result=closed_channels) def get_connection_managers_info(self, registry_address): """Get a dict whose keys are token addresses and whose values are open channels, funds of last request, sum of deposits and number of channels""" connection_managers = dict() for token in self.raiden_api.get_tokens_list(registry_address): token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_raiden(self.raiden_api.raiden), payment_network_id=registry_address, token_address=token, ) try: connection_manager = self.raiden_api.raiden.connection_manager_for_token_network( token_network_identifier, ) except InvalidAddress: connection_manager = None open_channels = views.get_channelstate_open( chain_state=views.state_from_raiden(self.raiden_api.raiden), payment_network_id=registry_address, token_address=token, ) if connection_manager is not None and open_channels: connection_managers[to_checksum_address( connection_manager.token_address)] = { 'funds': connection_manager.funds, 'sum_deposits': views.get_our_capacity_for_token_network( views.state_from_raiden(self.raiden_api.raiden), registry_address, token, ), 'channels': len(open_channels), } return connection_managers def get_channel_list(self, registry_address, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, partner_address, ) assert isinstance(raiden_service_result, list) result = [ self.channel_schema.dump(channel_schema).data for channel_schema in raiden_service_result ] return api_response(result=result) def get_tokens_list(self, registry_address): raiden_service_result = self.raiden_api.get_tokens_list( registry_address) assert isinstance(raiden_service_result, list) tokens_list = AddressList(raiden_service_result) result = self.address_list_schema.dump(tokens_list) return api_response(result=result.data) def get_network_events(self, registry_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_network_events( registry_address, from_block, to_block, ) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) return api_response( result=normalize_events_list(raiden_service_result)) def get_token_network_events(self, token_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block, ) return api_response( result=normalize_events_list(raiden_service_result)) except UnknownTokenAddress as e: return api_error(str(e), status_code=HTTPStatus.NOT_FOUND) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) def get_channel_events( self, token_address, partner_address=None, from_block=None, to_block=None, ): try: raiden_service_result = self.raiden_api.get_channel_events( token_address, partner_address, from_block, to_block, ) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) return api_response( result=normalize_events_list(raiden_service_result)) def get_channel(self, registry_address, token_address, partner_address): try: channel_state = self.raiden_api.get_channel( registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) result = self.channel_schema.dump(channel_state) return api_response(result=result.data) except ChannelNotFound as e: return api_error( errors=str(e), status_code=HTTPStatus.NOT_FOUND, ) def get_partners_by_token(self, registry_address, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, ) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_state.address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebytokenandpartneraddress', token_address=token_address, partner_address=result.partner_state.address, ), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return api_response(result=result.data) def initiate_transfer( self, registry_address, token_address, target_address, amount, identifier, ): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( registry_address=registry_address, token_address=token_address, target=target_address, amount=amount, identifier=identifier, ) except (InvalidAmount, InvalidAddress) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) if transfer_result is False: return api_error( errors="Payment couldn't be completed " "(insufficient funds, no route to target or target offline).", status_code=HTTPStatus.CONFLICT, ) transfer = { 'initiator_address': self.raiden_api.address, 'registry_address': registry_address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return api_response(result=result.data) def _deposit(self, registry_address, channel_state, total_deposit): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors="Can't set total deposit on a closed channel", status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.set_total_channel_deposit( registry_address, channel_state.token_address, channel_state.partner_state.address, total_deposit, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) except DepositOverLimit as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except DepositMismatch as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.token_address, channel_state.partner_state.address, ) result = self.channel_schema.dump(updated_channel_state) return api_response(result=result.data) def _close(self, registry_address, channel_state): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors='Attempted to close an already closed channel', status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.channel_close( registry_address, channel_state.token_address, channel_state.partner_state.address, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.token_address, channel_state.partner_state.address, ) result = self.channel_schema.dump(updated_channel_state) return api_response(result=result.data) def patch_channel( self, registry_address, token_address, partner_address, total_deposit=None, state=None, ): if total_deposit is not None and state is not None: return api_error( errors= "Can not update a channel's total deposit and state at the same time", status_code=HTTPStatus.CONFLICT, ) if total_deposit is None and state is None: return api_error( errors= "Nothing to do. Should either provide 'total_deposit' or 'state' argument", status_code=HTTPStatus.BAD_REQUEST, ) try: channel_state = self.raiden_api.get_channel( registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) except ChannelNotFound: return api_error( errors='Requested channel for token {} and partner {} not found' .format( to_checksum_address(token_address), to_checksum_address(partner_address), ), status_code=HTTPStatus.CONFLICT, ) if total_deposit is not None: result = self._deposit(registry_address, channel_state, total_deposit) elif state == CHANNEL_STATE_CLOSED: result = self._close(registry_address, channel_state) else: # should never happen, channel_state is validated in the schema result = api_error( errors='Provided invalid channel state {}'.format(state), status_code=HTTPStatus.BAD_REQUEST, ) return result #demo def start_cross(self, registry_address, token_address, target_address, initiator_address, sendETH_amount, sendBTC_amount, receiveBTC_address, cross_type, identifier=None): if identifier is None: identifier = create_default_identifier() try: self.raiden_api.crosstransaction_async( registry_address, token_address, target_address, initiator_address, sendETH_amount, sendBTC_amount, receiveBTC_address, cross_type, identifier) except Exception as e: print(str(e)) return api_error( errors="cross err:" + str(e), status_code=HTTPStatus.CONFLICT, ) cross_transfer = { 'initiator_address': initiator_address, 'target_address': target_address, 'token_address': token_address, 'sendETH_amount': sendETH_amount, 'sendBTC_amount': sendBTC_amount, 'cross_type': cross_type, } result = self.crosstransaction_schema.dump(cross_transfer) return api_response(result=result.data) #demo def get_crosstransaction(self, cross_id): result = self.raiden_api.get_crosstransaction_by_id(cross_id) crosstransaction = { "crossid": result[0], "initiator_address": result[1], "target_address": result[2], "token_network_identifier": result[3], 'sendETH_amount': result[4], 'sendBTC_amount': result[5], 'status': result[7], } result = self.crosstransaction_sql_schema.dump(crosstransaction) return api_response(result=result.data) def get_crosstransaction_all(self): res = self.raiden_api.get_crosstransaction_all() print(res) crosstransaction_all = list(dict()) for result in res: crosstransaction = { "crossid": result[0], "initiator_address": result[1], "target_address": result[2], "token_network_identifier": result[3], 'sendETH_amount': result[4], 'sendBTC_amount': result[5], 'status': result[7], } crosstransaction_all.append(crosstransaction) print(crosstransaction_all) return api_response(result=crosstransaction_all) def state_change_by_r(self, hashr): return self.raiden_api.get_state_change_by_r(hashr) def post_lnd(self, port, identity, address, macaroon): return self.raiden_api.post_lnd(port, identity, address, macaroon) def get_lnd(self): return self.raiden_api.get_lnd()
class RestAPI: """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelStateSchema() self.address_list_schema = AddressListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def get_our_address(self): return api_response( result=dict(our_address=to_checksum_address(self.raiden_api.address)), ) def register_token(self, registry_address, token_address): try: token_network_address = self.raiden_api.token_network_register( registry_address, token_address, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except (InvalidAddress, AlreadyRegisteredTokenAddress, TransactionThrew) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) return api_response( result=dict(token_network_address=to_checksum_address(token_network_address)), status_code=HTTPStatus.CREATED, ) def open( self, registry_address, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None, ): try: self.raiden_api.channel_open( registry_address, token_address, partner_address, settle_timeout, reveal_timeout, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, DuplicatedChannelError) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) if balance: # make initial deposit try: self.raiden_api.set_total_channel_deposit( registry_address, token_address, partner_address, balance, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) except DepositOverLimit as e: return api_error( errors=str(e), status_code=HTTPStatus.EXPECTATION_FAILED, ) channel_state = views.get_channelstate_for( views.state_from_raiden(self.raiden_api.raiden), registry_address, token_address, partner_address, ) result = self.channel_schema.dump(channel_state) return api_response( result=result.data, status_code=HTTPStatus.CREATED, ) def connect( self, registry_address, token_address, funds, initial_channel_target=None, joinable_funds_target=None, ): try: self.raiden_api.token_network_connect( registry_address, token_address, funds, initial_channel_target, joinable_funds_target, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) return api_response( result=dict(), status_code=HTTPStatus.NO_CONTENT, ) def leave(self, registry_address, token_address, only_receiving): closed_channels = self.raiden_api.token_network_leave( registry_address, token_address, only_receiving, ) closed_channels = [ self.channel_schema.dump(channel_state).data for channel_state in closed_channels ] return api_response(result=closed_channels) def get_connection_managers_info(self, registry_address): """Get a dict whose keys are token addresses and whose values are open channels, funds of last request, sum of deposits and number of channels""" connection_managers = dict() for token in self.raiden_api.get_tokens_list(registry_address): token_network_identifier = views.get_token_network_identifier_by_token_address( views.state_from_raiden(self.raiden_api.raiden), payment_network_id=registry_address, token_address=token, ) try: connection_manager = self.raiden_api.raiden.connection_manager_for_token_network( token_network_identifier, ) except InvalidAddress: connection_manager = None open_channels = views.get_channelstate_open( chain_state=views.state_from_raiden(self.raiden_api.raiden), payment_network_id=registry_address, token_address=token, ) if connection_manager is not None and open_channels: connection_managers[to_checksum_address(connection_manager.token_address)] = { 'funds': connection_manager.funds, 'sum_deposits': views.get_our_capacity_for_token_network( views.state_from_raiden(self.raiden_api.raiden), registry_address, token, ), 'channels': len(open_channels), } return connection_managers def get_channel_list(self, registry_address, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, partner_address, ) assert isinstance(raiden_service_result, list) result = [ self.channel_schema.dump(channel_schema).data for channel_schema in raiden_service_result ] return api_response(result=result) def get_tokens_list(self, registry_address): raiden_service_result = self.raiden_api.get_tokens_list(registry_address) assert isinstance(raiden_service_result, list) tokens_list = AddressList(raiden_service_result) result = self.address_list_schema.dump(tokens_list) return api_response(result=result.data) def get_network_events(self, registry_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_network_events( registry_address, from_block, to_block, ) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) return api_response(result=normalize_events_list(raiden_service_result)) def get_token_network_events(self, token_address, from_block, to_block): try: raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block, ) return api_response(result=normalize_events_list(raiden_service_result)) except UnknownTokenAddress as e: return api_error(str(e), status_code=HTTPStatus.NOT_FOUND) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) def get_channel_events( self, token_address, partner_address=None, from_block=None, to_block=None, ): try: raiden_service_result = self.raiden_api.get_channel_events( token_address, partner_address, from_block, to_block, ) except InvalidBlockNumberInput as e: return api_error(str(e), status_code=HTTPStatus.CONFLICT) return api_response(result=normalize_events_list(raiden_service_result)) def get_channel(self, registry_address, token_address, partner_address): try: channel_state = self.raiden_api.get_channel( registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) result = self.channel_schema.dump(channel_state) return api_response(result=result.data) except ChannelNotFound as e: return api_error( errors=str(e), status_code=HTTPStatus.NOT_FOUND, ) def get_partners_by_token(self, registry_address, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list( registry_address, token_address, ) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_state.address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebytokenandpartneraddress', token_address=token_address, partner_address=result.partner_state.address, ), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return api_response(result=result.data) def initiate_transfer( self, registry_address, token_address, target_address, amount, identifier, ): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( registry_address=registry_address, token_address=token_address, target=target_address, amount=amount, identifier=identifier, ) except (InvalidAmount, InvalidAddress) as e: return api_error( errors=str(e), status_code=HTTPStatus.CONFLICT, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) if transfer_result is False: return api_error( errors="Payment couldn't be completed " "(insufficient funds or no route to target).", status_code=HTTPStatus.CONFLICT, ) transfer = { 'initiator_address': self.raiden_api.address, 'registry_address': registry_address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return api_response(result=result.data) def _deposit(self, registry_address, channel_state, total_deposit): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors="Can't set total deposit on a closed channel", status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.set_total_channel_deposit( registry_address, channel_state.token_address, channel_state.partner_state.address, total_deposit, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) except DepositOverLimit as e: return api_error( errors=str(e), status_code=HTTPStatus.EXPECTATION_FAILED, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.token_address, channel_state.partner_state.address, ) result = self.channel_schema.dump(updated_channel_state) return api_response(result=result.data) def _close(self, registry_address, channel_state): if channel.get_status(channel_state) != CHANNEL_STATE_OPENED: return api_error( errors='Attempted to close an already closed channel', status_code=HTTPStatus.CONFLICT, ) try: self.raiden_api.channel_close( registry_address, channel_state.token_address, channel_state.partner_state.address, ) except EthNodeCommunicationError: return api_response( result='', status_code=HTTPStatus.ACCEPTED, ) except InsufficientFunds as e: return api_error( errors=str(e), status_code=HTTPStatus.PAYMENT_REQUIRED, ) updated_channel_state = self.raiden_api.get_channel( registry_address, channel_state.token_address, channel_state.partner_state.address, ) result = self.channel_schema.dump(updated_channel_state) return api_response(result=result.data) def patch_channel( self, registry_address, token_address, partner_address, total_deposit=None, state=None, ): if total_deposit is not None and state is not None: return api_error( errors="Can not update a channel's total deposit and state at the same time", status_code=HTTPStatus.CONFLICT, ) if total_deposit is None and state is None: return api_error( errors="Nothing to do. Should either provide 'total_deposit' or 'state' argument", status_code=HTTPStatus.BAD_REQUEST, ) try: channel_state = self.raiden_api.get_channel( registry_address=registry_address, token_address=token_address, partner_address=partner_address, ) except ChannelNotFound: return api_error( errors='Requested channel for token {} and partner {} not found'.format( to_checksum_address(token_address), to_checksum_address(partner_address), ), status_code=HTTPStatus.CONFLICT, ) if total_deposit is not None: result = self._deposit(registry_address, channel_state, total_deposit) elif state == CHANNEL_STATE_CLOSED: result = self._close(registry_address, channel_state) else: # should never happen, channel_state is validated in the schema result = api_error( errors='Provided invalid channel state {}'.format(state), status_code=HTTPStatus.BAD_REQUEST, ) return result
class RestAPI(object): """ This wraps around the actual RaidenAPI in api/python. It will provide the additional, neccessary RESTful logic and the proper JSON-encoding of the Objects provided by the RaidenAPI """ version = 1 def __init__(self, raiden_api): self.raiden_api = raiden_api self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.tokens_list_schema = TokensListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() self.transfer_schema = TransferSchema() def get_our_address(self): return {'our_address': address_encoder(self.raiden_api.address)} def register_token(self, token_address): manager_address = self.raiden_api.manager_address_if_token_registered( token_address) if manager_address is not None: return make_response('Token is already registered', httplib.CONFLICT) if manager_address is None: manager_address = self.raiden_api.register_token(token_address) return jsonify_with_response(data=dict( channel_manager_address=address_encoder(manager_address)), status_code=httplib.CREATED) def open(self, partner_address, token_address, settle_timeout=None, reveal_timeout=None, balance=None): try: raiden_service_result = self.raiden_api.open( token_address, partner_address, settle_timeout, reveal_timeout, ) except (InvalidAddress, InvalidSettleTimeout, SamePeerAddress, AddressWithoutCode, NoTokenManager, DuplicatedChannelError) as e: return make_response(str(e), httplib.CONFLICT) if balance: # make initial deposit try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, balance) except EthNodeCommunicationError as e: return make_response(str(e), httplib.REQUEST_TIMEOUT) except InsufficientFunds as e: return make_response(str(e), httplib.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify_with_response(data=result.data, status_code=httplib.CREATED) def deposit(self, token_address, partner_address, amount): try: raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount) except EthNodeCommunicationError as e: return make_response(str(e), httplib.REQUEST_TIMEOUT) except InsufficientFunds as e: return make_response(str(e), httplib.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close(token_address, partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) def connect(self, token_address, funds, initial_channel_target=None, joinable_funds_target=None): self.raiden_api.connect_token_network(token_address, funds, initial_channel_target, joinable_funds_target) return make_response('', httplib.NO_CONTENT) def leave(self, token_address): self.raiden_api.leave_token_network(token_address) return make_response('', httplib.NO_CONTENT) def get_connection_manager_funds(self, token_address): connection_manager = self.raiden_api.get_connection_manager_funds( token_address) if connection_manager is None: return make_response('No connection manager exists for token', httplib.NO_CONTENT) if connection_manager is not None: return jsonify(connection_manager) def get_connection_managers_list(self): raiden_service_result = self.raiden_api.get_connection_managers_list() assert isinstance(raiden_service_result, list) new_list = [] for result in raiden_service_result: new_list.append(address_encoder(result)) return jsonify(new_list) def get_channel_list(self, token_address=None, partner_address=None): raiden_service_result = self.raiden_api.get_channel_list( token_address, partner_address) assert isinstance(raiden_service_result, list) channel_list = ChannelList(raiden_service_result) result = self.channel_list_schema.dump(channel_list) return jsonify(result.data) def get_tokens_list(self): raiden_service_result = self.raiden_api.get_tokens_list() assert isinstance(raiden_service_result, list) new_list = [] for result in raiden_service_result: new_list.append({'address': result}) tokens_list = TokensList(new_list) result = self.tokens_list_schema.dump(tokens_list) return jsonify(result.data) def get_network_events(self, from_block, to_block): raiden_service_result = self.raiden_api.get_network_events( from_block, to_block) return normalize_events_list(raiden_service_result) def get_token_network_events(self, token_address, from_block, to_block): raiden_service_result = self.raiden_api.get_token_network_events( token_address, from_block, to_block) return normalize_events_list(raiden_service_result) def get_channel_events(self, channel_address, from_block, to_block): raiden_service_result = self.raiden_api.get_channel_events( channel_address, from_block, to_block) return normalize_events_list(raiden_service_result) def get_channel(self, channel_address): channel = self.raiden_api.get_channel(channel_address) result = self.channel_schema.dump(channel_to_api_dict(channel)) return jsonify(result.data) def get_partners_by_token(self, token_address): return_list = [] raiden_service_result = self.raiden_api.get_channel_list(token_address) for result in raiden_service_result: return_list.append({ 'partner_address': result.partner_address, 'channel': url_for( # TODO: Somehow nicely parameterize this for future versions 'v1_resources.channelsresourcebychanneladdress', channel_address=result.channel_address), }) schema_list = PartnersPerTokenList(return_list) result = self.partner_per_token_list_schema.dump(schema_list) return jsonify(result.data) def initiate_transfer(self, token_address, target_address, amount, identifier): if identifier is None: identifier = create_default_identifier() try: transfer_result = self.raiden_api.transfer( token_address=token_address, target=target_address, amount=amount, identifier=identifier) except (InvalidAmount, InvalidAddress, NoPathError) as e: return make_response(str(e), httplib.CONFLICT) except (InsufficientFunds) as e: return make_response(str(e), httplib.PAYMENT_REQUIRED) if transfer_result is False: return make_response( "Payment couldn't be completed " "(insufficient funds or no route to target).", httplib.CONFLICT) transfer = { 'initiator_address': self.raiden_api.raiden.address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } result = self.transfer_schema.dump(transfer) return jsonify(result.data) def patch_channel(self, channel_address, balance=None, state=None): if balance is not None and state is not None: return make_response( 'Can not update balance and change channel state at the same time', httplib.CONFLICT, ) elif balance is None and state is None: return make_response( 'Nothing to do. Should either provide \'balance\' or \'state\' argument', httplib.BAD_REQUEST, ) # find the channel try: channel = self.raiden_api.get_channel(channel_address) except ChannelNotFound: return make_response( "Requested channel {} not found".format( address_encoder(channel_address)), httplib.CONFLICT) current_state = channel.state # if we patch with `balance` it's a deposit if balance is not None: if current_state != CHANNEL_STATE_OPENED: return make_response( "Can't deposit on a closed channel", httplib.CONFLICT, ) try: raiden_service_result = self.raiden_api.deposit( channel.token_address, channel.partner_address, balance) except InsufficientFunds as e: return make_response(str(e), httplib.PAYMENT_REQUIRED) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) if state == CHANNEL_STATE_CLOSED: if current_state != CHANNEL_STATE_OPENED: return make_response( 'Attempted to close an already closed channel', httplib.CONFLICT, ) raiden_service_result = self.raiden_api.close( channel.token_address, channel.partner_address) result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) if state == CHANNEL_STATE_SETTLED: if current_state == CHANNEL_STATE_SETTLED or current_state == CHANNEL_STATE_OPENED: return make_response( 'Attempted to settle a channel at its {} state'.format( current_state), httplib.CONFLICT, ) try: raiden_service_result = self.raiden_api.settle( channel.token_address, channel.partner_address) except InvalidState: result = make_response( 'Settlement period is not yet over', httplib.CONFLICT, ) else: result = self.channel_schema.dump( channel_to_api_dict(raiden_service_result)) return jsonify(result.data) # should never happen, channel_state is validated in the schema return make_response( 'Provided invalid channel state {}'.format(state), httplib.BAD_REQUEST, ) def token_swap(self, target_address, identifier, role, sending_token, sending_amount, receiving_token, receiving_amount): if role == 'maker': self.raiden_api.token_swap( identifier=identifier, maker_token=sending_token, maker_amount=sending_amount, maker_address=self.raiden_api.address, taker_token=receiving_token, taker_amount=receiving_amount, taker_address=target_address, ) elif role == 'taker': self.raiden_api.expect_token_swap( identifier=identifier, maker_token=receiving_token, maker_amount=receiving_amount, maker_address=target_address, taker_token=sending_token, taker_amount=sending_amount, taker_address=self.raiden_api.address) else: # should never happen, role is validated in the schema return make_response( 'Provided invalid token swap role {}'.format(role), httplib.BAD_REQUEST, ) return jsonify_with_response(dict(), httplib.CREATED)