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

Commit 2c63162

Browse files
committed
Issue #89 - Initial work on adding checksum option to get_new_addresses
1 parent ac6167d commit 2c63162

File tree

5 files changed

+98
-14
lines changed

5 files changed

+98
-14
lines changed

examples/address_generator.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
from six import binary_type, moves as compat, text_type
1717

1818

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

2323
# Create the API instance.
@@ -34,7 +34,7 @@ def main(uri, index, count):
3434
print('')
3535

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

@@ -111,4 +111,19 @@ def output_seed(seed):
111111
'If not specified, the first unused address will be returned.'
112112
)
113113

114+
parser.add_argument(
115+
'--security',
116+
type = int,
117+
default = 2,
118+
help = 'Security level to be used for the private key / address. Can be 1, 2 or 3',
119+
)
120+
121+
parser.add_argument(
122+
'--with-checksum',
123+
action = 'store_true',
124+
default = False,
125+
dest = 'checksum',
126+
help = 'List the address with the checksum.',
127+
)
128+
114129
main(**vars(parser.parse_args(argv[1:])))

iota/api.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,9 @@ def get_new_addresses(
611611
index = 0,
612612
count = 1,
613613
security_level = AddressGenerator.DEFAULT_SECURITY_LEVEL,
614+
checksum = False,
614615
):
615-
# type: (int, Optional[int], int) -> dict
616+
# type: (int, Optional[int], int, Optional[bool]) -> dict
616617
"""
617618
Generates one or more new addresses from the seed.
618619
@@ -636,6 +637,10 @@ def get_new_addresses(
636637
637638
This value must be between 1 and 3, inclusive.
638639
640+
:param checksum:
641+
Specify whether to return the address with the checksum.
642+
Defaults to False.
643+
639644
:return:
640645
Dict with the following items::
641646
@@ -651,6 +656,7 @@ def get_new_addresses(
651656
count = count,
652657
index = index,
653658
securityLevel = security_level,
659+
checksum = checksum,
654660
seed = self.seed,
655661
)
656662

iota/commands/extended/get_new_addresses.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import filters as f
88

9-
from iota import Address
9+
from iota import Address, AddressChecksum
1010
from iota.commands import FilterCommand, RequestFilter
1111
from iota.commands.core.find_transactions import FindTransactionsCommand
1212
from iota.crypto.addresses import AddressGenerator
@@ -36,25 +36,35 @@ def _execute(self, request):
3636
count = request['count'] # type: Optional[int]
3737
index = request['index'] # type: int
3838
security_level = request['securityLevel'] # type: int
39+
checksum = request['checksum'] # type: Optional[bool]
3940
seed = request['seed'] # type: Seed
4041

4142
return {
42-
'addresses': self._find_addresses(seed, index, count, security_level),
43+
'addresses':
44+
self._find_addresses(seed, index, count, security_level, checksum),
4345
}
4446

45-
def _find_addresses(self, seed, index, count, security_level):
46-
# type: (Seed, int, Optional[int], int) -> List[Address]
47+
def _find_addresses(self, seed, index, count, security_level, checksum):
48+
# type: (Seed, int, Optional[int], int, Optional[bool]) -> List[Address]
4749
"""
4850
Find addresses matching the command parameters.
4951
"""
50-
# type: (Seed, int, Optional[int]) -> List[Address]
51-
generator = AddressGenerator(seed, security_level)
52+
# type: (Seed, int, Optional[bool]) -> List[Address]
53+
generator = AddressGenerator(seed, security_level, checksum)
5254

5355
if count is None:
5456
# Connect to Tangle and find the first address without any
5557
# transactions.
5658
for addy in generator.create_iterator(start=index):
57-
response = FindTransactionsCommand(self.adapter)(addresses=[addy])
59+
# If we're generating addresses with checksums we need to check
60+
# for transactions on the address without the checksum
61+
if not checksum:
62+
response = FindTransactionsCommand(self.adapter)(addresses=[addy])
63+
else:
64+
response = FindTransactionsCommand(self.adapter)(
65+
addresses=[addy][:-AddressChecksum.LEN]
66+
)
67+
5868

5969
if not response.get('hashes'):
6070
return [addy]
@@ -84,12 +94,15 @@ def __init__(self):
8494
| f.Max(self.MAX_SECURITY_LEVEL)
8595
| f.Optional(default=AddressGenerator.DEFAULT_SECURITY_LEVEL),
8696

97+
'checksum': f.Type(bool) | f.Optional(default=False),
98+
8799
'seed': f.Required | Trytes(result_type=Seed),
88100
},
89101

90102
allow_missing_keys = {
91103
'count',
92104
'index',
93105
'securityLevel',
106+
'checksum',
94107
},
95108
)

iota/crypto/addresses.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@ class AddressGenerator(Iterable[Address]):
4040
- :py:class:`iota.transaction.BundleValidator`
4141
"""
4242

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

