From ef183f1880328e4d1a9bb117cf09b604d0d70a2f Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Thu, 9 Nov 2017 17:34:31 +0800 Subject: [PATCH 1/7] Add offset and length to pycurl pycurl.Curl.absorb: Add offset and length pycurl.Curl.squeeze: Add offset sequeeze not added with length, is because it will only copy one hash to trits, therefore no need to add length --- iota/crypto/pycurl.py | 46 +++++++++++++--- test/crypto/pycurl_test.py | 107 +++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 test/crypto/pycurl_test.py diff --git a/iota/crypto/pycurl.py b/iota/crypto/pycurl.py index ce2337e6..925bdf9a 100644 --- a/iota/crypto/pycurl.py +++ b/iota/crypto/pycurl.py @@ -4,6 +4,8 @@ from typing import List, MutableSequence, Optional, Sequence +from iota.exceptions import with_context + __all__ = [ 'Curl', 'HASH_LENGTH', @@ -59,16 +61,35 @@ def reset(self): """ self._state = [0] * STATE_LENGTH # type: List[int] - def absorb(self, trits): - # type: (Sequence[int], Optional[int]) -> None + def absorb(self, trits, offset=0, length=None): + # type: (Sequence[int], int, Optional[int]) -> None """ Absorb trits into the sponge. :param trits: Sequence of trits to absorb. + + :param offset: + Starting offset in ``trits``. + + :param length: + Number of trits to absorb. Defaults to ``len(trits)``. """ - length = len(trits) - offset = 0 + pad = ((len(trits) % HASH_LENGTH) or HASH_LENGTH) + trits += [0] * (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, + }, + ) # Copy trits from ``trits`` into internal state, one hash at a # time, transforming internal state in between hashes. @@ -92,7 +113,7 @@ def absorb(self, trits): # Move on to the next hash. offset += HASH_LENGTH - def squeeze(self, trits): + def squeeze(self, trits, offset=0): # type: (MutableSequence[int]) -> None """ Squeeze trits from the sponge. @@ -100,6 +121,9 @@ def squeeze(self, trits): :param trits: Sequence that the squeezed trits will be copied to. Note: this object will be modified! + + :param offset: + Starting offset in ``trits``. """ # # Squeeze is kind of like the opposite of absorb; it copies trits @@ -113,8 +137,18 @@ def squeeze(self, trits): # Ensure that ``trits`` can hold at least one hash worth of trits. trits.extend([0] * max(0, HASH_LENGTH - len(trits))) + # Check trits with offset can handle hash length + if len(trits) - offset < HASH_LENGTH: + raise with_context( + exc = ValueError('Invalid offset passed to ``squeeze``.'), + context = { + 'trits': trits, + 'offset': offset, + }, + ) + # Copy exactly one hash. - trits[0:HASH_LENGTH] = self._state[0:HASH_LENGTH] + trits[offset:offset + HASH_LENGTH] = self._state[0:HASH_LENGTH] # One hash worth of trits copied; now transform. self._transform() diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py new file mode 100644 index 00000000..7cb97511 --- /dev/null +++ b/test/crypto/pycurl_test.py @@ -0,0 +1,107 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function, \ + unicode_literals + +from unittest import TestCase + +from iota import TryteString +from iota.crypto.pycurl import Curl + + +class TestCurl(TestCase): + def test_correct_first(self): + inp = ( + 'EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJ' + 'FGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH' + ) + + trits = TryteString(inp).as_trits() + + curl = Curl() + curl.absorb(trits) + trits_out = [] + curl.squeeze(trits_out) + + trits_out = TryteString.from_trits(trits_out) + + self.assertEqual( + trits_out, + 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIG' + 'RXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSM') + + def test_input_length_greater_then_243(self): + inp = ( + 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' + 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' + '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA' + ) + + trits = TryteString(inp).as_trits() + + curl = Curl() + curl.absorb(trits, length=486) + trits_out = [] + curl.squeeze(trits_out) + + trits_out = TryteString.from_trits(trits_out) + + self.assertEqual( + trits_out, + 'RWCBOLRFANOAYQWXXTFQJYQFAUTEEBSZWTIRSSDR' + 'EYGCNFRLHQVDZXYXSJKCQFQLJMMRHYAZKRRLQZDKR') + + def test_input_with_offset(self): + inp = ( + 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' + 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' + '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA' + ) + + trits = TryteString(inp).as_trits() + + curl = Curl() + curl.absorb(trits, 243, length=486) + curl.absorb(trits, 0, length=243) + trits_out = [] + curl.squeeze(trits_out) + + trits_out = TryteString.from_trits(trits_out) + + self.assertEqual( + trits_out, + 'ZWNF9YOCAKC9CXQFYZDKXSSAZOCAZLEVEB9OZDJQG' + 'WEULHUDY9RAWAT9GIUXTTUSYJEGNGQDVJCGTQLN9') + + def test_squeeze_with_offset(self): + inp = ( + 'CDLFODMOGMQAWXDURDXTUAOO9BFESHYGZLBUWIIHPTLNZCUNHZAAXSUPUIBW' + 'IRLOVKCVWJSWEKRJQZUVRDZGZRNANUNCSGANCJWVHMZMVNJVUAZNFZKDAIVV' + 'LSMIM9SVGUHYECTGGIXTAMXXO9FIXUMQFZCGRQWAOWJPBTXNNQIRSTZEEAJV' + 'FSXWTHWBQJCWQNYYMHSPCYRA99ITVILYJPMFGOGOUOZUVABK9HMGABSORCVD' + 'FNGLMPJ9NFKBWCZMFPIWEAGRWPRNLLG9VYUUVLCTEWKGWQIRIJKERZWC9LVR' + 'XJEXNHBNUGEGGLMWGERKYFB9YEZCLXLKKMCGLRKQOGASDOUDYEDJLMV9BHPG' + 'GCXQIUVUOFFXKEIIINLVWLRYHHLKXPLSTWKIKNEJWEDFQQFXQVEHGRCIJC9T' + 'GVQNPPKGCFGPJNWSCPQZDDSIGAVZEIVYJDVPUOCTEMKTZFGXNGPQCOIBD9MX' + 'YTHJTX' + ) + + + d = [0] * 243 + trits = TryteString(inp).as_trits() + curl = Curl() + + for i in range(6): + curl.reset() + curl.absorb(trits, i * 243, (i + 1) * 243) + curl.squeeze(trits, i * 243) + + curl.reset() + curl.absorb(trits) + curl.squeeze(d) + + trits_out = TryteString.from_trits(d) + + self.assertEqual( + trits_out, + 'TAWDGNSEAD9ZRGBBVRVEKQYYVDOKHYQ9KEIYJKFT' + 'BQEYZDWZVMRFJQQGTMPHBZOGPIJCCVWLZVDKLAQVI') From 1043297e435e420116fc15f6adf3ce590ecd517f Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 11:25:00 +0800 Subject: [PATCH 2/7] Change curl import --- test/crypto/pycurl_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py index 7cb97511..fa5305e9 100644 --- a/test/crypto/pycurl_test.py +++ b/test/crypto/pycurl_test.py @@ -5,7 +5,7 @@ from unittest import TestCase from iota import TryteString -from iota.crypto.pycurl import Curl +from iota.crypto import Curl class TestCurl(TestCase): From 9287e8c08441d2af61710660f364586c5b6fd2a2 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 11:45:16 +0800 Subject: [PATCH 3/7] Add docstring and tweak test cases --- test/crypto/pycurl_test.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py index fa5305e9..396e4305 100644 --- a/test/crypto/pycurl_test.py +++ b/test/crypto/pycurl_test.py @@ -9,7 +9,11 @@ class TestCurl(TestCase): + """ + This is the test case for CURL-P hash function used in IOTA + """ def test_correct_first(self): + """Test the inp tryte string will get the correct output""" inp = ( 'EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJ' 'FGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH' @@ -29,7 +33,8 @@ def test_correct_first(self): 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIG' 'RXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSM') - def test_input_length_greater_then_243(self): + def test_input_length_greater_than_243(self): + """Test input trytes length greater than hash length should work""" inp = ( 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' @@ -39,7 +44,7 @@ def test_input_length_greater_then_243(self): trits = TryteString(inp).as_trits() curl = Curl() - curl.absorb(trits, length=486) + curl.absorb(trits) trits_out = [] curl.squeeze(trits_out) @@ -50,7 +55,31 @@ def test_input_length_greater_then_243(self): 'RWCBOLRFANOAYQWXXTFQJYQFAUTEEBSZWTIRSSDR' 'EYGCNFRLHQVDZXYXSJKCQFQLJMMRHYAZKRRLQZDKR') + def test_input_without_offset(self): + """Test input without offset should work""" + inp = ( + 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' + 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' + '9IFGYOLZXWZBKWZN9QOOTBQMWMUBLEWUEEASRHRTNIQWJQNDWRYLCA' + ) + + trits = TryteString(inp).as_trits() + + curl = Curl() + curl.absorb(trits, 0, length=486) + curl.absorb(trits, 0, length=243) + trits_out = [] + curl.squeeze(trits_out) + + trits_out = TryteString.from_trits(trits_out) + + self.assertEqual( + trits_out, + 'OTYHXEXJLCSMEY9LYCC9ASJXMORTLAYQEHRS9DAH' + '9NR9DXLXYDGOVOBEL9LWRITLWPHPYPZDKXVPAPKUA') + def test_input_with_offset(self): + """Test input with offset should work""" inp = ( 'G9JYBOMPUXHYHKSNRNMMSSZCSHOFYOYNZRSZMAAYWDYEIMVVOGKPJB' 'VBM9TDPULSFUNMTVXRKFIDOHUXXVYDLFSZYZTWQYTE9SPYYWYTXJYQ' @@ -73,6 +102,7 @@ def test_input_with_offset(self): 'WEULHUDY9RAWAT9GIUXTTUSYJEGNGQDVJCGTQLN9') def test_squeeze_with_offset(self): + """Test squeeze with offset, this only used in ISS""" inp = ( 'CDLFODMOGMQAWXDURDXTUAOO9BFESHYGZLBUWIIHPTLNZCUNHZAAXSUPUIBW' 'IRLOVKCVWJSWEKRJQZUVRDZGZRNANUNCSGANCJWVHMZMVNJVUAZNFZKDAIVV' From 358d4b0c1cab8fe3c0c84ef2c4606f7174a6bafb Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 11:46:52 +0800 Subject: [PATCH 4/7] Add ISS source code link --- test/crypto/pycurl_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py index 396e4305..6f867660 100644 --- a/test/crypto/pycurl_test.py +++ b/test/crypto/pycurl_test.py @@ -102,7 +102,9 @@ def test_input_with_offset(self): 'WEULHUDY9RAWAT9GIUXTTUSYJEGNGQDVJCGTQLN9') def test_squeeze_with_offset(self): - """Test squeeze with offset, this only used in ISS""" + """Test squeeze with offset, this only used in ISS + GitHub IRI ISS: https://github.com/iotaledger/iri/blob/dev/src/main/java/com/iota/iri/hash/ISS.java#L83 + """ inp = ( 'CDLFODMOGMQAWXDURDXTUAOO9BFESHYGZLBUWIIHPTLNZCUNHZAAXSUPUIBW' 'IRLOVKCVWJSWEKRJQZUVRDZGZRNANUNCSGANCJWVHMZMVNJVUAZNFZKDAIVV' From fb98b22bb517fa40be7157bc02b9969a4fd9494f Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 12:02:21 +0800 Subject: [PATCH 5/7] Add squeeze length and test case --- iota/crypto/pycurl.py | 32 +++++++++++++++++++++++++------- test/crypto/pycurl_test.py | 26 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/iota/crypto/pycurl.py b/iota/crypto/pycurl.py index 925bdf9a..1fbf0ec4 100644 --- a/iota/crypto/pycurl.py +++ b/iota/crypto/pycurl.py @@ -113,8 +113,8 @@ def absorb(self, trits, offset=0, length=None): # Move on to the next hash. offset += HASH_LENGTH - def squeeze(self, trits, offset=0): - # type: (MutableSequence[int]) -> None + def squeeze(self, trits, offset=0, length=HASH_LENGTH): + # type: (MutableSequence[int], Optional[int], Optional[int]) -> None """ Squeeze trits from the sponge. @@ -124,6 +124,9 @@ def squeeze(self, trits, offset=0): :param offset: Starting offset in ``trits``. + + :param length: + Number of trits to squeeze, default to ``HASH_LENGTH`` """ # # Squeeze is kind of like the opposite of absorb; it copies trits @@ -134,8 +137,18 @@ def squeeze(self, trits, offset=0): # can simplify the implementation somewhat. # + # Ensure length can be mod by HASH_LENGTH + if length % HASH_LENGTH != 0: + raise with_context( + exc = ValueError('Invalid length passed to ``sequeeze`.'), + context = { + 'trits': trits, + 'offset': offset, + 'length': length, + }) + # Ensure that ``trits`` can hold at least one hash worth of trits. - trits.extend([0] * max(0, HASH_LENGTH - len(trits))) + trits.extend([0] * max(0, length - len(trits))) # Check trits with offset can handle hash length if len(trits) - offset < HASH_LENGTH: @@ -144,14 +157,19 @@ def squeeze(self, trits, offset=0): context = { 'trits': trits, 'offset': offset, + 'length': length }, ) - # Copy exactly one hash. - trits[offset:offset + HASH_LENGTH] = self._state[0:HASH_LENGTH] + while length >= HASH_LENGTH: + # Copy exactly one hash. + trits[offset:offset + HASH_LENGTH] = self._state[0:HASH_LENGTH] - # One hash worth of trits copied; now transform. - self._transform() + # One hash worth of trits copied; now transform. + self._transform() + + offset += HASH_LENGTH + length -= HASH_LENGTH def _transform(self): # type: () -> None diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py index 6f867660..71f8878c 100644 --- a/test/crypto/pycurl_test.py +++ b/test/crypto/pycurl_test.py @@ -137,3 +137,29 @@ def test_squeeze_with_offset(self): trits_out, 'TAWDGNSEAD9ZRGBBVRVEKQYYVDOKHYQ9KEIYJKFT' 'BQEYZDWZVMRFJQQGTMPHBZOGPIJCCVWLZVDKLAQVI') + + def test_squeeze_with_486_length_should_work(self): + """ + Test squeeze with 486 length should work as well, no one use this + in real situation + """ + inp = ( + 'EMIDYNHBWMBCXVDEFOFWINXTERALUKYYPPHKP9JJ' + 'FGJEIUY9MUDVNFZHMMWZUYUSWAIOWEVTHNWMHANBH' + ) + + trits = TryteString(inp).as_trits() + + curl = Curl() + curl.absorb(trits) + trits_out = [] + curl.squeeze(trits_out, length=486) + + trits_out = TryteString.from_trits(trits_out) + + self.assertEqual( + trits_out, + 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIG' + 'RXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSMQ' + 'GSJWCCFQRHWKTSMVPWWCEGOMCNWFYWDZBEDBLXIFB' + 'HOTCKUMCANLSXXTNKSYNBMOSDDEYFTDOYIKDRJM') \ No newline at end of file From 0245bc8ed2206d9165a161f5e1e016e2ef035436 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 12:02:51 +0800 Subject: [PATCH 6/7] Fix absorb docstring --- iota/crypto/pycurl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iota/crypto/pycurl.py b/iota/crypto/pycurl.py index 1fbf0ec4..3ed0ee23 100644 --- a/iota/crypto/pycurl.py +++ b/iota/crypto/pycurl.py @@ -62,7 +62,7 @@ def reset(self): self._state = [0] * STATE_LENGTH # type: List[int] def absorb(self, trits, offset=0, length=None): - # type: (Sequence[int], int, Optional[int]) -> None + # type: (Sequence[int], Optional[int], Optional[int]) -> None """ Absorb trits into the sponge. From b28b9d8a11d11690dc52f16f18a64193123a3401 Mon Sep 17 00:00:00 2001 From: Louie Lu Date: Wed, 10 Jan 2018 12:06:59 +0800 Subject: [PATCH 7/7] Add blank line in the end --- test/crypto/pycurl_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/crypto/pycurl_test.py b/test/crypto/pycurl_test.py index 71f8878c..df3c5b04 100644 --- a/test/crypto/pycurl_test.py +++ b/test/crypto/pycurl_test.py @@ -162,4 +162,4 @@ def test_squeeze_with_486_length_should_work(self): 'AQBOPUMJMGVHFOXSMUAGZNACKUTISDPBSILMRAGIG' 'RXXS9JJTLIKZUW9BCJWKSTFBDSBLNVEEGVGAMSSMQ' 'GSJWCCFQRHWKTSMVPWWCEGOMCNWFYWDZBEDBLXIFB' - 'HOTCKUMCANLSXXTNKSYNBMOSDDEYFTDOYIKDRJM') \ No newline at end of file + 'HOTCKUMCANLSXXTNKSYNBMOSDDEYFTDOYIKDRJM')