def _execute(self, request):
        inclusion_states = request['inclusionStates']  # type: bool
        seed = request['seed']  # type: Seed
        start = request['start']  # type: int
        stop = request['stop']  # type: Optional[int]

        # Determine the addresses we will be scanning, and pull their
        # transaction hashes.
        if stop is None:
            my_hashes = list(
                chain(*(hashes for _, hashes in iter_used_addresses(
                    self.adapter, seed, start))))
        else:
            ft_response =\
              FindTransactionsCommand(self.adapter)(
                addresses =
                  AddressGenerator(seed).get_addresses(start, stop - start),
              )

            my_hashes = ft_response['hashes']

        return {
            'bundles':
            get_bundles_from_transaction_hashes(
                adapter=self.adapter,
                transaction_hashes=my_hashes,
                inclusion_states=inclusion_states,
            ),
        }
class FindTransactionsResponseFilterTestCase(BaseFilterTestCase):
    filter_type = FindTransactionsCommand(MockAdapter()).get_response_filter
    skip_value_check = True

    # noinspection SpellCheckingInspection
    def setUp(self):
        super(FindTransactionsResponseFilterTestCase, self).setUp()

        # Define a few valid values here that we can reuse across multiple
        # tests.
        self.trytes1 = b'RBTC9D9DCDQAEASBYBCCKBFA'
        self.trytes2 =\
          b'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA'

    def test_no_results(self):
        """
    The incoming response contains no hashes.
    """
        response = {
            'hashes': [],
            'duration': 42,
        }

        filter_ = self._filter(response)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(filter_.cleaned_data, response)

    # noinspection SpellCheckingInspection
    def test_search_results(self):
        """
    The incoming response contains lots of hashes.
    """
        filter_ = self._filter({
            'hashes': [
                'RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFW'
                'YWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVA',
                'ZJVYUGTDRPDYFGFXMKOTV9ZWSGFK9CFPXTITQLQN'
                'LPPG9YNAARMKNKYQO9GSCSBIOTGMLJUFLZWSY9999',
            ],
            'duration':
            42,
        })

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'hashes': [
                    TransactionHash(
                        b'RVORZ9SIIP9RCYMREUIXXVPQIPHVCNPQ9HZWYKFW'
                        b'YWZRE9JQKG9REPKIASHUUECPSQO9JT9XNMVKWYGVA', ),
                    TransactionHash(
                        b'ZJVYUGTDRPDYFGFXMKOTV9ZWSGFK9CFPXTITQLQN'
                        b'LPPG9YNAARMKNKYQO9GSCSBIOTGMLJUFLZWSY9999', ),
                ],
                'duration':
                42,
            },
        )
    def _find_addresses(self, seed, index, count):
        """
    Find addresses matching the command parameters.
    """
        # type: (Seed, int, Optional[int]) -> List[Address]
        generator = AddressGenerator(seed)

        if count is None:
            # Connect to Tangle and find the first address without any
            # transactions.
            for addy in generator.create_iterator(start=index):
                response = FindTransactionsCommand(
                    self.adapter)(addresses=[addy])

                if not response.get('hashes'):
                    return [addy]

        return generator.get_addresses(start=index, count=count)