4747
self.security_level = security_level
48+
self.checksum = checksum
4849
self.seed = Seed(seed)
4950

5051
def __iter__(self):
@@ -175,7 +176,10 @@ def _generate_address(self, key_iterator):
175176
176177
Used in the event of a cache miss.
177178
"""
178-
return self.address_from_digest(self._get_digest(key_iterator))
179+
if self.checksum:
180+
return self.address_from_digest(self._get_digest(key_iterator)).with_valid_checksum()
181+
else:
182+
return self.address_from_digest(self._get_digest(key_iterator))
179183

180184
@staticmethod
181185
def _get_digest(key_iterator):

test/commands/extended/get_new_addresses_test.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def test_pass_happy_path(self):
3535
'index': 1,
3636
'count': 1,
3737
'securityLevel': 2,
38+
'checksum': False,
3839
}
3940

4041
filter_ = self._filter(request)
@@ -59,6 +60,7 @@ def test_pass_optional_parameters_excluded(self):
5960
'index': 0,
6061
'count': None,
6162
'securityLevel': AddressGenerator.DEFAULT_SECURITY_LEVEL,
63+
'checksum': False,
6264
},
6365
)
6466

@@ -75,6 +77,9 @@ def test_pass_compatible_types(self):
7577
'index': 100,
7678
'count': 8,
7779
'securityLevel': 2,
80+
81+
# ``checksum`` must be boolean.
82+
'checksum': False,
7883
})
7984

8085
self.assertFilterPasses(filter_)
@@ -86,6 +91,7 @@ def test_pass_compatible_types(self):
8691
'index': 100,
8792
'count': 8,
8893
'securityLevel': 2,
94+
'checksum': False,
8995
},
9096
)
9197

@@ -111,6 +117,7 @@ def test_fail_unexpected_parameters(self):
111117
'index': None,
112118
'count': 1,
113119
'securityLevel': 2,
120+
'checksum': False,
114121

115122
# Some men just want to watch the world burn.
116123
'foo': 'bar',
@@ -306,6 +313,21 @@ def test_fail_security_level_wrong_type(self):
306313
},
307314
)
308315

316+
def test_fail_checksum_wrong_type(self):
317+
"""
318+
``checksum`` is not a boolean.
319+
"""
320+
self.assertFilterErrors(
321+
{
322+
'checksum': '2',
323+
'seed': Seed(self.seed),
324+
},
325+
326+
{
327+
'checksum': [f.Type.CODE_WRONG_TYPE],
328+
},
329+
)
330+
309331

310332
class GetNewAddressesCommandTestCase(TestCase):
311333
# noinspection SpellCheckingInspection
@@ -333,6 +355,13 @@ def setUp(self):
333355
b'IWYTLQUUHDWSOVXLIKVJTYZBFKLABWRBFYVSMD9NB',
334356
)
335357

358+
self.addy_1_checksum =\
359+
Address(
360+
b'NYMWLBUJEISSACZZBRENC9HEHYQXHCGQHSNHVCEA'
361+
b'ZDCTEVNGSDUEKTSYBSQGMVJRIEDHWDYSEYCFAZAH'
362+
b'9T9FPJROTW',
363+
)
364+
336365
def test_wireup(self):
337366
"""
338367
Verify that the command is wired up correctly.
@@ -446,3 +475,20 @@ def test_get_addresses_online(self):
446475
},
447476
],
448477
)
478+
479+
def test_new_address_checksum(self):
480+
"""
481+
Generate address with a checksum.
482+
"""
483+
response =\
484+
self.command(
485+
count = 1,
486+
index = 0,
487+
seed = self.seed,
488+
checksum = True,
489+
)
490+
491+
self.assertDictEqual(
492+
response,
493+
{'addresses': [self.addy_1_checksum]},
494+
)

0 commit comments

Comments
 (0)