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

Commit f8dd41c

Browse files
authored
Merge pull request #86 from iotaledger/feature/84_newIriCompat
Feature/84 new iri compat
2 parents 0868b15 + f870b59 commit f8dd41c

File tree

13 files changed

+387
-186
lines changed

13 files changed

+387
-186
lines changed

iota/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from .transaction import *
3838
from .adapter import *
3939
from .api import *
40+
from .trits import *
4041

4142
# :see: http://stackoverflow.com/a/2073599/
4243
from pkg_resources import require

iota/adapter/__init__.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
from typing import Container, Dict, List, Optional, Text, Tuple, Union
1212

1313
from requests import Response, codes, request
14+
from six import PY2, binary_type, iteritems, moves as compat, text_type, \
15+
with_metaclass
16+
1417
from iota.exceptions import with_context
1518
from iota.json import JsonEncoder
16-
from six import PY2, binary_type, moves as compat, text_type, with_metaclass
1719

1820
__all__ = [
21+
'API_VERSION',
1922
'AdapterSpec',
2023
'BadApiResponse',
2124
'InvalidUri',
@@ -30,6 +33,13 @@
3033
__all__ = map(binary_type, __all__)
3134

3235

36+
API_VERSION = '1'
37+
"""
38+
API protocol version.
39+
https://github.com/iotaledger/iota.lib.py/issues/84
40+
"""
41+
42+
3343
# Custom types for type hints and docstrings.
3444
AdapterSpec = Union[Text, 'BaseAdapter']
3545

@@ -201,6 +211,18 @@ class HttpAdapter(BaseAdapter):
201211
"""
202212
supported_protocols = ('http', 'https',)
203213

214+
DEFAULT_HEADERS = {
215+
'Content-type': 'application/json',
216+
217+
# https://github.com/iotaledger/iota.lib.py/issues/84
218+
'X-IOTA-API-Version': API_VERSION,
219+
}
220+
"""
221+
Default headers sent with every request.
222+
These can be overridden on a per-request basis, by specifying values
223+
in the ``headers`` kwarg.
224+
"""
225+
204226
def __init__(self, uri):
205227
# type: (Union[Text, SplitResult]) -> None
206228
super(HttpAdapter, self).__init__()
@@ -265,7 +287,8 @@ def get_uri(self):
265287
def send_request(self, payload, **kwargs):
266288
# type: (dict, dict) -> dict
267289
kwargs.setdefault('headers', {})
268-
kwargs['headers']['Content-type'] = 'application/json'
290+
for key, value in iteritems(self.DEFAULT_HEADERS):
291+
kwargs['headers'].setdefault(key, value)
269292

270293
response = self._send_http_request(
271294
# Use a custom JSON encoder that knows how to convert Tryte values.

iota/crypto/signing.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from iota.crypto.kerl import Kerl
1212
from iota.crypto.types import PrivateKey, Seed
1313
from iota.exceptions import with_context
14+
from iota.trits import add_trits, trits_from_int
1415

1516
__all__ = [
1617
'KeyGenerator',
@@ -307,20 +308,8 @@ def _create_sponge(self, index):
307308
"""
308309
seed = self.seed_as_trits[:]
309310

310-
for i in range(index):
311-
# Treat ``seed`` like a really big number and add ``index``.
312-
# Note that addition works a little bit differently in balanced
313-
# ternary.
314-
for j in range(len(seed)):
315-
seed[j] += 1
316-
317-
if seed[j] > 1:
318-
seed[j] = -1
319-
else:
320-
break
321-
322311
sponge = Kerl()
323-
sponge.absorb(seed)
312+
sponge.absorb(add_trits(seed, trits_from_int(index)))
324313

325314
# Squeeze all of the trits out of the sponge and re-absorb them.
326315
# Note that the sponge transforms several times per operation, so

iota/transaction/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
from iota.json import JsonSerializable
1212
from iota.transaction.types import BundleHash, Fragment, Nonce, TransactionHash, \
1313
TransactionTrytes
14-
from iota.types import Address, Tag, TryteString, TrytesCompatible, \
15-
int_from_trits, trits_from_int
14+
from iota.trits import int_from_trits, trits_from_int
15+
from iota.types import Address, Tag, TryteString, TrytesCompatible
1616

1717
__all__ = [
1818
'Bundle',

iota/transaction/creation.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99

1010
from iota.crypto import HASH_LENGTH
1111
from iota.crypto.kerl import Kerl
12-
from iota.crypto.signing import KeyGenerator
12+
from iota.crypto.signing import KeyGenerator, normalize
1313
from iota.crypto.types import PrivateKey
1414
from iota.exceptions import with_context
1515
from iota.transaction.base import Bundle, Transaction
16-
from iota.transaction.types import BundleHash, Fragment, TransactionHash, Nonce
16+
from iota.transaction.types import BundleHash, Fragment, Nonce, TransactionHash
1717
from iota.transaction.utils import get_current_timestamp
18-
from iota.types import Address, Hash, Tag, TryteString
18+
from iota.trits import add_trits
19+
from iota.types import Address, Tag, TryteString
1920

2021
__all__ = [
2122
'ProposedBundle',
@@ -83,6 +84,17 @@ def as_tryte_string(self):
8384

8485
return super(ProposedTransaction, self).as_tryte_string()
8586

87+
def increment_legacy_tag(self):
88+
"""
89+
Increments the transaction's legacy tag, used to fix insecure
90+
bundle hashes when finalizing a bundle.
91+
92+
References:
93+
- https://github.com/iotaledger/iota.lib.py/issues/84
94+
"""
95+
self._legacy_tag =\
96+
Tag.from_trits(add_trits(self.legacy_tag.as_trits(), [1]))
97+
8698

8799
Transfer = ProposedTransaction
88100
"""
@@ -322,20 +334,31 @@ def finalize(self):
322334
)
323335

324336
# Generate bundle hash.
325-
sponge = Kerl()
326-
last_index = len(self) - 1
337+
while True:
338+
sponge = Kerl()
339+
last_index = len(self) - 1
340+
341+
for (i, txn) in enumerate(self): # type: Tuple[int, ProposedTransaction]
342+
txn.current_index = i
343+
txn.last_index = last_index
327344

328-
for (i, txn) in enumerate(self): # type: Tuple[int, ProposedTransaction]
329-
txn.current_index = i
330-
txn.last_index = last_index
345+
sponge.absorb(txn.get_signature_validation_trytes().as_trits())
331346

332-
sponge.absorb(txn.get_signature_validation_trytes().as_trits())
347+
bundle_hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int]
348+
sponge.squeeze(bundle_hash_trits)
333349

334-
bundle_hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int]
335-
sponge.squeeze(bundle_hash_trits)
350+
bundle_hash = BundleHash.from_trits(bundle_hash_trits)
351+
352+
# Check that we generated a secure bundle hash.
353+
# https://github.com/iotaledger/iota.lib.py/issues/84
354+
if any(13 in part for part in normalize(bundle_hash)):
355+
# Increment the legacy tag and try again.
356+
tail_transaction = self.tail_transaction # type: ProposedTransaction
357+
tail_transaction.increment_legacy_tag()
358+
else:
359+
break
336360

337361
# Copy bundle hash to individual transactions.
338-
bundle_hash = BundleHash.from_trits(bundle_hash_trits)
339362
for txn in self:
340363
txn.bundle_hash = bundle_hash
341364

iota/trits.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# coding=utf-8
2+
"""
3+
Provides functions for manipulating sequences of trits.
4+
5+
Based on:
6+
https://github.com/iotaledger/iota.lib.js/blob/v0.4.2/lib/crypto/helpers/adder.js
7+
"""
8+
9+
from __future__ import absolute_import, division, print_function, \
10+
unicode_literals
11+
12+
from typing import Iterable, List, Optional, Sequence, Tuple
13+
14+
__all__ = [
15+
'add_trits',
16+
'int_from_trits',
17+
'trits_from_int',
18+
]
19+
20+
21+
def add_trits(left, right):
22+
# type: (Sequence[int], Sequence[int]) -> List[int]
23+
"""
24+
Adds two sequences of trits together.
25+
26+
The result is a list of trits equal in length to the longer of the
27+
two sequences.
28+
29+
Note: Overflow is possible.
30+
For example, ``add_trits([1], [1])`` returns ``[-1]``.
31+
"""
32+
target_len = max(len(left), len(right))
33+
34+
res = [0] * target_len
35+
left += [0] * (target_len - len(left))
36+
right += [0] * (target_len - len(right))
37+
38+
carry = 0
39+
for i in range(len(res)):
40+
res[i], carry = _full_add_trits(left[i], right[i], carry)
41+
42+
return res
43+
44+
45+
def int_from_trits(trits):
46+
# type: (Iterable[int]) -> int
47+
"""
48+
Converts a sequence of trits into an integer value.
49+
"""
50+
# Normally we'd have to wrap ``enumerate`` inside ``reversed``, but
51+
# balanced ternary puts least significant digits first.
52+
return sum(base * (3 ** power) for power, base in enumerate(trits))
53+
54+
55+
def trits_from_int(n, pad=1):
56+
# type: (int, Optional[int]) -> List[int]
57+
"""
58+
Returns a trit representation of an integer value.
59+
60+
:param n:
61+
Integer value to convert.
62+
63+
:param pad:
64+
Ensure the result has at least this many trits.
65+
66+
References:
67+
- https://dev.to/buntine/the-balanced-ternary-machines-of-soviet-russia
68+
- https://en.wikipedia.org/wiki/Balanced_ternary
69+
- https://rosettacode.org/wiki/Balanced_ternary#Python
70+
"""
71+
if n == 0:
72+
trits = []
73+
else:
74+
quotient, remainder = divmod(n, 3)
75+
76+
if remainder == 2:
77+
# Lend 1 to the next place so we can make this trit negative.
78+
quotient += 1
79+
remainder = -1
80+
81+
trits = [remainder] + trits_from_int(quotient, pad=0)
82+
83+
if pad:
84+
trits += [0] * max(0, pad - len(trits))
85+
86+
return trits
87+
88+
89+
def _cons_trits(left, right):
90+
# type: (int, int) -> int
91+
"""
92+
Compares two trits. If they have the same value, returns that value.
93+
Otherwise, returns 0.
94+
"""
95+
return left if left == right else 0
96+
97+
98+
def _add_trits(left, right):
99+
# type: (int, int) -> int
100+
"""
101+
Adds two individual trits together.
102+
103+
The result is always a single trit.
104+
"""
105+
res = left + right
106+
return res if -2 < res < 2 else (res < 0) - (res > 0)
107+
108+
109+
def _any_trits(left, right):
110+
# type: (int, int) -> int
111+
"""
112+
Adds two individual trits together and returns a single trit
113+
indicating whether the result is positive or negative.
114+
"""
115+
res = left + right
116+
return (res > 0) - (res < 0)
117+
118+
119+
def _full_add_trits(left, right, carry):
120+
# type: (int, int, int) -> Tuple[int, int]
121+
"""
122+
Adds two trits together, with support for a carry trit.
123+
"""
124+
sum_both = _add_trits(left, right)
125+
cons_left = _cons_trits(left, right)
126+
cons_right = _cons_trits(sum_both, carry)
127+
128+
return _add_trits(sum_both, carry), _any_trits(cons_left, cons_right)

iota/types.py

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from iota.crypto.kerl import Kerl
1818
from iota.exceptions import with_context
1919
from iota.json import JsonSerializable
20+
from iota.trits import int_from_trits, trits_from_int
2021

2122
__all__ = [
2223
'Address',
@@ -25,59 +26,13 @@
2526
'Tag',
2627
'TryteString',
2728
'TrytesCompatible',
28-
'int_from_trits',
29-
'trits_from_int',
3029
]
3130

3231

3332
# Custom types for type hints and docstrings.
3433
TrytesCompatible = Union[AnyStr, bytearray, 'TryteString']
3534

3635

37-
def trits_from_int(n, pad=1):
38-
# type: (int, Optional[int]) -> List[int]
39-
"""
40-
Returns a trit representation of an integer value.
41-
42-
:param n:
43-
Integer value to convert.
44-
45-
:param pad:
46-
Ensure the result has at least this many trits.
47-
48-
References:
49-
- https://dev.to/buntine/the-balanced-ternary-machines-of-soviet-russia
50-
- https://en.wikipedia.org/wiki/Balanced_ternary
51-
- https://rosettacode.org/wiki/Balanced_ternary#Python
52-
"""
53-
if n == 0:
54-
trits = []
55-
else:
56-
quotient, remainder = divmod(n, 3)
57-
58-
if remainder == 2:
59-
# Lend 1 to the next place so we can make this trit negative.
60-
quotient += 1
61-
remainder = -1
62-
63-
trits = [remainder] + trits_from_int(quotient, pad=0)
64-
65-
if pad:
66-
trits += [0] * max(0, pad - len(trits))
67-
68-
return trits
69-
70-
71-
def int_from_trits(trits):
72-
# type: (Iterable[int]) -> int
73-
"""
74-
Converts a sequence of trits into an integer value.
75-
"""
76-
# Normally we'd have to wrap ``enumerate`` inside ``reversed``, but
77-
# balanced ternary puts least significant digits first.
78-
return sum(base * (3 ** power) for power, base in enumerate(trits))
79-
80-
8136
@python_2_unicode_compatible
8237
class TryteString(JsonSerializable):
8338
"""

0 commit comments

Comments
 (0)