Beispiel #4
0
def iter_used_addresses(adapter, seed, start):
    # type: (BaseAdapter, Seed, int) -> Generator[Tuple[Address, List[TransactionHash]]]
    """
  Scans the Tangle for used addresses.

  This is basically the opposite of invoking ``getNewAddresses`` with
  ``stop=None``.
  """
    ft_command = FindTransactionsCommand(adapter)

    for addy in AddressGenerator(seed).create_iterator(start):
        ft_response = ft_command(addresses=[addy])

        if ft_response['hashes']:
            yield addy, ft_response['hashes']
        else:
            break

        # Reset the command so that we can call it again.
        ft_command.reset()
    def _execute(self, request):
        inclusion_states = request['inclusionStates']  # type: bool
        seed = request['seed']  # type: Seed
        start = request['start']  # type: int
        stop = request['stop']  # type: Optional[int]

        if stop is None:
            my_addresses = []  # type: List[Address]
            my_hashes = []  # type: List[TransactionHash]

            for addy, hashes in iter_used_addresses(self.adapter, seed, start):
                my_addresses.append(addy)
                my_hashes.extend(hashes)
        else:
            ft_command = FindTransactionsCommand(self.adapter)

            my_addresses = AddressGenerator(seed).get_addresses(
                start, stop - start)
            my_hashes = ft_command(addresses=my_addresses).get('hashes') or []

        account_balance = 0
        if my_hashes:
            # Load balances for the addresses that we generated.
            gb_response = GetBalancesCommand(
                self.adapter)(addresses=my_addresses)

            for i, balance in enumerate(gb_response['balances']):
                my_addresses[i].balance = balance
                account_balance += balance

        return {
            'addresses':
            list(sorted(my_addresses, key=attrgetter('key_index'))),
            'balance':
            account_balance,
            'bundles':
            get_bundles_from_transaction_hashes(
                adapter=self.adapter,
                transaction_hashes=my_hashes,
                inclusion_states=inclusion_states,
            ),
        }
Beispiel #6
0
def find_transaction_objects(adapter, **kwargs):
    # type: (BaseAdapter, dict) -> List[Transaction]
    """
    Finds transactions matching the specified criteria, fetches the
    corresponding trytes and converts them into Transaction objects.
    """
    ft_response = FindTransactionsCommand(adapter)(**kwargs)

    hashes = ft_response['hashes']

    if hashes:
        gt_response = GetTrytesCommand(adapter)(hashes=hashes)

        return list(
            map(
                Transaction.from_tryte_string,
                gt_response.get('trytes') or [],
            ))  # type: List[Transaction]

    return []
