Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #106 from plenarius/new_address_checksum
Browse files Browse the repository at this point in the history
Issue #89 - Initial work on adding checksum option to get_new_addresses
  • Loading branch information
todofixthis committed Dec 9, 2017
2 parents ff5e944 + 9c67ba3 commit 0a5d564
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 16 deletions.
23 changes: 20 additions & 3 deletions examples/address_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
from typing import Optional, Text

from iota import __version__, Iota
from iota.crypto.addresses import AddressGenerator
from iota.crypto.types import Seed
from six import binary_type, moves as compat, text_type


def main(uri, index, count):
# type: (Text, int, Optional[int], bool) -> None
def main(uri, index, count, security, checksum):
# type: (Text, int, Optional[int], Optional[int], bool) -> None
seed = get_seed()

# Create the API instance.
Expand All @@ -34,7 +35,7 @@ def main(uri, index, count):
print('')

# Here's where all the magic happens!
api_response = api.get_new_addresses(index, count)
api_response = api.get_new_addresses(index, count, security, checksum)
for addy in api_response['addresses']:
print(binary_type(addy).decode('ascii'))

Expand Down Expand Up @@ -111,4 +112,20 @@ def output_seed(seed):
'If not specified, the first unused address will be returned.'
)

parser.add_argument(
'--security',
type = int,
default = AddressGenerator.DEFAULT_SECURITY_LEVEL,
help = 'Security level to be used for the private key / address. '
'Can be 1, 2 or 3',
)

parser.add_argument(
'--with-checksum',
action = 'store_true',
default = False,
dest = 'checksum',
help = 'List the address with the checksum.',
)

main(**vars(parser.parse_args(argv[1:])))
8 changes: 7 additions & 1 deletion iota/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,9 @@ def get_new_addresses(
index = 0,
count = 1,
security_level = AddressGenerator.DEFAULT_SECURITY_LEVEL,
checksum = False,
):
# type: (int, Optional[int], int) -> dict
# type: (int, Optional[int], int, bool) -> dict
"""
Generates one or more new addresses from the seed.
Expand All @@ -636,6 +637,10 @@ def get_new_addresses(
This value must be between 1 and 3, inclusive.
:param checksum:
Specify whether to return the address with the checksum.
Defaults to False.
:return:
Dict with the following items::
Expand All @@ -651,6 +656,7 @@ def get_new_addresses(
count = count,
index = index,
securityLevel = security_level,
checksum = checksum,
seed = self.seed,
)

Expand Down
26 changes: 17 additions & 9 deletions iota/commands/extended/get_new_addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import filters as f

from iota import Address
from iota import Address, AddressChecksum
from iota.commands import FilterCommand, RequestFilter
from iota.commands.core.find_transactions import FindTransactionsCommand
from iota.crypto.addresses import AddressGenerator
Expand All @@ -33,28 +33,33 @@ def get_response_filter(self):
pass

def _execute(self, request):
checksum = request['checksum'] # type: bool
count = request['count'] # type: Optional[int]
index = request['index'] # type: int
security_level = request['securityLevel'] # type: int
seed = request['seed'] # type: Seed

return {
'addresses': self._find_addresses(seed, index, count, security_level),
'addresses':
self._find_addresses(seed, index, count, security_level, checksum),
}

def _find_addresses(self, seed, index, count, security_level):
# type: (Seed, int, Optional[int], int) -> List[Address]
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.
"""
# type: (Seed, int, Optional[int]) -> List[Address]
generator = AddressGenerator(seed, security_level)
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):
response = FindTransactionsCommand(self.adapter)(addresses=[addy])
# We use addy.address here because FindTransactions does
# not work on an address with a checksum
response = FindTransactionsCommand(self.adapter)(
addresses=[addy.address]
)

