def test_generator_checksum(self): """ Creating a generator with checksums on the addresses. """ ag = AddressGenerator( self.seed_2, security_level=AddressGenerator.DEFAULT_SECURITY_LEVEL, checksum=True) generator = ag.create_iterator() # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'FNKCVJPUANHNWNBAHFBTCONMCUBC9KCZ9EKREBCJ' b'AFMABCTEPLGGXDJXVGPXDCFOUCRBWFJFLEAVOEUPY' b'ADHVCBXFD', ), ) # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'MSYILYYZLSJ99TDMGQHDOBWGHTBARCBGJZE9PIMQ' b'LTEXJXKTDREGVTPA9NDGGLQHTMGISGRAKSLYPGWMB' b'WIKQRCIOD', ), )
def test_get_addresses_single(self): """ Generating a single address. """ ag = AddressGenerator(self.seed_1) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=0), [ Address( b'DLEIS9XU9V9T9OURAKDUSQWBQEYFGJLRPRVEWKN9' b'SSUGIHBEIPBPEWISSAURGTQKWKWNHXGCBQTWNOGIY', ), ], ) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=10), [ Address( b'XLXFTFBXUOOHRJDVBDBFEBDQDUKSLSOCLUYWGLAP' b'R9FUROUHPFINIUFKYSRTFMNWKNEPDZATWXIVWJMDD', ), ], )
def test_get_addresses_error_step_zero(self): """ Providing a ``step`` value of 0 to ``get_addresses``. """ ag = AddressGenerator(seed=b'') with self.assertRaises(ValueError): ag.get_addresses(start=0, step=0)
def test_get_addresses_error_start_too_small(self): """ Providing a negative ``start`` value to ``get_addresses``. :py:class:`AddressGenerator` can potentially generate an infinite number of addresses, so there is no "end" to offset against. """ ag = AddressGenerator(seed=b'') with self.assertRaises(ValueError): ag.get_addresses(start=-1)
async def iter_used_addresses( adapter, # type: BaseAdapter seed, # type: Seed start, # type: int security_level=None, # type: Optional[int] ): # type: (...) -> Generator[Tuple[Address, List[TransactionHash]], None, None] """ Scans the Tangle for used addresses. This is basically the opposite of invoking ``getNewAddresses`` with ``stop=None``. """ if security_level is None: security_level = AddressGenerator.DEFAULT_SECURITY_LEVEL ft_command = FindTransactionsCommand(adapter) for addy in AddressGenerator(seed, security_level).create_iterator(start): ft_response = await 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()
async 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 await iter_used_addresses( self.adapter, seed, start)))) else: ft_response = \ await FindTransactionsCommand(self.adapter)( addresses= AddressGenerator(seed).get_addresses(start, stop - start), ) my_hashes = ft_response['hashes'] return { 'bundles': await get_bundles_from_transaction_hashes( adapter=self.adapter, transaction_hashes=my_hashes, inclusion_states=inclusion_states, ), }
def test_get_addresses_step_negative(self): """ Providing a negative ``step`` value to ``get_addresses``. This is probably a weird use case, but what the heck. """ ag = AddressGenerator(self.seed_1) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=1, count=2, step=-1), [ Address( b'PNLOTLFSALMICK9PSW9ZWLE9KJAKPKGJZQJDAFMO' b'VLHXMJCJXFPVHOTTOYDIAUAYELXKZWZUITCQBIQKY', ), Address( b'DLEIS9XU9V9T9OURAKDUSQWBQEYFGJLRPRVEWKN9' b'SSUGIHBEIPBPEWISSAURGTQKWKWNHXGCBQTWNOGIY', ), ], )
def test_security_level_elevated(self): """ Generating addresses with a higher security level. """ ag = AddressGenerator(self.seed_1, security_level=3) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=0, count=3), [ Address( b'BGHTGOUKKNTYFHYUAAPSRUEVN9QQXFOGVCH9Y9BO' b'NWXUBDLSKAWEOFZIVMHXBAYVPGDZEYCKNTUJCLPAX', ), Address( b'EGMRJEUIYFUGWAIXXZCHCZUVUUYITICVHDSHCQXG' b'FHJIVDCLTI9ZVRIKRLZQWW9CPOIXVDCBAHVGLUHI9', ), Address( b'ENPSARVJZGMMPWZTAIRHADEOZCEVIFNJWSZQHNEI' b'RVEVI9GYMFNEOGNUYCPGPSEFCSDHUHOQKDPVGDKYC', ), ], )
def test_security_level_lowered(self): """ Generating addresses with a lower security level. """ ag = AddressGenerator(self.seed_1, security_level=1) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=0, count=3), [ Address( b'KNDWDEEWWFVZLISLYRABGVWZCHZNZLNSEJXFKVGA' b'UFLL9UMZYEZMEJB9BDLAASWTHEKFREUDIUPY9ICKW', ), Address( b'CHOBTRTQWTMH9GWFWGWUODRSGPOJOIVJUNIQIBZL' b'HSWNYPHOD9APWJBMJMGLHFZENWFKDYWHX9JDFXTAB', ), Address( b'YHTOYQUCLDHAIDILFNPITVPYSTOCFAZIUNDYTRDZ' b'CVMVGZPONPINNVPJTOAOKHHZWLOKIZPVASTOGAKPA', ), ], )
async def _find_addresses(self, seed, index, count, security_level, checksum): # type: (Seed, int, Optional[int], int, bool) -> List[Address] """ Find addresses matching the command parameters. """ generator = AddressGenerator(seed, security_level, checksum) if count is None: # Connect to Tangle and find the first address without any # transactions. for addy in generator.create_iterator(start=index): # We use addy.address here because FindTransactions does # not work on an address with a checksum response = await FindTransactionsCommand(self.adapter)( addresses=[addy.address], ) if not response.get('hashes'): return [addy] return generator.get_addresses(start=index, count=count)
def test_generator_with_offset(self): """ Creating a generator that starts at an offset greater than 0. """ ag = AddressGenerator(self.seed_1) generator = ag.create_iterator(start=1, step=2) # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'PNLOTLFSALMICK9PSW9ZWLE9KJAKPKGJZQJDAFMO' b'VLHXMJCJXFPVHOTTOYDIAUAYELXKZWZUITCQBIQKY', ), ) # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'IWWMMHBFWCWOZQLBNXDJ9OOTIGXXU9WNUHFGUZWR' b'9FWGIUUUQUECHPKXJLIEKZBOVSEA9BCT9DLOCNCEC', ), )
def test_generator(self): """ Creating a generator. """ ag = AddressGenerator(self.seed_2) generator = ag.create_iterator() # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'FNKCVJPUANHNWNBAHFBTCONMCUBC9KCZ9EKREBCJ' b'AFMABCTEPLGGXDJXVGPXDCFOUCRBWFJFLEAVOEUPY', ), ) # noinspection SpellCheckingInspection self.assertEqual( next(generator), Address( b'MSYILYYZLSJ99TDMGQHDOBWGHTBARCBGJZE9PIMQ' b'LTEXJXKTDREGVTPA9NDGGLQHTMGISGRAKSLYPGWMB', ), )
def test_get_addresses_multiple(self): """ Generating multiple addresses in one go. """ ag = AddressGenerator(self.seed_2) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=0, count=3), [ Address( b'FNKCVJPUANHNWNBAHFBTCONMCUBC9KCZ9EKREBCJ' b'AFMABCTEPLGGXDJXVGPXDCFOUCRBWFJFLEAVOEUPY', ), Address( b'MSYILYYZLSJ99TDMGQHDOBWGHTBARCBGJZE9PIMQ' b'LTEXJXKTDREGVTPA9NDGGLQHTMGISGRAKSLYPGWMB', ), Address( b'IIREHGHXUHARKVZDMHGUUCHZLUEQQULLEUSJHIIB' b'WFYZIZDUFTOVHAWCKRJXUZ9CSUVLTRYSUGBVRMTOW', ), ], ) # noinspection SpellCheckingInspection self.assertListEqual( ag.get_addresses(start=10, count=3), [ Address( b'BPXMVV9UPKBTVPJXPBHHOJYAFLALOYCGTSEDLZBH' b'NFMGEHREBQTRIPZAPREANPMZJNZZNCDIUFOYYGGFY', ), Address( b'RUCZQJWKXVDIXTLHHOKGMHOV9AKVDBG9HUQHPWNZ' b'UNKJNFVMULUSLKFJGSTBSNJMRYSJOBVBQSKVXISZB', ), Address( b'FQAKF9XVCLTBESJKWCHFOCTVABYEEJP9RXUVAEUW' b'ENFUUQK9VCHFEORHCYDUJQHNUDWNRDUDZTUGKHSPD', ), ], )
async 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] security_level = request['security_level'] # 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, security_level): my_addresses.append(addy) my_hashes.extend(hashes) else: my_addresses = (AddressGenerator(seed, security_level).get_addresses( start, stop - start)) my_hashes = await FindTransactionsCommand(self.adapter )(addresses=my_addresses) my_hashes = my_hashes.get('hashes') or [] account_balance = 0 if my_hashes: # Load balances for the addresses that we generated. gb_response = (await 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, ), }
def test_security_level_1_with_stop(self): """ Testing GetInputsCoommand: - with security_level = 1 (non default) - with `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] self.adapter.seed_response('getBalances', { 'balances': [86], }) # ``getInputs`` uses ``findTransactions`` to identify unused # addresses. # noinspection SpellCheckingInspection self.adapter.seed_response( 'findTransactions', { 'hashes': [ TransactionHash( b'TESTVALUE9DONTUSEINPRODUCTION99999YFXGOD' b'GISBJAX9PDJIRDMDV9DCRDCAEG9FN9KECCBDDFZ9H'), ], }) self.adapter.seed_response('findTransactions', { 'hashes': [], }) response = GetInputsCommand(self.adapter)( seed=seed, securityLevel=1, stop=1, # <<<<< here ) 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)
async 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] security_level = request['securityLevel'] # int # Determine the addresses we will be scanning. if stop is None: addresses = [addy for addy, _ in await iter_used_addresses( adapter=self.adapter, seed=seed, start=start, security_level=security_level )] else: addresses = ( AddressGenerator(seed, security_level).get_addresses( start=start, count=stop - start, ) ) if addresses: # Load balances for the addresses that we generated. gb_response = await GetBalancesCommand(self.adapter)(addresses=addresses) else: gb_response = {'balances': []} 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'], }, )