class FindTransactionsRequestFilterTestCase(BaseFilterTestCase):
    filter_type = FindTransactionsCommand(MockAdapter()).get_request_filter
    skip_value_check = True

    # noinspection SpellCheckingInspection
    def setUp(self):
        super(FindTransactionsRequestFilterTestCase, self).setUp()

        # Define a few valid values that we can reuse across tests.
        self.trytes1 = b'RBTC9D9DCDQAEASBYBCCKBFA'
        self.trytes2 =\
          b'CCPCBDVC9DTCEAKDXC9D9DEARCWCPCBDVCTCEAHDWCTCEAKDCDFD9DSCSA'
        self.trytes3 = b'999999999999999999999999999'

    def test_pass_all_parameters(self):
        """
    The request contains valid values for all parameters.
    """
        request = {
            'bundles': [
                TransactionHash(self.trytes1),
                TransactionHash(self.trytes2),
            ],
            'addresses': [
                Address(self.trytes1),
                Address(self.trytes2),
            ],
            'tags': [
                Tag(self.trytes1),
                Tag(self.trytes3),
            ],
            'approvees': [
                TransactionHash(self.trytes1),
                TransactionHash(self.trytes3),
            ],
        }

        filter_ = self._filter(request)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(filter_.cleaned_data, request)

    def test_pass_compatible_types(self):
        """
    The request contains values that can be converted to the expected
    types.
    """
        filter_ = self._filter({
            'bundles': [
                binary_type(self.trytes1),
                bytearray(self.trytes2),
            ],
            'addresses': [
                binary_type(self.trytes1),
                bytearray(self.trytes2),
            ],
            'tags': [
                binary_type(self.trytes1),
                bytearray(self.trytes3),
            ],
            'approvees': [
                binary_type(self.trytes1),
                bytearray(self.trytes3),
            ],
        })

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'bundles': [
                    TransactionHash(self.trytes1),
                    TransactionHash(self.trytes2),
                ],
                'addresses': [
                    Address(self.trytes1),
                    Address(self.trytes2),
                ],
                'tags': [
                    Tag(self.trytes1),
                    Tag(self.trytes3),
                ],
                'approvees': [
                    TransactionHash(self.trytes1),
                    TransactionHash(self.trytes3),
                ],
            },
        )

    def test_pass_bundles_only(self):
        """
    The request only includes bundles.
    """
        request = {
            'bundles': [
                TransactionHash(self.trytes1),
                TransactionHash(self.trytes2),
            ],
        }

        filter_ = self._filter(request)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'bundles': [
                    TransactionHash(self.trytes1),
                    TransactionHash(self.trytes2),
                ],
                'addresses': [],
                'approvees': [],
                'tags': [],
            },
        )

    def test_pass_addresses_only(self):
        """
    The request only includes addresses.
    """
        request = {
            'addresses': [
                Address(self.trytes1),
                Address(self.trytes2),
            ],
        }

        filter_ = self._filter(request)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'addresses': [
                    Address(self.trytes1),
                    Address(self.trytes2),
                ],
                'approvees': [],
                'bundles': [],
                'tags': [],
            },
        )

    def test_pass_tags_only(self):
        """
    The request only includes tags.
    """
        request = {
            'tags': [
                Tag(self.trytes1),
                Tag(self.trytes3),
            ],
        }

        filter_ = self._filter(request)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'tags': [
                    Tag(self.trytes1),
                    Tag(self.trytes3),
                ],
                'addresses': [],
                'approvees': [],
                'bundles': [],
            },
        )

    def test_pass_approvees_only(self):
        """
    The request only includes approvees.
    """
        request = {
            'approvees': [
                TransactionHash(self.trytes1),
                TransactionHash(self.trytes3),
            ],
        }

        filter_ = self._filter(request)

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'approvees': [
                    TransactionHash(self.trytes1),
                    TransactionHash(self.trytes3),
                ],
                'addresses': [],
                'bundles': [],
                'tags': [],
            },
        )

    def test_fail_empty(self):
        """
    The request does not contain any parameters.
    """
        self.assertFilterErrors(
            {},
            {
                '': [FindTransactionsRequestFilter.CODE_NO_SEARCH_VALUES],
            },
        )

    def test_fail_all_parameters_empty(self):
        """
    The request contains all parameters, but every one is empty.
    """
        self.assertFilterErrors(
            {
                'addresses': [],
                'approvees': [],
                'bundles': [],
                'tags': [],
            },
            {
                '': [FindTransactionsRequestFilter.CODE_NO_SEARCH_VALUES],
            },
        )

    def test_fail_unexpected_parameters(self):
        """
    The request contains unexpected parameters.
    """
        self.assertFilterErrors(
            {
                'addresses': [Address(self.trytes1)],
                'approvees': [TransactionHash(self.trytes1)],
                'bundles': [TransactionHash(self.trytes1)],
                'tags': [Tag(self.trytes1)],

                # Hey, you're not allowed in he-argh!
                'foo': 'bar',
            },
            {
                'foo': [f.FilterMapper.CODE_EXTRA_KEY],
            },
        )

    def test_fail_bundles_wrong_type(self):
        """
    ``bundles`` is not an array.
    """
        self.assertFilterErrors(
            {
                'bundles': TransactionHash(self.trytes1),
            },
            {
                'bundles': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_bundles_contents_invalid(self):
        """
    ``bundles`` is an array, but it contains invalid values.
    """
        self.assertFilterErrors(
            {
                'bundles': [
                    b'',
                    text_type(self.trytes1, 'ascii'),
                    True,
                    None,
                    b'not valid trytes',

                    # This is actually valid; I just added it to make sure the
                    # filter isn't cheating!
                    TryteString(self.trytes2),
                    2130706433,
                    b'9' * 82,
                ],
            },
            {
                'bundles.0': [f.Required.CODE_EMPTY],
                'bundles.1': [f.Type.CODE_WRONG_TYPE],
                'bundles.2': [f.Type.CODE_WRONG_TYPE],
                'bundles.3': [f.Required.CODE_EMPTY],
                'bundles.4': [Trytes.CODE_NOT_TRYTES],
                'bundles.6': [f.Type.CODE_WRONG_TYPE],
                'bundles.7': [Trytes.CODE_WRONG_FORMAT],
            },
        )

    def test_fail_addresses_wrong_type(self):
        """
    ``addresses`` is not an array.
    """
        self.assertFilterErrors(
            {
                'addresses': Address(self.trytes1),
            },
            {
                'addresses': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_addresses_contents_invalid(self):
        """
    ``addresses`` is an array, but it contains invalid values.
    """
        self.assertFilterErrors(
            {
                'addresses': [
                    b'',
                    text_type(self.trytes1, 'ascii'),
                    True,
                    None,
                    b'not valid trytes',

                    # This is actually valid; I just added it to make sure the
                    # filter isn't cheating!
                    TryteString(self.trytes2),
                    2130706433,
                    b'9' * 82,
                ],
            },
            {
                'addresses.0': [f.Required.CODE_EMPTY],
                'addresses.1': [f.Type.CODE_WRONG_TYPE],
                'addresses.2': [f.Type.CODE_WRONG_TYPE],
                'addresses.3': [f.Required.CODE_EMPTY],
                'addresses.4': [Trytes.CODE_NOT_TRYTES],
                'addresses.6': [f.Type.CODE_WRONG_TYPE],
                'addresses.7': [Trytes.CODE_WRONG_FORMAT],
            },
        )

    def test_fail_tags_wrong_type(self):
        """
    ``tags`` is not an array.
    """
        self.assertFilterErrors(
            {
                'tags': Tag(self.trytes1),
            },
            {
                'tags': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_tags_contents_invalid(self):
        """
    ``tags`` is an array, but it contains invalid values.
    """
        self.assertFilterErrors(
            {
                'tags': [
                    b'',
                    text_type(self.trytes1, 'ascii'),
                    True,
                    None,
                    b'not valid trytes',

                    # This is actually valid; I just added it to make sure the
                    # filter isn't cheating!
                    TryteString(self.trytes1),
                    2130706433,
                    b'9' * 28,
                ],
            },
            {
                'tags.0': [f.Required.CODE_EMPTY],
                'tags.1': [f.Type.CODE_WRONG_TYPE],
                'tags.2': [f.Type.CODE_WRONG_TYPE],
                'tags.3': [f.Required.CODE_EMPTY],
                'tags.4': [Trytes.CODE_NOT_TRYTES],
                'tags.6': [f.Type.CODE_WRONG_TYPE],
                'tags.7': [Trytes.CODE_WRONG_FORMAT],
            },
        )

    def test_fail_approvees_wrong_type(self):
        """
    ``approvees`` is not an array.
    """
        self.assertFilterErrors(
            {
                'approvees': TransactionHash(self.trytes1),
            },
            {
                'approvees': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_approvees_contents_invalid(self):
        """
    ``approvees`` is an array, but it contains invalid values.
    """
        self.assertFilterErrors(
            {
                'approvees': [
                    b'',
                    text_type(self.trytes1, 'ascii'),
                    True,
                    None,
                    b'not valid trytes',

                    # This is actually valid; I just added it to make sure the
                    # filter isn't cheating!
                    TryteString(self.trytes2),
                    2130706433,
                    b'9' * 82,
                ],
            },
            {
                'approvees.0': [f.Required.CODE_EMPTY],
                'approvees.1': [f.Type.CODE_WRONG_TYPE],
                'approvees.2': [f.Type.CODE_WRONG_TYPE],
                'approvees.3': [f.Required.CODE_EMPTY],
                'approvees.4': [Trytes.CODE_NOT_TRYTES],
                'approvees.6': [f.Type.CODE_WRONG_TYPE],
                'approvees.7': [Trytes.CODE_WRONG_FORMAT],
            },
        )