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(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)