Exemple #1
0
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")
Exemple #3
0
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
Exemple #4
0
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')
Exemple #6
0
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,
                )