class RestAPI(object): """ This wraps around the actual RaidenAPI in raiden_service. 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.dumps( channel_to_api_dict(raiden_service_result)) return result def deposit(self, token_address, partner_address, amount): raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount) result = self.channel_schema.dumps( channel_to_api_dict(raiden_service_result)) return result def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close(token_address, partner_address) result = self.channel_schema.dumps( channel_to_api_dict(raiden_service_result)) return 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.dumps(channel_list) return result 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.dumps(tokens_list) return result 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) return self.channel_schema.dumps(channel_to_api_dict(channel)) 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.dumps(schema_list) return result 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) transfer = { 'initiator_address': self.raiden_api.raiden.address, 'token_address': token_address, 'target_address': target_address, 'amount': amount, 'identifier': identifier, } return self.transfer_schema.dumps(transfer) except (InvalidAmount, InvalidAddress, NoPathError) as e: return make_response(str(e), httplib.CONFLICT) 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) return self.channel_schema.dumps( channel_to_api_dict(raiden_service_result)) else: 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) return self.channel_schema.dumps( channel_to_api_dict(raiden_service_result)) elif 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) return self.channel_schema.dumps( channel_to_api_dict(raiden_service_result)) else: # 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 ApiTestContext(): def __init__(self, reveal_timeout): self.events = list() self.channels = [] self.tokens = set() self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.reveal_timeout = reveal_timeout self.tokens_to_manager_address = dict() self.connection_managers = [] def add_events(self, events): self.events += events def specify_token_for_channelnew(self, token_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the token address properly makes it through the REST api""" self.token_for_channelnew = address_decoder(token_address) def specify_channel_for_events(self, channel_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the channel address properly makes it through the REST api""" self.channel_for_events = address_decoder(channel_address) def specify_tokenswap_input(self, tokenswap_input, target_address, identifier): """We don't test the actual tokenswap but only that the input makes it to the backend in the expected format""" self.tokenswap_input = dict(tokenswap_input) self.tokenswap_input['sending_token'] = address_decoder( self.tokenswap_input['sending_token'] ) self.tokenswap_input['receiving_token'] = address_decoder( self.tokenswap_input['receiving_token'] ) self.tokenswap_input['identifier'] = identifier self.tokenswap_input['target_address'] = address_decoder(target_address) def get_network_events(self, from_block, to_block): return_list = list() for event in self.events: expected_event_type = event['_event_type'] == 'TokenAdded' in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_token_network_events(self, token_address, from_block, to_block): return_list = list() if token_address != self.token_for_channelnew: raise ValueError( 'Unexpected token address: "{}" during channelnew ' 'query'.format(pex(token_address)) ) for event in self.events: expected_event_type = event['_event_type'] == 'ChannelNew' in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_channel_events(self, channel_address, from_block, to_block): return_list = list() if channel_address != self.channel_for_events: raise ValueError( 'Unexpected channel address: "{}" during channel events ' 'query'.format(pex(channel_address)) ) for event in self.events: is_channel_event = ( event['_event_type'] == 'ChannelNewBalance' or event['_event_type'] == 'ChannelClosed' or event['_event_type'] == 'TransferUpdated' or event['_event_type'] == 'ChannelSettled' or event['_event_type'] == 'ChannelSecretRevealed' ) in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if is_channel_event and in_block_range: return_list.append(event) return return_list def make_channel( self, token_address=make_address(), partner_address=make_address(), reveal_timeout=20, settle_timeout=800, balance=0, block_number=1): our_address = make_address() our_balance = balance partner_balance = balance our_state = ChannelEndState( our_address, our_balance, None, EMPTY_MERKLE_TREE, ) partner_state = ChannelEndState( partner_address, partner_balance, None, EMPTY_MERKLE_TREE, ) channel_for_hashlock = list() netting_channel = NettingChannelMock(make_address()) external_state = ChannelExternalState( lambda *args: channel_for_hashlock.append(args), netting_channel, ) self.tokens.add(token_address) return Channel( our_state, partner_state, external_state, token_address, reveal_timeout, settle_timeout, ) def register_token(self, token_address): self.tokens.add(token_address) manager_address = make_address() self.tokens_to_manager_address[token_address] = manager_address return manager_address def manager_address_if_token_registered(self, token_address): if token_address not in self.tokens: return None return self.tokens_to_manager_address[token_address] def make_channel_and_add(self): channel = self.make_channel() self.channels.append(channel) def find_channel(self, token_address, partner_address): for channel in self.channels: if (channel.token_address == token_address and channel.partner_state.address == partner_address): return channel raise ValueError("Could not find channel") def find_channel_by_address(self, channel_address): for channel in self.channels: if channel.channel_address == channel_address: return channel raise ValueError("Could not find channel") def query_channels(self, token_address=None, partner_address=None): if not token_address: return self.channels new_list = [] for channel in self.channels: if channel.token_address == token_address: new_list.append(channel) return new_list def query_tokens(self): return list(self.tokens) def expect_channels(self): channel_list = ChannelList(self.channels) return json.loads(self.channel_list_schema.dumps(channel_list).data) def open_channel( self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): invalid_timeout = ( settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX ) if invalid_timeout: raise InvalidSettleTimeout('`settle_timeout` should be in range [{}, {}].'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX )) if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel open') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel open') reveal_value = reveal_timeout if reveal_timeout is not None else self.reveal_timeout channel = self.make_channel( token_address=token_address, partner_address=partner_address, settle_timeout=settle_timeout, reveal_timeout=reveal_value ) self.channels.append(channel) return channel def deposit(self, token_address, partner_address, amount): channel = self.find_channel(token_address, partner_address) channel.our_state.contract_balance += amount return channel def close(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = CHANNEL_STATE_CLOSED channel.external_state._closed_block = 1 return channel def settle(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = CHANNEL_STATE_SETTLED channel.external_state._settled_block = 1 return channel def get_channel(self, channel_address): return self.find_channel_by_address(channel_address) def _check_tokenswap_input(self, argname, input_argname, kwargs): if kwargs[argname] != self.tokenswap_input[input_argname]: raise ValueError( 'Expected "{}" for {} but got "{}"'.format( self.tokenswap_input[input_argname], input_argname, kwargs[argname]) ) def token_swap(self, **kwargs): self._check_tokenswap_input('maker_token', 'sending_token', kwargs) self._check_tokenswap_input('maker_amount', 'sending_amount', kwargs) self._check_tokenswap_input('taker_token', 'receiving_token', kwargs) self._check_tokenswap_input('taker_amount', 'receiving_amount', kwargs) def expect_token_swap(self, **kwargs): self._check_tokenswap_input('identifier', 'identifier', kwargs) self._check_tokenswap_input('maker_token', 'receiving_token', kwargs) self._check_tokenswap_input('maker_amount', 'receiving_amount', kwargs) self._check_tokenswap_input('taker_token', 'sending_token', kwargs) self._check_tokenswap_input('taker_amount', 'sending_amount', kwargs) def transfer(self, token_address, amount, target, identifier): # Do nothing. These tests only test the api endpoints, so nothing to do here pass def connect( self, token_address, funds, initial_channel_target, joinable_funds_target): if not isaddress(token_address): raise InvalidAddress('not an address %s' % pex(token_address)) funding = int((funds * joinable_funds_target) / initial_channel_target) for i in range(0, initial_channel_target): channel = self.make_channel(token_address=token_address, balance=funding) self.channels.append(channel) connection_manager = ConnectionManagerMock(token_address, funds) self.connection_managers.append(connection_manager) def leave(self, token_address, only_receiving): if not isaddress(token_address): raise InvalidAddress('not an address %s' % pex(token_address)) # for the mocking purposes let's just ignore only_receiving_ but keep its # value in the mock context for checking self.last_only_receiving = only_receiving channels = self.get_all_channels_for_token(token_address) for channel in channels: channel.external_state.netting_channel.state = CHANNEL_STATE_SETTLED channel.external_state._settled_block = 1 return channels def get_connection_managers_info(self): token_addresses = {} for connection_manager in self.connection_managers: token_addresses[connection_manager.token_address] = { "funds": connection_manager.funds, "sum_deposits": connection_manager.sum_deposits, "channels": len(connection_manager.open_channels) } return token_addresses def get_all_channels_for_token(self, token_address): channels = [channel for channel in self.channels if channel.token_address == token_address] if len(channels) > 0: return channels else: raise ValueError("Could not find channel")
class ApiTestContext(): def __init__(self, reveal_timeout): self.events = list() self.channels = [] self.tokens = set() self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.reveal_timeout = reveal_timeout def add_events(self, events): self.events += events def specify_token_for_channelnew(self, token_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the token address properly makes it through the REST api""" self.token_for_channelnew = address_decoder(token_address) def specify_channel_for_events(self, channel_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the channel address properly makes it through the REST api""" self.channel_for_events = address_decoder(channel_address) def specify_tokenswap_input(self, tokenswap_input, target_address, identifier): """We don't test the actual tokenswap but only that the input makes it to the backend in the expected format""" self.tokenswap_input = dict(tokenswap_input) self.tokenswap_input['sending_token'] = address_decoder( self.tokenswap_input['sending_token']) self.tokenswap_input['receiving_token'] = address_decoder( self.tokenswap_input['receiving_token']) self.tokenswap_input['identifier'] = identifier self.tokenswap_input['target_address'] = address_decoder( target_address) def get_network_events(self, from_block, to_block): return_list = list() for event in self.events: expected_event_type = event['_event_type'] == 'TokenAdded' in_block_range = (event['block_number'] >= from_block and event['block_number'] <= to_block) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_token_network_events(self, token_address, from_block, to_block): return_list = list() if token_address != self.token_for_channelnew: raise ValueError( 'Unexpected token address: "{}" during channelnew ' 'query'.format(token_address)) for event in self.events: expected_event_type = event['_event_type'] == 'ChannelNew' in_block_range = (event['block_number'] >= from_block and event['block_number'] <= to_block) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_channel_events(self, channel_address, from_block, to_block): return_list = list() if channel_address != self.channel_for_events: raise ValueError( 'Unexpected channel address: "{}" during channel events ' 'query'.format(channel_address)) for event in self.events: is_channel_event = (event['_event_type'] == 'ChannelNewBalance' or event['_event_type'] == 'ChannelClosed' or event['_event_type'] == 'TransferUpdated' or event['_event_type'] == 'ChannelSettled' or event['_event_type'] == 'ChannelSecretRevealed') in_block_range = (event['block_number'] >= from_block and event['block_number'] <= to_block) if is_channel_event and in_block_range: return_list.append(event) return return_list def make_channel( self, token_address=make_address(), partner_address=make_address(), reveal_timeout=20, settle_timeout=800, balance=0, ): our_address = make_address() our_balance = balance partner_balance = balance our_state = ChannelEndState(our_address, our_balance, 1) partner_state = ChannelEndState(partner_address, partner_balance, 1) block_alarm = list() channel_for_hashlock = list() netting_channel = NettingChannelMock(make_address()) external_state = ChannelExternalState( block_alarm.append, lambda *args: channel_for_hashlock.append(args), lambda: 1, netting_channel, ) self.tokens.add(token_address) return Channel( our_state, partner_state, external_state, token_address, reveal_timeout, settle_timeout, ) def make_channel_and_add(self): channel = self.make_channel() self.channels.append(channel) def find_channel(self, token_address, partner_address): for channel in self.channels: if (channel.token_address == token_address and channel.partner_state.address == partner_address): return channel raise ValueError("Could not find channel") def find_channel_by_address(self, channel_address): for channel in self.channels: if channel.channel_address == channel_address: return channel raise ValueError("Could not find channel") def query_channels(self, token_address=None, partner_address=None): if not token_address: return self.channels new_list = [] for channel in self.channels: if channel.token_address == token_address: new_list.append(channel) return new_list def query_tokens(self): return list(self.tokens) def expect_channels(self): channel_list = ChannelList(self.channels) return json.loads(self.channel_list_schema.dumps(channel_list).data) def open_channel(self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): reveal_value = reveal_timeout if reveal_timeout is not None else self.reveal_timeout channel = self.make_channel(token_address=token_address, partner_address=partner_address, settle_timeout=settle_timeout, reveal_timeout=reveal_value) self.channels.append(channel) return channel def deposit(self, token_address, partner_address, amount): channel = self.find_channel(token_address, partner_address) channel.our_state.contract_balance += amount return channel def close(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = 'closed' channel.external_state._closed_block = 1 return channel def settle(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = 'settled' channel.external_state._settled_block = 1 return channel def get_channel(self, channel_address): return self.find_channel_by_address(channel_address) def _check_tokenswap_input(self, argname, input_argname, kwargs): if kwargs[argname] != self.tokenswap_input[input_argname]: raise ValueError('Expected "{}" for {} but got "{}"'.format( self.tokenswap_input[input_argname], input_argname, kwargs[argname])) def exchange(self, **kwargs): self._check_tokenswap_input('from_token', 'sending_token', kwargs) self._check_tokenswap_input('from_amount', 'sending_amount', kwargs) self._check_tokenswap_input('to_token', 'receiving_token', kwargs) self._check_tokenswap_input('to_amount', 'receiving_amount', kwargs) def expect_exchange(self, **kwargs): self._check_tokenswap_input('identifier', 'identifier', kwargs) self._check_tokenswap_input('from_token', 'sending_token', kwargs) self._check_tokenswap_input('from_amount', 'sending_amount', kwargs) self._check_tokenswap_input('to_token', 'receiving_token', kwargs) self._check_tokenswap_input('to_amount', 'receiving_amount', kwargs) def transfer(self, token_address, amount, target, identifier): # Do nothing. These tests only test the api endpoints, so nothing to do here pass
class ApiTestContext(): def __init__(self, reveal_timeout): self.channels = [] self.tokens = set() self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.events_list_schema = EventsListSchema() self.reveal_timeout = reveal_timeout def make_channel( self, token_address=make_address(), partner_address=make_address(), reveal_timeout=20, settle_timeout=800, balance=0, ): our_address = make_address() our_balance = balance partner_balance = balance our_state = ChannelEndState(our_address, our_balance, 1) partner_state = ChannelEndState(partner_address, partner_balance, 1) block_alarm = list() channel_for_hashlock = list() netting_channel = NettingChannelMock(make_address()) external_state = ChannelExternalState( block_alarm.append, lambda *args: channel_for_hashlock.append(args), lambda: 1, netting_channel, ) self.tokens.add(token_address) return Channel( our_state, partner_state, external_state, token_address, reveal_timeout, settle_timeout, ) def make_channel_and_add(self): channel = self.make_channel() self.channels.append(channel) def find_channel(self, token_address, partner_address): for channel in self.channels: if (channel.token_address == token_address and channel.partner_state.address == partner_address): return channel raise ValueError("Could not find channel") def find_channel_by_address(self, channel_address): for channel in self.channels: if channel.channel_address == channel_address: return channel raise ValueError("Could not find channel") def query_channels(self, token_address=None, partner_address=None): if not token_address: return self.channels new_list = [] for channel in self.channels: if channel.token_address == token_address: new_list.append(channel) return new_list def query_tokens(self): return list(self.tokens) def expect_channels(self): channel_list = ChannelList(self.channels) return json.loads(self.channel_list_schema.dumps(channel_list).data) def open_channel( self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): reveal_value = reveal_timeout if reveal_timeout is not None else self.reveal_timeout channel = self.make_channel( token_address=token_address, partner_address=partner_address, settle_timeout=settle_timeout, reveal_timeout=reveal_value ) self.channels.append(channel) return channel def deposit(self, token_address, partner_address, amount): channel = self.find_channel(token_address, partner_address) channel.our_state.contract_balance += amount return channel def close(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = 'closed' channel.external_state._closed_block = 1 return channel def settle(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = 'settled' channel.external_state._settled_block = 1 return channel def get_channel(self, channel_address): return self.find_channel_by_address(channel_address)
class ApiTestContext: def __init__(self, reveal_timeout): self.events = list() self.channels = [] self.tokens = set() self.channel_schema = ChannelSchema() self.channel_list_schema = ChannelListSchema() self.reveal_timeout = reveal_timeout self.tokens_to_manager_address = dict() self.connection_managers = [] def add_events(self, events): self.events += events def specify_token_for_channelnew(self, token_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the token address properly makes it through the REST api""" self.token_for_channelnew = address_decoder(token_address) def specify_channel_for_events(self, channel_address): """Since it's not part of the event but part of the querying and we mock the interface we should check that the channel address properly makes it through the REST api""" self.channel_for_events = address_decoder(channel_address) def specify_tokenswap_input(self, tokenswap_input, target_address, identifier): """We don't test the actual tokenswap but only that the input makes it to the backend in the expected format""" self.tokenswap_input = dict(tokenswap_input) self.tokenswap_input['sending_token'] = address_decoder( self.tokenswap_input['sending_token'] ) self.tokenswap_input['receiving_token'] = address_decoder( self.tokenswap_input['receiving_token'] ) self.tokenswap_input['identifier'] = identifier self.tokenswap_input['target_address'] = address_decoder(target_address) def get_network_events(self, from_block, to_block): return_list = list() for event in self.events: expected_event_type = event['_event_type'] == 'TokenAdded' in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_token_network_events(self, token_address, from_block, to_block): return_list = list() if token_address != self.token_for_channelnew: raise ValueError( 'Unexpected token address: "{}" during channelnew ' 'query'.format(pex(token_address)) ) for event in self.events: expected_event_type = event['_event_type'] == 'ChannelNew' in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if expected_event_type and in_block_range: return_list.append(event) return return_list def get_channel_events(self, channel_address, from_block, to_block): return_list = list() if channel_address != self.channel_for_events: raise ValueError( 'Unexpected channel address: "{}" during channel events ' 'query'.format(pex(channel_address)) ) for event in self.events: is_channel_event = ( event['_event_type'] == 'ChannelNewBalance' or event['_event_type'] == 'ChannelClosed' or event['_event_type'] == 'TransferUpdated' or event['_event_type'] == 'ChannelSettled' or event['_event_type'] == 'ChannelSecretRevealed' ) in_block_range = ( event['block_number'] >= from_block and event['block_number'] <= to_block ) if is_channel_event and in_block_range: return_list.append(event) return return_list def make_channel( self, token_address=make_address(), partner_address=make_address(), reveal_timeout=20, settle_timeout=800, balance=0, block_number=1): our_address = make_address() our_balance = balance partner_balance = balance our_state = ChannelEndState( our_address, our_balance, None, EMPTY_MERKLE_TREE, ) partner_state = ChannelEndState( partner_address, partner_balance, None, EMPTY_MERKLE_TREE, ) channel_for_hashlock = list() netting_channel = NettingChannelMock(make_address()) external_state = ChannelExternalState( lambda *args: channel_for_hashlock.append(args), netting_channel, ) self.tokens.add(token_address) return Channel( our_state, partner_state, external_state, token_address, reveal_timeout, settle_timeout, ) def register_token(self, token_address): self.tokens.add(token_address) manager_address = make_address() self.tokens_to_manager_address[token_address] = manager_address return manager_address def manager_address_if_token_registered(self, token_address): if token_address not in self.tokens: return None return self.tokens_to_manager_address[token_address] def make_channel_and_add(self): channel = self.make_channel() self.channels.append(channel) def find_channel(self, token_address, partner_address): for channel in self.channels: if (channel.token_address == token_address and channel.partner_state.address == partner_address): return channel raise ValueError('Could not find channel') def find_channel_by_address(self, channel_address): for channel in self.channels: if channel.channel_address == channel_address: return channel raise ValueError('Could not find channel') def query_channels(self, token_address=None, partner_address=None): if not token_address: return self.channels new_list = [] for channel in self.channels: if channel.token_address == token_address: new_list.append(channel) return new_list def query_tokens(self): return list(self.tokens) def expect_channels(self): channel_list = ChannelList(self.channels) return json.loads(self.channel_list_schema.dumps(channel_list).data) def open_channel( self, token_address, partner_address, settle_timeout=None, reveal_timeout=None): invalid_timeout = ( settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX ) if invalid_timeout: raise InvalidSettleTimeout('`settle_timeout` should be in range [{}, {}].'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX )) if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token in channel open') if not isaddress(partner_address): raise InvalidAddress('Expected binary address format for partner in channel open') reveal_value = reveal_timeout if reveal_timeout is not None else self.reveal_timeout channel = self.make_channel( token_address=token_address, partner_address=partner_address, settle_timeout=settle_timeout, reveal_timeout=reveal_value ) self.channels.append(channel) return channel def deposit(self, token_address, partner_address, amount): channel = self.find_channel(token_address, partner_address) channel.our_state.contract_balance += amount return channel def close(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = CHANNEL_STATE_CLOSED channel.external_state._closed_block = 1 return channel def settle(self, token_address, partner_address): channel = self.find_channel(token_address, partner_address) channel.external_state.netting_channel.state = CHANNEL_STATE_SETTLED channel.external_state._settled_block = 1 return channel def get_channel(self, channel_address): return self.find_channel_by_address(channel_address) def _check_tokenswap_input(self, argname, input_argname, kwargs): if kwargs[argname] != self.tokenswap_input[input_argname]: raise ValueError( 'Expected "{}" for {} but got "{}"'.format( self.tokenswap_input[input_argname], input_argname, kwargs[argname]) ) def token_swap(self, **kwargs): self._check_tokenswap_input('maker_token', 'sending_token', kwargs) self._check_tokenswap_input('maker_amount', 'sending_amount', kwargs) self._check_tokenswap_input('taker_token', 'receiving_token', kwargs) self._check_tokenswap_input('taker_amount', 'receiving_amount', kwargs) def expect_token_swap(self, **kwargs): self._check_tokenswap_input('identifier', 'identifier', kwargs) self._check_tokenswap_input('maker_token', 'receiving_token', kwargs) self._check_tokenswap_input('maker_amount', 'receiving_amount', kwargs) self._check_tokenswap_input('taker_token', 'sending_token', kwargs) self._check_tokenswap_input('taker_amount', 'sending_amount', kwargs) def transfer(self, token_address, amount, target, identifier): # Do nothing. These tests only test the api endpoints, so nothing to do here pass def connect( self, token_address, funds, initial_channel_target, joinable_funds_target): if not isaddress(token_address): raise InvalidAddress('not an address %s' % pex(token_address)) funding = int((funds * joinable_funds_target) / initial_channel_target) for i in range(0, initial_channel_target): channel = self.make_channel(token_address=token_address, balance=funding) self.channels.append(channel) connection_manager = ConnectionManagerMock(token_address, funds) self.connection_managers.append(connection_manager) def leave(self, token_address, only_receiving): if not isaddress(token_address): raise InvalidAddress('not an address %s' % pex(token_address)) # for the mocking purposes let's just ignore only_receiving_ but keep its # value in the mock context for checking self.last_only_receiving = only_receiving channels = self.get_all_channels_for_token(token_address) for channel in channels: channel.external_state.netting_channel.state = CHANNEL_STATE_SETTLED channel.external_state._settled_block = 1 return channels def get_connection_managers_info(self): token_addresses = {} for connection_manager in self.connection_managers: token_addresses[connection_manager.token_address] = { 'funds': connection_manager.funds, 'sum_deposits': connection_manager.sum_deposits, 'channels': len(connection_manager.open_channels), } return token_addresses def get_all_channels_for_token(self, token_address): channels = [channel for channel in self.channels if channel.token_address == token_address] if len(channels) > 0: return channels else: raise ValueError('Could not find channel')
class RestAPI(object): """ This wraps around the actual RaidenAPI in raiden_service. 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.events_list_schema = EventsListSchema() self.tokens_list_schema = TokensListSchema() self.partner_per_token_list_schema = PartnersPerTokenListSchema() 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.dumps(raiden_service_result) return result def deposit(self, token_address, partner_address, amount): raiden_service_result = self.raiden_api.deposit( token_address, partner_address, amount ) result = self.channel_schema.dumps(raiden_service_result) return result def close(self, token_address, partner_address): raiden_service_result = self.raiden_api.close( token_address, partner_address ) result = self.channel_schema.dumps(raiden_service_result) return 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.dumps(channel_list) return result 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.dumps(tokens_list) return result def get_new_events(self): raiden_service_result = self.get_new_events() assert isinstance(raiden_service_result, list) events_list = EventsList(raiden_service_result) result = self.events_list_schema.dumps(events_list) return result def get_channel(self, channel_address): channel = self.raiden_api.get_channel(channel_address) return self.channel_schema.dumps(channel) 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.dumps(schema_list) return result 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 != 'open': 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 ) return self.channel_schema.dumps(raiden_service_result) else: if state == 'closed': if current_state != 'open': 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 ) return self.channel_schema.dumps(raiden_service_result) elif state == 'settled': if current_state == 'settled' or current_state == 'open': 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 ) return self.channel_schema.dumps(raiden_service_result) else: return make_response( 'Provided invalid channel state {}'.format(state), httplib.BAD_REQUEST, )