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=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: """ 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 = 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(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)