예제 #1
0
    def setUp(self):
        super(GetInputsCommandTestCase, self).setUp()

        self.adapter = MockAdapter()
        self.command = GetInputsCommand(self.adapter)

        # Define some valid tryte sequences that we can reuse between
        # tests.
        self.addy0 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION99999FIODSG'
              b'IC9CCIFCNBTBDFIEHHE9RBAEVGK9JECCLCPBIINAX',

            key_index = 0,
          )

        self.addy1 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION999999EPCNH'
              b'MBTEH9KDVFMHHESDOBTFFACCGBFGACEDCDDCGICIL',

            key_index = 1,
          )

        self.addy2 =\
          Address(
            trytes =
              b'TESTVALUE9DONTUSEINPRODUCTION99999YDOHWF'
              b'U9PFOFHGKFACCCBGDALGI9ZBEBABFAMBPDSEQ9XHJ',

            key_index = 2,
          )
예제 #2
0
    def test_security_level_1_no_stop(self):
        """
    Testing GetInputsCoommand:
      - with security_level = 1 (non default)
      - without `stop` parameter
    """

        # one address with index 0 for selected security levels for the random seed.
        # to check with respective outputs from command
        seed = Seed.random()
        address = AddressGenerator(seed, security_level=1).get_addresses(0)[0]

        # ``getInputs`` uses ``findTransactions`` and
        # ``wereAddressesSpentFrom`` to identify unused addresses.
        # noinspection SpellCheckingInspection
        self.adapter.seed_response(
            'findTransactions', {
                'hashes': [
                    TransactionHash(
                        b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD'
                        b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'),
                ],
            })
        self.adapter.seed_response('findTransactions', {
            'hashes': [],
        })

        self.adapter.seed_response('wereAddressesSpentFrom', {
            'states': [False],
        })

        self.adapter.seed_response('getBalances', {
            'balances': [86],
        })

        response = GetInputsCommand(self.adapter)(
            seed=seed,
            securityLevel=1,
        )

        self.assertEqual(response['totalBalance'], 86)
        self.assertEqual(len(response['inputs']), 1)

        input0 = response['inputs'][0]
        self.assertIsInstance(input0, Address)
        self.assertEqual(input0, address)
        self.assertEqual(input0.balance, 86)
        self.assertEqual(input0.key_index, 0)
예제 #3
0
class GetInputsRequestFilterTestCase(BaseFilterTestCase):
    filter_type = GetInputsCommand(MockAdapter()).get_request_filter
    skip_value_check = True

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

        # Define a few tryte sequences that we can re-use between tests.
        self.seed = b'HELLOIOTA'

    def test_pass_happy_path(self):
        """
    Request is valid.
    """
        request = {
            'seed': Seed(self.seed),
            'start': 0,
            'stop': 10,
            'threshold': 100,
        }

        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({
            # ``seed`` can be any value that is convertible into a
            # TryteString.
            'seed': binary_type(self.seed),

            # These values must still be integers, however.
            'start': 42,
            'stop': 86,
            'threshold': 99,
        })

        self.assertFilterPasses(filter_)
        self.assertDictEqual(
            filter_.cleaned_data,
            {
                'seed': Seed(self.seed),
                'start': 42,
                'stop': 86,
                'threshold': 99,
            },
        )

    def test_pass_optional_parameters_excluded(self):
        """
    The request contains only required parameters.
    """
        filter_ = self._filter({
            'seed': Seed(self.seed),
        })

        self.assertFilterPasses(filter_)
        self.assertDictEqual(filter_.cleaned_data, {
            'seed': Seed(self.seed),
            'start': 0,
            'stop': None,
            'threshold': None,
        })

    def test_fail_empty_request(self):
        """
    The request is empty.
    """
        self.assertFilterErrors(
            {},
            {
                'seed': [f.FilterMapper.CODE_MISSING_KEY],
            },
        )

    def test_fail_unexpected_parameters(self):
        """
    The request contains unexpected parameters.
    """
        self.assertFilterErrors(
            {
                'seed': Seed(self.seed),

                # Told you I did. Reckless is he. Now, matters are worse.
                'foo': 'bar',
            },
            {
                'foo': [f.FilterMapper.CODE_EXTRA_KEY],
            },
        )

    def test_fail_seed_null(self):
        """
    ``seed`` is null.
    """
        self.assertFilterErrors(
            {
                'seed': None,
            },
            {
                'seed': [f.Required.CODE_EMPTY],
            },
        )

    def test_fail_seed_wrong_type(self):
        """
    ``seed`` cannot be converted into a TryteString.
    """
        self.assertFilterErrors(
            {
                'seed': text_type(self.seed, 'ascii'),
            },
            {
                'seed': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_seed_malformed(self):
        """
    ``seed`` has the correct type, but it contains invalid characters.
    """
        self.assertFilterErrors(
            {
                'seed':
                b'not valid; seeds can only contain uppercase and "9".',
            },
            {
                'seed': [Trytes.CODE_NOT_TRYTES],
            },
        )

    def test_fail_start_string(self):
        """
    ``start`` is a string.
    """
        self.assertFilterErrors(
            {
                # Not valid; it must be an int.
                'start': '0',
                'seed': Seed(self.seed),
            },
            {
                'start': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_start_float(self):
        """
    ``start`` is a float.
    """
        self.assertFilterErrors(
            {
                # Even with an empty fpart, floats are not valid.
                # It's gotta be an int.
                'start': 8.0,
                'seed': Seed(self.seed),
            },
            {
                'start': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_start_too_small(self):
        """
    ``start`` is less than 0.
    """
        self.assertFilterErrors(
            {
                'start': -1,
                'seed': Seed(self.seed),
            },
            {
                'start': [f.Min.CODE_TOO_SMALL],
            },
        )

    def test_fail_stop_string(self):
        """
    ``stop`` is a string.
    """
        self.assertFilterErrors(
            {
                # Not valid; it must be an int.
                'stop': '0',
                'seed': Seed(self.seed),
            },
            {
                'stop': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_stop_float(self):
        """
    ``stop`` is a float.
    """
        self.assertFilterErrors(
            {
                # Even with an empty fpart, floats are not valid.
                # It's gotta be an int.
                'stop': 8.0,
                'seed': Seed(self.seed),
            },
            {
                'stop': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_stop_too_small(self):
        """
    ``stop`` is less than 0.
    """
        self.assertFilterErrors(
            {
                'stop': -1,
                'seed': Seed(self.seed),
            },
            {
                'stop': [f.Min.CODE_TOO_SMALL],
            },
        )

    def test_fail_stop_occurs_before_start(self):
        """
    ``stop`` is less than ``start``.
    """
        self.assertFilterErrors(
            {
                'start': 1,
                'stop': 0,
                'seed': Seed(self.seed),
            },
            {
                'start': [GetInputsRequestFilter.CODE_INTERVAL_INVALID],
            },
        )

    def test_fail_interval_too_large(self):
        """
    ``stop`` is way more than ``start``.
    """
        self.assertFilterErrors(
            {
                'start': 0,
                'stop': GetInputsRequestFilter.MAX_INTERVAL + 1,
                'seed': Seed(self.seed),
            },
            {
                'stop': [GetInputsRequestFilter.CODE_INTERVAL_TOO_BIG],
            },
        )

    def test_fail_threshold_string(self):
        """
    ``threshold`` is a string.
    """
        self.assertFilterErrors(
            {
                # Not valid; it must be an int.
                'threshold': '0',
                'seed': Seed(self.seed),
            },
            {
                'threshold': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_threshold_float(self):
        """
    ``threshold`` is a float.
    """
        self.assertFilterErrors(
            {
                # Even with an empty fpart, floats are not valid.
                # It's gotta be an int.
                'threshold': 8.0,
                'seed': Seed(self.seed),
            },
            {
                'threshold': [f.Type.CODE_WRONG_TYPE],
            },
        )

    def test_fail_threshold_too_small(self):
        """
    ``threshold`` is less than 0.
    """
        self.assertFilterErrors(
            {
                'threshold': -1,
                'seed': Seed(self.seed),
            },
            {
                'threshold': [f.Min.CODE_TOO_SMALL],
            },
        )
예제 #4
0
    def _execute(self, request):
        # Required parameters.
        seed = request['seed']  # type: Seed
        bundle = ProposedBundle(request['transfers'])

        # Optional parameters.
        change_address = request.get(
            'changeAddress')  # type: Optional[Address]
        proposed_inputs = request.get(
            'inputs')  # type: Optional[List[Address]]

        want_to_spend = bundle.balance
        if want_to_spend > 0:
            # We are spending inputs, so we need to gather and sign them.
            if proposed_inputs is None:
                # No inputs provided.  Scan addresses for unspent inputs.
                gi_response = GetInputsCommand(self.adapter)(
                    seed=seed,
                    threshold=want_to_spend,
                )

                confirmed_inputs = gi_response['inputs']
            else:
                # Inputs provided.  Check to make sure we have sufficient
                # balance.
                available_to_spend = 0
                confirmed_inputs = []  # type: List[Address]

                gb_response = GetBalancesCommand(self.adapter)(
                    addresses=[i.address for i in proposed_inputs], )

                for i, balance in enumerate(gb_response.get('balances') or []):
                    input_ = proposed_inputs[i]

                    if balance > 0:
                        available_to_spend += balance

                        # Update the address balance from the API response, just in
                        # case somebody tried to cheat.
                        input_.balance = balance
                        confirmed_inputs.append(input_)

                if available_to_spend < want_to_spend:
                    raise with_context(
                        exc=BadApiResponse(
                            'Insufficient balance; found {found}, need {need} '
                            '(``exc.context`` has more info).'.format(
                                found=available_to_spend,
                                need=want_to_spend,
                            ), ),
                        context={
                            'available_to_spend': available_to_spend,
                            'confirmed_inputs': confirmed_inputs,
                            'request': request,
                            'want_to_spend': want_to_spend,
                        },
                    )

            bundle.add_inputs(confirmed_inputs)

            if bundle.balance < 0:
                if not change_address:
                    change_address =\
                      GetNewAddressesCommand(self.adapter)(seed=seed)['addresses'][0]

                bundle.send_unspent_inputs_to(change_address)

            bundle.finalize()

            if confirmed_inputs:
                bundle.sign_inputs(KeyGenerator(seed))
        else:
            bundle.finalize()

        return {
            'trytes': bundle.as_tryte_strings(),
        }