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

Commit 4f40ffa

Browse files
authored
Merge pull request #68 from iotaledger/feature/kerl
Feature/kerl
2 parents 5b16f2b + 62e0802 commit 4f40ffa

33 files changed

+32189
-2863
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
dist: trusty
12
language: python
23
python:
34
- '2.7'

iota/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def default_min_weight_magnitude(self):
133133
Returns the default ``min_weight_magnitude`` value to use for API
134134
requests.
135135
"""
136-
return 9 if self.testnet else 15
136+
return 9 if self.testnet else 14
137137

138138
def add_neighbors(self, uris):
139139
# type: (Iterable[Text]) -> dict

iota/commands/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44

55
from abc import ABCMeta, abstractmethod as abstract_method
66
from importlib import import_module
7-
from inspect import isabstract as is_abstract, isclass as is_class, \
8-
getmembers as get_members
7+
from inspect import getmembers as get_members, isabstract as is_abstract, \
8+
isclass as is_class
99
from pkgutil import walk_packages
1010
from types import ModuleType
11-
from typing import Dict, Mapping, Optional, Text, Union
11+
from typing import Any, Dict, Mapping, Optional, Text, Union
1212

1313
import filters as f
14-
from iota.exceptions import with_context
15-
from six import with_metaclass, string_types
14+
from six import string_types, with_metaclass
1615

1716
from iota.adapter import BaseAdapter
17+
from iota.exceptions import with_context
1818

1919
__all__ = [
2020
'BaseCommand',
@@ -49,10 +49,14 @@ def discover_commands(package, recursively=True):
4949

5050
commands = {}
5151

52-
for _, name, is_package in walk_packages(package.__path__):
52+
for _, name, is_package in walk_packages(package.__path__, package.__name__ + '.'):
5353
# Loading the module is good enough; the CommandMeta metaclass will
5454
# ensure that any commands in the module get registered.
55-
sub_package = import_module(package.__name__ + '.' + name)
55+
56+
# Prefix in name module move to function "walk_packages" for fix
57+
# conflict with names importing packages
58+
# Bug https://github.com/iotaledger/iota.lib.py/issues/63
59+
sub_package = import_module(name)
5660

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

101105
def __call__(self, **kwargs):
102-
# type: (dict) -> dict
106+
# type: (**Any) -> dict
103107
"""
104108
Sends the command to the node.
105109
"""

iota/commands/extended/get_new_addresses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
from __future__ import absolute_import, division, print_function, \
33
unicode_literals
44

5-
from typing import Optional, List
5+
from typing import List, Optional
66

77
import filters as f
8-
from iota import Address
98

9+
from iota import Address
1010
from iota.commands import FilterCommand, RequestFilter
1111
from iota.commands.core.find_transactions import FindTransactionsCommand
1212
from iota.crypto.addresses import AddressGenerator

iota/crypto/addresses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Generator, Iterable, List, MutableSequence
66

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

160-
sponge = Curl()
160+
sponge = Kerl()
161161
sponge.absorb(digest.as_trits())
162162
sponge.squeeze(address_trits)
163163

iota/crypto/kerl/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# coding=utf-8
2+
from __future__ import absolute_import, division, print_function, \
3+
unicode_literals
4+
5+
from .pykerl import *

iota/crypto/kerl/conv.py

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# coding=utf-8
2+
from __future__ import absolute_import, division, print_function, \
3+
unicode_literals
4+
5+
6+
BYTE_HASH_LENGTH = 48
7+
TRIT_HASH_LENGTH = 243
8+
9+
tryte_table = {
10+
'9': [ 0, 0, 0], # 0
11+
'A': [ 1, 0, 0], # 1
12+
'B': [-1, 1, 0], # 2
13+
'C': [ 0, 1, 0], # 3
14+
'D': [ 1, 1, 0], # 4
15+
'E': [-1, -1, 1], # 5
16+
'F': [ 0, -1, 1], # 6
17+
'G': [ 1, -1, 1], # 7
18+
'H': [-1, 0, 1], # 8
19+
'I': [ 0, 0, 1], # 9
20+
'J': [ 1, 0, 1], # 10
21+
'K': [-1, 1, 1], # 11
22+
'L': [ 0, 1, 1], # 12
23+
'M': [ 1, 1, 1], # 13
24+
'N': [-1, -1, -1], # -13
25+
'O': [ 0, -1, -1], # -12
26+
'P': [ 1, -1, -1], # -11
27+
'Q': [-1, 0, -1], # -10
28+
'R': [ 0, 0, -1], # -9
29+
'S': [ 1, 0, -1], # -8
30+
'T': [-1, 1, -1], # -7
31+
'U': [ 0, 1, -1], # -6
32+
'V': [ 1, 1, -1], # -5
33+
'W': [-1, -1, 0], # -4
34+
'X': [ 0, -1, 0], # -3
35+
'Y': [ 1, -1, 0], # -2
36+
'Z': [-1, 0, 0], # -1
37+
}
38+
39+
# Invert for trit -> tryte lookup
40+
trit_table = {tuple(v): k for k, v in tryte_table.items()}
41+
42+
def trytes_to_trits(trytes):
43+
trits = []
44+
for tryte in trytes:
45+
trits.extend(tryte_table[tryte])
46+
47+
return trits
48+
49+
def trits_to_trytes(trits):
50+
trytes = []
51+
trits_chunks = [trits[i:i + 3] for i in range(0, len(trits), 3)]
52+
53+
for trit in trits_chunks:
54+
trytes.extend(trit_table[tuple(trit)])
55+
56+
return ''.join(trytes)
57+
58+
def convertToTrits(bytes_k):
59+
bigInt = convertBytesToBigInt(bytes_k)
60+
trits = convertBigintToBase(bigInt, 3, TRIT_HASH_LENGTH)
61+
return trits
62+
63+
def convertToBytes(trits):
64+
bigInt = convertBaseToBigint(trits, 3)
65+
bytes_k = convertBigintToBytes(bigInt)
66+
return bytes_k
67+
68+
def convertBytesToBigInt(ba):
69+
# copy of array
70+
bytesArray = list(map(lambda x: x, ba))
71+
72+
# number sign in MSB
73+
signum = (1 if bytesArray[0] >= 0 else -1)
74+
75+
if signum == -1:
76+
# sub1
77+
for pos in reversed(range(len(bytesArray))):
78+
sub = (bytesArray[pos] & 0xFF) - 1
79+
bytesArray[pos] = (sub if sub <= 0x7F else sub - 0x100)
80+
if bytesArray[pos] != -1:
81+
break
82+
83+
# 1-compliment
84+
bytesArray = list(map(lambda x: ~x, bytesArray))
85+
86+
# sum magnitudes and set sign
87+
return sum((x & 0xFF) << pos * 8 for (pos, x) in
88+
enumerate(reversed(bytesArray))) * signum
89+
90+
91+
def convertBigintToBytes(big):
92+
bytesArrayTemp = [(abs(big) >> pos * 8) % (1 << 8) for pos in
93+
range(48)]
94+
95+
# big endian and balanced
96+
bytesArray = list(map(lambda x: (x if x <= 0x7F else x - 0x100),
97+
reversed(bytesArrayTemp)))
98+
99+
if big < 0:
100+
# 1-compliment
101+
bytesArray = list(map(lambda x: ~x, bytesArray))
102+
103+
# add1
104+
for pos in reversed(range(len(bytesArray))):
105+
add = (bytesArray[pos] & 0xFF) + 1
106+
bytesArray[pos] = (add if add <= 0x7F else add - 0x100)
107+
if bytesArray[pos] != 0:
108+
break
109+
110+
return bytesArray
111+
112+
def convertBaseToBigint(array, base):
113+
bigint = 0
114+
115+
for i in range(len(array)):
116+
bigint += array[i] * (base ** i)
117+
118+
return bigint
119+
120+
def convertBigintToBase(bigInt, base, length):
121+
result = []
122+
123+
is_negative = bigInt < 0
124+
quotient = abs(bigInt)
125+
126+
MAX = (base-1) // 2
127+
if is_negative:
128+
MAX = base // 2
129+
130+
for i in range(length):
131+
quotient, remainder = divmod(quotient, base)
132+
133+
if remainder > MAX:
134+
# Lend 1 to the next place so we can make this digit negative.
135+
quotient += 1
136+
remainder -= base
137+
138+
if is_negative:
139+
remainder = remainder * -1
140+
141+
result.append(remainder)
142+
143+
return result
144+
145+
def convert_sign(byte):
146+
"""
147+
Convert between signed and unsigned bytes
148+
"""
149+
if byte < 0:
150+
return 256 + byte
151+
elif byte > 127:
152+
return -256 + byte
153+
return byte

iota/crypto/kerl/pykerl.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# coding=utf-8
2+
from __future__ import absolute_import, division, print_function, \
3+
unicode_literals
4+
5+
from sha3 import keccak_384
6+
from six import PY2
7+
from typing import MutableSequence, Optional
8+
9+
from iota.crypto.kerl import conv
10+
from iota.exceptions import with_context
11+
12+
__all__ = [
13+
'Kerl',
14+
]
15+
16+
BYTE_HASH_LENGTH = 48
17+
TRIT_HASH_LENGTH = 243
18+
19+
class Kerl(object):
20+
k = None # type: keccak_384
21+
22+
def __init__(self):
23+
self.reset()
24+
25+
def absorb(self, trits, offset=0, length=None):
26+
# type: (MutableSequence[int], int, Optional[int]) -> None
27+
"""
28+
Absorb trits into the sponge from a buffer.
29+
30+
:param trits:
31+
Buffer that contains the trits to absorb.
32+
33+
:param offset:
34+
Starting offset in ``trits``.
35+
36+
:param length:
37+
Number of trits to absorb. Defaults to ``len(trits)``.
38+
"""
39+
# Pad input if necessary, so that it can be divided evenly into
40+
# hashes.
41+
# Note that this operation creates a COPY of ``trits``; the
42+
# incoming buffer is not modified!
43+
pad = ((len(trits) % TRIT_HASH_LENGTH) or TRIT_HASH_LENGTH)
44+
trits += [0] * (TRIT_HASH_LENGTH - pad)
45+
46+
if length is None:
47+
length = len(trits)
48+
49+
if length < 1:
50+
raise with_context(
51+
exc = ValueError('Invalid length passed to ``absorb``.'),
52+
53+
context = {
54+
'trits': trits,
55+
'offset': offset,
56+
'length': length,
57+
},
58+
)
59+
60+
while offset < length:
61+
stop = min(offset + TRIT_HASH_LENGTH, length)
62+
63+
# If we're copying over a full chunk, zero last trit
64+
if stop - offset == TRIT_HASH_LENGTH:
65+
trits[stop - 1] = 0
66+
67+
signed_nums = conv.convertToBytes(trits[offset:stop])
68+
69+
# Convert signed bytes into their equivalent unsigned representation
70+
# In order to use Python's built-in bytes type
71+
unsigned_bytes = bytearray(conv.convert_sign(b) for b in signed_nums)
72+
73+
self.k.update(unsigned_bytes)
74+
75+
offset += TRIT_HASH_LENGTH
76+
77+
def squeeze(self, trits, offset=0, length=None):
78+
# type: (MutableSequence[int], int, Optional[int]) -> None
79+
"""
80+
Squeeze trits from the sponge into a buffer.
81+
82+
:param trits:
83+
Buffer that will hold the squeezed trits.
84+
85+
IMPORTANT: If ``trits`` is too small, it will be extended!
86+
87+
:param offset:
88+
Starting offset in ``trits``.
89+
90+
:param length:
91+
Number of trits to squeeze from the sponge.
92+
93+
If not specified, defaults to :py:data:`TRIT_HASH_LENGTH` (i.e.,
94+
by default, we will try to squeeze exactly 1 hash).
95+
"""
96+
# Pad input if necessary, so that it can be divided evenly into
97+
# hashes.
98+
pad = ((len(trits) % TRIT_HASH_LENGTH) or TRIT_HASH_LENGTH)
99+
trits += [0] * (TRIT_HASH_LENGTH - pad)
100+
101+
if length is None:
102+
# By default, we will try to squeeze one hash.
103+
# Note that this is different than ``absorb``.
104+
length = len(trits) or TRIT_HASH_LENGTH
105+
106+
if length < 1:
107+
raise with_context(
108+
exc = ValueError('Invalid length passed to ``squeeze``.'),
109+
110+
context = {
111+
'trits': trits,
112+
'offset': offset,
113+
'length': length,
114+
},
115+
)
116+
117+
while offset < length:
118+
unsigned_hash = self.k.digest()
119+
120+
if PY2:
121+
unsigned_hash = map(ord, unsigned_hash) # type: ignore
122+
123+
signed_hash = [conv.convert_sign(b) for b in unsigned_hash]
124+
125+
trits_from_hash = conv.convertToTrits(signed_hash)
126+
trits_from_hash[TRIT_HASH_LENGTH - 1] = 0
127+
128+
stop = min(TRIT_HASH_LENGTH, length-offset)
129+
trits[offset:offset+stop] = trits_from_hash[0:stop]
130+
131+
flipped_bytes = bytearray(conv.convert_sign(~b) for b in unsigned_hash)
132+
133+
# Reset internal state before feeding back in
134+
self.reset()
135+
self.k.update(flipped_bytes)
136+
137+
offset += TRIT_HASH_LENGTH
138+
139+
def reset(self):
140+
self.k = keccak_384()

0 commit comments

Comments
 (0)