if not response.get('hashes'):
return [addy]
Expand All @@ -75,8 +80,10 @@ def __init__(self):
super(GetNewAddressesRequestFilter, self).__init__(
{
# Everything except ``seed`` is optional.
'count': f.Type(int) | f.Min(1),
'index': f.Type(int) | f.Min(0) | f.Optional(default=0),

'checksum': f.Type(bool) | f.Optional(default=False),
'count': f.Type(int) | f.Min(1),
'index': f.Type(int) | f.Min(0) | f.Optional(default=0),

'securityLevel':
f.Type(int)
Expand All @@ -88,6 +95,7 @@ def __init__(self):
},

allow_missing_keys = {
'checksum',
'count',
'index',
'securityLevel',
Expand Down
10 changes: 7 additions & 3 deletions iota/crypto/addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ class AddressGenerator(Iterable[Address]):
- :py:class:`iota.transaction.BundleValidator`
"""

def __init__(self, seed, security_level=DEFAULT_SECURITY_LEVEL):
# type: (TrytesCompatible, int) -> None
def __init__(self, seed, security_level=DEFAULT_SECURITY_LEVEL, checksum=False):
# type: (TrytesCompatible, int, bool) -> None
super(AddressGenerator, self).__init__()

self.security_level = security_level
self.checksum = checksum
self.seed = Seed(seed)

def __iter__(self):
Expand Down Expand Up @@ -175,7 +176,10 @@ def _generate_address(self, key_iterator):
Used in the event of a cache miss.
"""
return self.address_from_digest(self._get_digest(key_iterator))
if self.checksum:
return self.address_from_digest(self._get_digest(key_iterator)).with_valid_checksum()
else:
return self.address_from_digest(self._get_digest(key_iterator))

@staticmethod
def _get_digest(key_iterator):
Expand Down
46 changes: 46 additions & 0 deletions test/commands/extended/get_new_addresses_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_pass_happy_path(self):
'index': 1,
'count': 1,
'securityLevel': 2,
'checksum': False,
}

filter_ = self._filter(request)
Expand All @@ -59,6 +60,7 @@ def test_pass_optional_parameters_excluded(self):
'index': 0,
'count': None,
'securityLevel': AddressGenerator.DEFAULT_SECURITY_LEVEL,
'checksum': False,
},
)

Expand All @@ -75,6 +77,9 @@ def test_pass_compatible_types(self):
'index': 100,
'count': 8,
'securityLevel': 2,

# ``checksum`` must be boolean.
'checksum': False,
})

self.assertFilterPasses(filter_)
Expand All @@ -86,6 +91,7 @@ def test_pass_compatible_types(self):
'index': 100,
'count': 8,
'securityLevel': 2,
'checksum': False,
},
)

Expand All @@ -111,6 +117,7 @@ def test_fail_unexpected_parameters(self):
'index': None,
'count': 1,
'securityLevel': 2,
'checksum': False,

# Some men just want to watch the world burn.
'foo': 'bar',
Expand Down Expand Up @@ -306,6 +313,21 @@ def test_fail_security_level_wrong_type(self):
},
)

def test_fail_checksum_wrong_type(self):
"""
``checksum`` is not a boolean.
"""
self.assertFilterErrors(
{
'checksum': '2',
'seed': Seed(self.seed),
},

{
'checksum': [f.Type.CODE_WRONG_TYPE],
},
)


class GetNewAddressesCommandTestCase(TestCase):
# noinspection SpellCheckingInspection
Expand Down Expand Up @@ -333,6 +355,13 @@ def setUp(self):
b'IWYTLQUUHDWSOVXLIKVJTYZBFKLABWRBFYVSMD9NB',
)

self.addy_1_checksum =\
Address(
b'NYMWLBUJEISSACZZBRENC9HEHYQXHCGQHSNHVCEA'
b'ZDCTEVNGSDUEKTSYBSQGMVJRIEDHWDYSEYCFAZAH'
b'9T9FPJROTW',
)

def test_wireup(self):
"""
Verify that the command is wired up correctly.
Expand Down Expand Up @@ -446,3 +475,20 @@ def test_get_addresses_online(self):
},
],
)

def test_new_address_checksum(self):
"""
Generate address with a checksum.
"""
response =\
self.command(
checksum = True,
count = 1,
index = 0,
seed = self.seed,
)

self.assertDictEqual(
response,
{'addresses': [self.addy_1_checksum]},
)
34 changes: 34 additions & 0 deletions test/crypto/addresses_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,37 @@ def test_security_level_elevated(self):
),
],
)

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',
),
)

0 comments on commit 0a5d564

Please sign in to comment.