def test_generator_with_offset(self): """ Creating a generator that starts at an offset greater than 0. """ # Seed is not important for this test; it is only used by # :py:class:`KeyGenerator`, which we will mock in this test. ag = AddressGenerator(seed=b'') # noinspection PyUnresolvedReferences with patch.object(ag, '_create_digest_generator', self._mock_digest_gen): generator = ag.create_generator(start=1, step=2) self.assertEqual(next(generator), self.addy1) self.assertEqual(next(generator), self.addy3)
def _execute(self, request): stop = request['stop'] # type: Optional[int] seed = request['seed'] # type: Seed start = request['start'] # type: int threshold = request['threshold'] # type: Optional[int] generator = AddressGenerator(seed) # Determine the addresses we will be scanning. if stop is None: # This is similar to the ``getNewAddresses`` command, except it # is interested in all the addresses that `getNewAddresses` # skips. addresses = [] # type: List[Address] for addy in generator.create_generator(start): ft_response = FindTransactionsCommand( self.adapter)(addresses=[addy]) if ft_response.get('hashes'): addresses.append(addy) else: break else: addresses = generator.get_addresses(start, stop) # Load balances for the addresses that we generated. gb_response = GetBalancesCommand(self.adapter)(addresses=addresses) result = { 'inputs': [], 'totalBalance': 0, } threshold_met = threshold is None for i, balance in enumerate(gb_response['balances']): addresses[i].balance = balance if balance: result['inputs'].append(addresses[i]) result['totalBalance'] += balance if (threshold is not None) and (result['totalBalance'] >= threshold): threshold_met = True break if threshold_met: return result else: # This is an exception case, but note that we attach the result # to the exception context so that it can be used for # troubleshooting. raise with_context( exc=BadApiResponse( 'Accumulated balance {balance} is less than threshold {threshold} ' '(``exc.context`` contains more information).'.format( threshold=threshold, balance=result['totalBalance'], ), ), context={ 'inputs': result['inputs'], 'request': request, 'total_balance': result['totalBalance'], }, )
def _execute(self, request): stop = request['stop'] # type: Optional[int] inclusion_states = request['inclusionStates'] # type: bool seed = request['seed'] # type: Seed start = request['start'] # type: int generator = AddressGenerator(seed) ft_command = FindTransactionsCommand(self.adapter) # Determine the addresses we will be scanning, and pull their # transaction hashes. if stop is None: # This is similar to the ``getNewAddresses`` command, except it # is interested in all the addresses that `getNewAddresses` # skips. hashes = [] for addy in generator.create_generator(start): ft_response = ft_command(addresses=[addy]) if ft_response.get('hashes'): hashes += ft_response['hashes'] else: break # Reset the command so that we can call it again. ft_command.reset() else: ft_response =\ ft_command(addresses=generator.get_addresses(start, stop - start)) hashes = ft_response.get('hashes') or [] all_bundles = [] # type: List[Bundle] if hashes: # Sort transactions into tail and non-tail. tail_transaction_hashes = set() non_tail_bundle_hashes = set() gt_response = GetTrytesCommand(self.adapter)(hashes=hashes) all_transactions = list( map( Transaction.from_tryte_string, gt_response['trytes'], )) # type: List[Transaction] for txn in all_transactions: if txn.is_tail: tail_transaction_hashes.add(txn.hash) else: # Capture the bundle ID instead of the transaction hash so that # we can query the node to find the tail transaction for that # bundle. non_tail_bundle_hashes.add(txn.bundle_hash) if non_tail_bundle_hashes: for txn in self._find_transactions( bundles=list(non_tail_bundle_hashes)): if txn.is_tail: if txn.hash not in tail_transaction_hashes: all_transactions.append(txn) tail_transaction_hashes.add(txn.hash) # Filter out all non-tail transactions. tail_transactions = [ txn for txn in all_transactions if txn.hash in tail_transaction_hashes ] # Attach inclusion states, if requested. if inclusion_states: gli_response = GetLatestInclusionCommand(self.adapter)( hashes=list(tail_transaction_hashes), ) for txn in tail_transactions: txn.is_confirmed = gli_response['states'].get(txn.hash) # Find the bundles for each transaction. for txn in tail_transactions: gb_response = GetBundlesCommand( self.adapter)(transaction=txn.hash) txn_bundles = gb_response['bundles'] # type: List[Bundle] if inclusion_states: for bundle in txn_bundles: bundle.is_confirmed = txn.is_confirmed all_bundles.extend(txn_bundles) return { # Sort bundles by tail transaction timestamp. 'bundles': list( sorted( all_bundles, key=lambda bundle_: bundle_.tail_transaction.timestamp, )), }