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 #86 from iotaledger/feature/84_newIriCompat
Browse files Browse the repository at this point in the history
Feature/84 new iri compat
  • Loading branch information
todofixthis committed Oct 28, 2017
2 parents 0868b15 + f870b59 commit f8dd41c
Show file tree
Hide file tree
Showing 13 changed files with 387 additions and 186 deletions.
1 change: 1 addition & 0 deletions iota/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from .transaction import *
from .adapter import *
from .api import *
from .trits import *

# :see: http://stackoverflow.com/a/2073599/
from pkg_resources import require
Expand Down
27 changes: 25 additions & 2 deletions iota/adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
from typing import Container, Dict, List, Optional, Text, Tuple, Union

from requests import Response, codes, request
from six import PY2, binary_type, iteritems, moves as compat, text_type, \
with_metaclass

from iota.exceptions import with_context
from iota.json import JsonEncoder
from six import PY2, binary_type, moves as compat, text_type, with_metaclass

__all__ = [
'API_VERSION',
'AdapterSpec',
'BadApiResponse',
'InvalidUri',
Expand All @@ -30,6 +33,13 @@
__all__ = map(binary_type, __all__)


API_VERSION = '1'
"""
API protocol version.
https://github.com/iotaledger/iota.lib.py/issues/84
"""


# Custom types for type hints and docstrings.
AdapterSpec = Union[Text, 'BaseAdapter']

Expand Down Expand Up @@ -201,6 +211,18 @@ class HttpAdapter(BaseAdapter):
"""
supported_protocols = ('http', 'https',)

DEFAULT_HEADERS = {
'Content-type': 'application/json',

# https://github.com/iotaledger/iota.lib.py/issues/84
'X-IOTA-API-Version': API_VERSION,
}
"""
Default headers sent with every request.
These can be overridden on a per-request basis, by specifying values
in the ``headers`` kwarg.
"""

def __init__(self, uri):
# type: (Union[Text, SplitResult]) -> None
super(HttpAdapter, self).__init__()
Expand Down Expand Up @@ -265,7 +287,8 @@ def get_uri(self):
def send_request(self, payload, **kwargs):
# type: (dict, dict) -> dict
kwargs.setdefault('headers', {})
kwargs['headers']['Content-type'] = 'application/json'
for key, value in iteritems(self.DEFAULT_HEADERS):
kwargs['headers'].setdefault(key, value)

response = self._send_http_request(
# Use a custom JSON encoder that knows how to convert Tryte values.
Expand Down
15 changes: 2 additions & 13 deletions iota/crypto/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from iota.crypto.kerl import Kerl
from iota.crypto.types import PrivateKey, Seed
from iota.exceptions import with_context
from iota.trits import add_trits, trits_from_int

__all__ = [
'KeyGenerator',
Expand Down Expand Up @@ -307,20 +308,8 @@ def _create_sponge(self, index):
"""
seed = self.seed_as_trits[:]

for i in range(index):
# Treat ``seed`` like a really big number and add ``index``.
# Note that addition works a little bit differently in balanced
# ternary.
for j in range(len(seed)):
seed[j] += 1

if seed[j] > 1:
seed[j] = -1
else:
break

sponge = Kerl()
sponge.absorb(seed)
sponge.absorb(add_trits(seed, trits_from_int(index)))

# Squeeze all of the trits out of the sponge and re-absorb them.
# Note that the sponge transforms several times per operation, so
Expand Down
4 changes: 2 additions & 2 deletions iota/transaction/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from iota.json import JsonSerializable
from iota.transaction.types import BundleHash, Fragment, Nonce, TransactionHash, \
TransactionTrytes
from iota.types import Address, Tag, TryteString, TrytesCompatible, \
int_from_trits, trits_from_int
from iota.trits import int_from_trits, trits_from_int
from iota.types import Address, Tag, TryteString, TrytesCompatible

__all__ = [
'Bundle',
Expand Down
47 changes: 35 additions & 12 deletions iota/transaction/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

from iota.crypto import HASH_LENGTH
from iota.crypto.kerl import Kerl
from iota.crypto.signing import KeyGenerator
from iota.crypto.signing import KeyGenerator, normalize
from iota.crypto.types import PrivateKey
from iota.exceptions import with_context
from iota.transaction.base import Bundle, Transaction
from iota.transaction.types import BundleHash, Fragment, TransactionHash, Nonce
from iota.transaction.types import BundleHash, Fragment, Nonce, TransactionHash
from iota.transaction.utils import get_current_timestamp
from iota.types import Address, Hash, Tag, TryteString
from iota.trits import add_trits
from iota.types import Address, Tag, TryteString

__all__ = [
'ProposedBundle',
Expand Down Expand Up @@ -83,6 +84,17 @@ def as_tryte_string(self):

return super(ProposedTransaction, self).as_tryte_string()

def increment_legacy_tag(self):
"""
Increments the transaction's legacy tag, used to fix insecure
bundle hashes when finalizing a bundle.
References:
- https://github.com/iotaledger/iota.lib.py/issues/84
"""
self._legacy_tag =\
Tag.from_trits(add_trits(self.legacy_tag.as_trits(), [1]))


Transfer = ProposedTransaction
"""
Expand Down Expand Up @@ -322,20 +334,31 @@ def finalize(self):
)

# Generate bundle hash.
sponge = Kerl()
last_index = len(self) - 1
while True:
sponge = Kerl()
last_index = len(self) - 1

for (i, txn) in enumerate(self): # type: Tuple[int, ProposedTransaction]
txn.current_index = i
txn.last_index = last_index

for (i, txn) in enumerate(self): # type: Tuple[int, ProposedTransaction]
txn.current_index = i
txn.last_index = last_index
sponge.absorb(txn.get_signature_validation_trytes().as_trits())

sponge.absorb(txn.get_signature_validation_trytes().as_trits())
bundle_hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int]
sponge.squeeze(bundle_hash_trits)

bundle_hash_trits = [0] * HASH_LENGTH # type: MutableSequence[int]
sponge.squeeze(bundle_hash_trits)
bundle_hash = BundleHash.from_trits(bundle_hash_trits)

# Check that we generated a secure bundle hash.
# https://github.com/iotaledger/iota.lib.py/issues/84
if any(13 in part for part in normalize(bundle_hash)):
# Increment the legacy tag and try again.
tail_transaction = self.tail_transaction # type: ProposedTransaction
tail_transaction.increment_legacy_tag()
else:
break

# Copy bundle hash to individual transactions.
bundle_hash = BundleHash.from_trits(bundle_hash_trits)
for txn in self:
txn.bundle_hash = bundle_hash

Expand Down
128 changes: 128 additions & 0 deletions iota/trits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# coding=utf-8
"""
Provides functions for manipulating sequences of trits.
Based on:
https://github.com/iotaledger/iota.lib.js/blob/v0.4.2/lib/crypto/helpers/adder.js
"""

from __future__ import absolute_import, division, print_function, \
unicode_literals

from typing import Iterable, List, Optional, Sequence, Tuple

__all__ = [
'add_trits',
'int_from_trits',
'trits_from_int',
]


def add_trits(left, right):
# type: (Sequence[int], Sequence[int]) -> List[int]
"""
Adds two sequences of trits together.
The result is a list of trits equal in length to the longer of the
two sequences.
Note: Overflow is possible.
For example, ``add_trits([1], [1])`` returns ``[-1]``.
"""
target_len = max(len(left), len(right))

res = [0] * target_len
left += [0] * (target_len - len(left))
right += [0] * (target_len - len(right))

carry = 0
for i in range(len(res)):
res[i], carry = _full_add_trits(left[i], right[i], carry)

return res


def int_from_trits(trits):
# type: (Iterable[int]) -> int
"""
Converts a sequence of trits into an integer value.
"""
# Normally we'd have to wrap ``enumerate`` inside ``reversed``, but
# balanced ternary puts least significant digits first.
return sum(base * (3 ** power) for power, base in enumerate(trits))


def trits_from_int(n, pad=1):
# type: (int, Optional[int]) -> List[int]
"""
Returns a trit representation of an integer value.
:param n:
Integer value to convert.
:param pad:
Ensure the result has at least this many trits.
References:
- https://dev.to/buntine/the-balanced-ternary-machines-of-soviet-russia
- https://en.wikipedia.org/wiki/Balanced_ternary
- https://rosettacode.org/wiki/Balanced_ternary#Python
"""
if n == 0:
trits = []
else:
quotient, remainder = divmod(n, 3)

if remainder == 2:
# Lend 1 to the next place so we can make this trit negative.
quotient += 1
remainder = -1

trits = [remainder] + trits_from_int(quotient, pad=0)

if pad:
trits += [0] * max(0, pad - len(trits))

return trits


def _cons_trits(left, right):
# type: (int, int) -> int
"""
Compares two trits. If they have the same value, returns that value.
Otherwise, returns 0.
"""
return left if left == right else 0


def _add_trits(left, right):
# type: (int, int) -> int
"""
Adds two individual trits together.
The result is always a single trit.
"""
res = left + right
return res if -2 < res < 2 else (res < 0) - (res > 0)


def _any_trits(left, right):
# type: (int, int) -> int
"""
Adds two individual trits together and returns a single trit
indicating whether the result is positive or negative.
"""
res = left + right
return (res > 0) - (res < 0)


def _full_add_trits(left, right, carry):
# type: (int, int, int) -> Tuple[int, int]
"""
Adds two trits together, with support for a carry trit.
"""
sum_both = _add_trits(left, right)
cons_left = _cons_trits(left, right)
cons_right = _cons_trits(sum_both, carry)

return _add_trits(sum_both, carry), _any_trits(cons_left, cons_right)
47 changes: 1 addition & 46 deletions iota/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from iota.crypto.kerl import Kerl
from iota.exceptions import with_context
from iota.json import JsonSerializable
from iota.trits import int_from_trits, trits_from_int

__all__ = [
'Address',
Expand All @@ -25,59 +26,13 @@
'Tag',
'TryteString',
'TrytesCompatible',
'int_from_trits',
'trits_from_int',
]


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


def trits_from_int(n, pad=1):
# type: (int, Optional[int]) -> List[int]
"""
Returns a trit representation of an integer value.
:param n:
Integer value to convert.
:param pad:
Ensure the result has at least this many trits.
References:
- https://dev.to/buntine/the-balanced-ternary-machines-of-soviet-russia
- https://en.wikipedia.org/wiki/Balanced_ternary
- https://rosettacode.org/wiki/Balanced_ternary#Python
"""
if n == 0:
trits = []
else:
quotient, remainder = divmod(n, 3)

if remainder == 2:
# Lend 1 to the next place so we can make this trit negative.
quotient += 1
remainder = -1

trits = [remainder] + trits_from_int(quotient, pad=0)

if pad:
trits += [0] * max(0, pad - len(trits))

return trits


def int_from_trits(trits):
# type: (Iterable[int]) -> int
"""
Converts a sequence of trits into an integer value.
"""
# Normally we'd have to wrap ``enumerate`` inside ``reversed``, but
# balanced ternary puts least significant digits first.
return sum(base * (3 ** power) for power, base in enumerate(trits))


@python_2_unicode_compatible
class TryteString(JsonSerializable):
"""
Expand Down

0 comments on commit f8dd41c

Please sign in to comment.