Skip to content
This repository was archived by the owner on Jan 13, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
85bbc7d
Universal wheels again (:
todofixthis Aug 16, 2017
9e1154d
Quick and dirty PyKerl install.
todofixthis Aug 16, 2017
24b4913
Unified Kerl impl in py2k and py3k.
todofixthis Aug 16, 2017
d6b1437
[UNSTABLE] Key/Addy gen now uses kerl.
todofixthis Aug 21, 2017
f1aee1b
[UNSTABLE][#61] s/Curl/Kerl/g
todofixthis Aug 22, 2017
8caf912
[#61] Fixed address checksum generation.
todofixthis Aug 25, 2017
75a7ce3
[UNSTABLE][#61] Addressed some failing unit tests.
todofixthis Sep 1, 2017
623d3bd
[UNSTABLE][#61] Addressed some unit test failures.
todofixthis Sep 1, 2017
68f6beb
Fix conflict with names when importing library.
Sep 4, 2017
0ecff95
[#61] Tx hash uses Curl, not Kerl.
todofixthis Sep 9, 2017
0ad34ca
[#61] Add support for old validation.
todofixthis Sep 9, 2017
20f91ed
[#61] BundleValidator Kerl vs. Curl s/b all-or-nothing.
todofixthis Sep 9, 2017
dbbe53b
Fixed incorrect type hint.
todofixthis Sep 15, 2017
91344c5
[#61] Addressed some unit test failures.
todofixthis Sep 15, 2017
830905f
[#61] Addressed some failing tests.
todofixthis Sep 15, 2017
a7105a3
[#61] Addressed failing unit tests.
todofixthis Sep 16, 2017
b41df60
[#61] Added instructions for validating signing tests.
todofixthis Sep 16, 2017
9822dd7
[#61] Addressed some failing unit tests.
todofixthis Sep 22, 2017
4dd7295
[#61] Added instructions for generating validation data.
todofixthis Sep 22, 2017
aaaf3ad
[#61] Fixed incorrect validation data instructions.
todofixthis Sep 22, 2017
42e3231
[#61] Upgraded to Curl-P-81.
todofixthis Sep 23, 2017
9c85e20
[#61] Started refactoring `prepareTransfer` tests.
todofixthis Sep 24, 2017
ccea099
[#61] Additional fixes for `prepareTransfer` unit tests.
todofixthis Sep 24, 2017
87c197a
[#61] Refactored `test_pass_inputs_explicit_with_change`.
todofixthis Sep 24, 2017
363e0aa
[#61] Refactored `test_pass_inputs_implicit_no_change`.
todofixthis Sep 24, 2017
6d67811
[#61] Refactored `test_pass_inputs_implicit_with_change`.
todofixthis Sep 24, 2017
ef3a641
[#61] Refactored `test_pass_change_address_auto_generated`.
todofixthis Sep 24, 2017
6c13f95
[#61] Refactored `test_pass_message_short`.
todofixthis Sep 24, 2017
5cfe392
[#61] Refactored `test_pass_message_long`.
todofixthis Sep 24, 2017
094b53e
[#61] Refactored `PrivateKey` digest tests.
todofixthis Sep 24, 2017
a6ecf24
[#61] Fix incorrect keys when seed is longer than 81 trytes.
todofixthis Sep 24, 2017
76e72d0
[#61] Refactored `BundleValidatorTestCase`.
todofixthis Sep 24, 2017
aaf3080
[#61] Regenerated test values for `BundleValidatorMultisigTestCase`.
todofixthis Sep 24, 2017
5bcdc31
Merge pull request #65 from labovich/fix_importing_packages
todofixthis Sep 24, 2017
62e0802
Removed debug code.
todofixthis Sep 24, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist: trusty
language: python
python:
- '2.7'
Expand Down
2 changes: 1 addition & 1 deletion iota/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def default_min_weight_magnitude(self):
Returns the default ``min_weight_magnitude`` value to use for API
requests.
"""
return 9 if self.testnet else 15
return 9 if self.testnet else 14

def add_neighbors(self, uris):
# type: (Iterable[Text]) -> dict
Expand Down
20 changes: 12 additions & 8 deletions iota/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@

from abc import ABCMeta, abstractmethod as abstract_method
from importlib import import_module
from inspect import isabstract as is_abstract, isclass as is_class, \
getmembers as get_members
from inspect import getmembers as get_members, isabstract as is_abstract, \
isclass as is_class
from pkgutil import walk_packages
from types import ModuleType
from typing import Dict, Mapping, Optional, Text, Union
from typing import Any, Dict, Mapping, Optional, Text, Union

import filters as f
from iota.exceptions import with_context
from six import with_metaclass, string_types
from six import string_types, with_metaclass

from iota.adapter import BaseAdapter
from iota.exceptions import with_context

__all__ = [
'BaseCommand',
Expand Down Expand Up @@ -49,10 +49,14 @@ def discover_commands(package, recursively=True):

commands = {}

for _, name, is_package in walk_packages(package.__path__):
for _, name, is_package in walk_packages(package.__path__, package.__name__ + '.'):
# Loading the module is good enough; the CommandMeta metaclass will
# ensure that any commands in the module get registered.
sub_package = import_module(package.__name__ + '.' + name)

# Prefix in name module move to function "walk_packages" for fix
# conflict with names importing packages
# Bug https://github.com/iotaledger/iota.lib.py/issues/63
sub_package = import_module(name)

# Index any command classes that we find.
for (_, obj) in get_members(sub_package):
Expand Down Expand Up @@ -99,7 +103,7 @@ def __init__(self, adapter):
self.response = None # type: dict

def __call__(self, **kwargs):
# type: (dict) -> dict
# type: (**Any) -> dict
"""
Sends the command to the node.
"""
Expand Down
4 changes: 2 additions & 2 deletions iota/commands/extended/get_new_addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
from __future__ import absolute_import, division, print_function, \
unicode_literals

from typing import Optional, List
from typing import List, Optional

import filters as f
from iota import Address

from iota import Address
from iota.commands import FilterCommand, RequestFilter
from iota.commands.core.find_transactions import FindTransactionsCommand
from iota.crypto.addresses import AddressGenerator
Expand Down
4 changes: 2 additions & 2 deletions iota/crypto/addresses.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import Generator, Iterable, List, MutableSequence

from iota import Address, TRITS_PER_TRYTE, TrytesCompatible
from iota.crypto import Curl
from iota.crypto.kerl import Kerl
from iota.crypto.signing import KeyGenerator, KeyIterator
from iota.crypto.types import Digest, PrivateKey, Seed
from iota.exceptions import with_context
Expand Down Expand Up @@ -157,7 +157,7 @@ def address_from_digest(digest):
"""
address_trits = [0] * (Address.LEN * TRITS_PER_TRYTE) # type: MutableSequence[int]

sponge = Curl()
sponge = Kerl()
sponge.absorb(digest.as_trits())
sponge.squeeze(address_trits)

Expand Down
5 changes: 5 additions & 0 deletions iota/crypto/kerl/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function, \
unicode_literals

from .pykerl import *
153 changes: 153 additions & 0 deletions iota/crypto/kerl/conv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function, \
unicode_literals


BYTE_HASH_LENGTH = 48
TRIT_HASH_LENGTH = 243

tryte_table = {
'9': [ 0, 0, 0], # 0
'A': [ 1, 0, 0], # 1
'B': [-1, 1, 0], # 2
'C': [ 0, 1, 0], # 3
'D': [ 1, 1, 0], # 4
'E': [-1, -1, 1], # 5
'F': [ 0, -1, 1], # 6
'G': [ 1, -1, 1], # 7
'H': [-1, 0, 1], # 8
'I': [ 0, 0, 1], # 9
'J': [ 1, 0, 1], # 10
'K': [-1, 1, 1], # 11
'L': [ 0, 1, 1], # 12
'M': [ 1, 1, 1], # 13
'N': [-1, -1, -1], # -13
'O': [ 0, -1, -1], # -12
'P': [ 1, -1, -1], # -11
'Q': [-1, 0, -1], # -10
'R': [ 0, 0, -1], # -9
'S': [ 1, 0, -1], # -8
'T': [-1, 1, -1], # -7
'U': [ 0, 1, -1], # -6
'V': [ 1, 1, -1], # -5
'W': [-1, -1, 0], # -4
'X': [ 0, -1, 0], # -3
'Y': [ 1, -1, 0], # -2
'Z': [-1, 0, 0], # -1
}

# Invert for trit -> tryte lookup
trit_table = {tuple(v): k for k, v in tryte_table.items()}

def trytes_to_trits(trytes):
trits = []
for tryte in trytes:
trits.extend(tryte_table[tryte])

return trits

def trits_to_trytes(trits):
trytes = []
trits_chunks = [trits[i:i + 3] for i in range(0, len(trits), 3)]

for trit in trits_chunks:
trytes.extend(trit_table[tuple(trit)])

return ''.join(trytes)

def convertToTrits(bytes_k):
bigInt = convertBytesToBigInt(bytes_k)
trits = convertBigintToBase(bigInt, 3, TRIT_HASH_LENGTH)
return trits

def convertToBytes(trits):
bigInt = convertBaseToBigint(trits, 3)
bytes_k = convertBigintToBytes(bigInt)
return bytes_k

def convertBytesToBigInt(ba):
# copy of array
bytesArray = list(map(lambda x: x, ba))

# number sign in MSB
signum = (1 if bytesArray[0] >= 0 else -1)

if signum == -1:
# sub1
for pos in reversed(range(len(bytesArray))):
sub = (bytesArray[pos] & 0xFF) - 1
bytesArray[pos] = (sub if sub <= 0x7F else sub - 0x100)
if bytesArray[pos] != -1:
break

# 1-compliment
bytesArray = list(map(lambda x: ~x, bytesArray))

# sum magnitudes and set sign
return sum((x & 0xFF) << pos * 8 for (pos, x) in
enumerate(reversed(bytesArray))) * signum


def convertBigintToBytes(big):
bytesArrayTemp = [(abs(big) >> pos * 8) % (1 << 8) for pos in
range(48)]

# big endian and balanced
bytesArray = list(map(lambda x: (x if x <= 0x7F else x - 0x100),
reversed(bytesArrayTemp)))

if big < 0:
# 1-compliment
bytesArray = list(map(lambda x: ~x, bytesArray))

# add1
for pos in reversed(range(len(bytesArray))):
add = (bytesArray[pos] & 0xFF) + 1
bytesArray[pos] = (add if add <= 0x7F else add - 0x100)
if bytesArray[pos] != 0:
break

return bytesArray

def convertBaseToBigint(array, base):
bigint = 0

for i in range(len(array)):
bigint += array[i] * (base ** i)

return bigint

def convertBigintToBase(bigInt, base, length):
result = []

is_negative = bigInt < 0
quotient = abs(bigInt)

MAX = (base-1) // 2
if is_negative:
MAX = base // 2

for i in range(length):
quotient, remainder = divmod(quotient, base)

if remainder > MAX:
# Lend 1 to the next place so we can make this digit negative.
quotient += 1
remainder -= base

if is_negative:
remainder = remainder * -1

result.append(remainder)

return result

def convert_sign(byte):
"""
Convert between signed and unsigned bytes
"""
if byte < 0:
return 256 + byte
elif byte > 127:
return -256 + byte
return byte
140 changes: 140 additions & 0 deletions iota/crypto/kerl/pykerl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function, \
unicode_literals

from sha3 import keccak_384
from six import PY2
from typing import MutableSequence, Optional

from iota.crypto.kerl import conv
from iota.exceptions import with_context

__all__ = [
'Kerl',
]

BYTE_HASH_LENGTH = 48
TRIT_HASH_LENGTH = 243

class Kerl(object):
k = None # type: keccak_384

def __init__(self):
self.reset()

def absorb(self, trits, offset=0, length=None):
# type: (MutableSequence[int], int, Optional[int]) -> None
"""
Absorb trits into the sponge from a buffer.

:param trits:
Buffer that contains the trits to absorb.

:param offset:
Starting offset in ``trits``.

:param length:
Number of trits to absorb. Defaults to ``len(trits)``.
"""
# Pad input if necessary, so that it can be divided evenly into
# hashes.
# Note that this operation creates a COPY of ``trits``; the
# incoming buffer is not modified!
pad = ((len(trits) % TRIT_HASH_LENGTH) or TRIT_HASH_LENGTH)
trits += [0] * (TRIT_HASH_LENGTH - pad)

if length is None:
length = len(trits)

if length < 1:
raise with_context(
exc = ValueError('Invalid length passed to ``absorb``.'),

context = {
'trits': trits,
'offset': offset,
'length': length,
},
)

while offset < length:
stop = min(offset + TRIT_HASH_LENGTH, length)

# If we're copying over a full chunk, zero last trit
if stop - offset == TRIT_HASH_LENGTH:
trits[stop - 1] = 0

signed_nums = conv.convertToBytes(trits[offset:stop])

# Convert signed bytes into their equivalent unsigned representation
# In order to use Python's built-in bytes type
unsigned_bytes = bytearray(conv.convert_sign(b) for b in signed_nums)

self.k.update(unsigned_bytes)

offset += TRIT_HASH_LENGTH

def squeeze(self, trits, offset=0, length=None):
# type: (MutableSequence[int], int, Optional[int]) -> None
"""
Squeeze trits from the sponge into a buffer.

:param trits:
Buffer that will hold the squeezed trits.

IMPORTANT: If ``trits`` is too small, it will be extended!

:param offset:
Starting offset in ``trits``.

:param length:
Number of trits to squeeze from the sponge.

If not specified, defaults to :py:data:`TRIT_HASH_LENGTH` (i.e.,
by default, we will try to squeeze exactly 1 hash).
"""
# Pad input if necessary, so that it can be divided evenly into
# hashes.
pad = ((len(trits) % TRIT_HASH_LENGTH) or TRIT_HASH_LENGTH)
trits += [0] * (TRIT_HASH_LENGTH - pad)

if length is None:
# By default, we will try to squeeze one hash.
# Note that this is different than ``absorb``.
length = len(trits) or TRIT_HASH_LENGTH

if length < 1:
raise with_context(
exc = ValueError('Invalid length passed to ``squeeze``.'),

context = {
'trits': trits,
'offset': offset,
'length': length,
},
)

while offset < length:
unsigned_hash = self.k.digest()

if PY2:
unsigned_hash = map(ord, unsigned_hash) # type: ignore

signed_hash = [conv.convert_sign(b) for b in unsigned_hash]

trits_from_hash = conv.convertToTrits(signed_hash)
trits_from_hash[TRIT_HASH_LENGTH - 1] = 0

stop = min(TRIT_HASH_LENGTH, length-offset)
trits[offset:offset+stop] = trits_from_hash[0:stop]

flipped_bytes = bytearray(conv.convert_sign(~b) for b in unsigned_hash)

# Reset internal state before feeding back in
self.reset()
self.k.update(flipped_bytes)

offset += TRIT_HASH_LENGTH

def reset(self):
self.k = keccak_384()
Loading