diff --git a/duniterpy/constants.py b/duniterpy/constants.py index 46fc30287aa2b18207841a4a175c171197a9e27b..21d7456143747e421f1e5ef3a48a1799f061ad5d 100644 --- a/duniterpy/constants.py +++ b/duniterpy/constants.py @@ -33,3 +33,4 @@ WS2PID_REGEX = "[0-9a-f]{8}" WS2P_PRIVATE_PREFIX_REGEX = "O[CT][SAM]" WS2P_PUBLIC_PREFIX_REGEX = "I[CT]" WS2P_HEAD_REGEX = "HEAD:?(?:[0-9]+)?" +EMPTY_HASH = "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" diff --git a/duniterpy/documents/__init__.py b/duniterpy/documents/__init__.py index 36c64af8e95a4b74ee497df8be9a8632ddf029d5..5aa5497b67e868d27b22b9eaefd05be8deb8f71e 100644 --- a/duniterpy/documents/__init__.py +++ b/duniterpy/documents/__init__.py @@ -1,7 +1,8 @@ -from .block import Block, BlockUID, block_uid +from .block import Block +from .block_uid import BlockUID, block_uid from .certification import Identity, Certification, Revocation from .membership import Membership from .transaction import SimpleTransaction, Transaction, InputSource, OutputSource, \ - SIGParameter, Unlock, UnlockParameter + SIGParameter, Unlock, UnlockParameter from .document import Document, MalformedDocumentError from .crc_pubkey import CRCPubkey diff --git a/duniterpy/documents/block.py b/duniterpy/documents/block.py index 0822deb78224feac86eace143d2d4455937730c3..8a95b208a71968bab67261f0fa994149e79da621 100644 --- a/duniterpy/documents/block.py +++ b/duniterpy/documents/block.py @@ -1,106 +1,13 @@ import base64 import hashlib import re -from typing import Union, TypeVar, Type, Optional, List, Sequence - +from typing import TypeVar, Type, Optional, List, Sequence +from .block_uid import BlockUID from .certification import Identity, Certification, Revocation from .document import Document, MalformedDocumentError from .membership import Membership from .transaction import Transaction -from ..constants import PUBKEY_REGEX, BLOCK_ID_REGEX, BLOCK_HASH_REGEX - -# required to type hint cls in classmethod -BlockUIDType = TypeVar('BlockUIDType', bound='BlockUID') - - -class BlockUID: - """ - A simple block id - """ - re_block_uid = re.compile("({block_id_regex})-({block_hash_regex})".format(block_id_regex=BLOCK_ID_REGEX, - block_hash_regex=BLOCK_HASH_REGEX)) - re_hash = re.compile("({block_hash_regex})".format(block_hash_regex=BLOCK_HASH_REGEX)) - - def __init__(self, number: int, sha_hash: str) -> None: - assert (type(number) is int) - assert (BlockUID.re_hash.match(sha_hash) is not None) - self.number = number - self.sha_hash = sha_hash - - @classmethod - def empty(cls: Type[BlockUIDType]) -> BlockUIDType: - return cls(0, Block.Empty_Hash) - - @classmethod - def from_str(cls: Type[BlockUIDType], blockid: str) -> BlockUIDType: - """ - :param blockid: The block id - """ - data = BlockUID.re_block_uid.match(blockid) - if data is None: - raise MalformedDocumentError("BlockUID") - try: - number = int(data.group(1)) - except AttributeError: - raise MalformedDocumentError("BlockUID") - - try: - sha_hash = data.group(2) - except AttributeError: - raise MalformedDocumentError("BlockHash") - - return cls(number, sha_hash) - - def __str__(self) -> str: - return "{0}-{1}".format(self.number, self.sha_hash) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, BlockUID): - return False - return self.number == other.number and self.sha_hash == other.sha_hash - - def __lt__(self, other: object) -> bool: - if not isinstance(other, BlockUID): - return False - return self.number < other.number - - def __gt__(self, other: object) -> bool: - if not isinstance(other, BlockUID): - return False - return self.number > other.number - - def __le__(self, other: object) -> bool: - if not isinstance(other, BlockUID): - return False - return self.number <= other.number - - def __ge__(self, other: object) -> bool: - if not isinstance(other, BlockUID): - return False - return self.number >= other.number - - def __hash__(self) -> int: - return hash((self.number, self.sha_hash)) - - def __bool__(self) -> bool: - return self != BlockUID.empty() - - -def block_uid(value: Union[str, BlockUID, None]) -> BlockUID: - """ - Convert value to BlockUID instance - - :param value: Value to convert - :return: - """ - if isinstance(value, BlockUID): - return value - elif isinstance(value, str): - return BlockUID.from_str(value) - elif value is None: - return BlockUID.empty() - else: - raise TypeError("Cannot convert {0} to BlockUID".format(type(value))) +from ..constants import PUBKEY_REGEX, BLOCK_HASH_REGEX # required to type hint cls in classmethod @@ -212,8 +119,6 @@ The class Block handles Block documents. } } - Empty_Hash = "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" - def __init__(self, version: int, currency: str, diff --git a/duniterpy/documents/block_uid.py b/duniterpy/documents/block_uid.py new file mode 100644 index 0000000000000000000000000000000000000000..0b84e84ce02de62f898e4dc7d1856876590e6555 --- /dev/null +++ b/duniterpy/documents/block_uid.py @@ -0,0 +1,98 @@ +import re +from typing import Union, TypeVar, Type + +from .document import MalformedDocumentError +from ..constants import EMPTY_HASH, BLOCK_ID_REGEX, BLOCK_HASH_REGEX + +# required to type hint cls in classmethod +BlockUIDType = TypeVar('BlockUIDType', bound='BlockUID') + + +class BlockUID: + """ + A simple block id + """ + re_block_uid = re.compile("({block_id_regex})-({block_hash_regex})".format(block_id_regex=BLOCK_ID_REGEX, + block_hash_regex=BLOCK_HASH_REGEX)) + re_hash = re.compile("({block_hash_regex})".format(block_hash_regex=BLOCK_HASH_REGEX)) + + def __init__(self, number: int, sha_hash: str) -> None: + assert (type(number) is int) + assert (BlockUID.re_hash.match(sha_hash) is not None) + self.number = number + self.sha_hash = sha_hash + + @classmethod + def empty(cls: Type[BlockUIDType]) -> BlockUIDType: + return cls(0, EMPTY_HASH) + + @classmethod + def from_str(cls: Type[BlockUIDType], blockid: str) -> BlockUIDType: + """ + :param blockid: The block id + """ + data = BlockUID.re_block_uid.match(blockid) + if data is None: + raise MalformedDocumentError("BlockUID") + try: + number = int(data.group(1)) + except AttributeError: + raise MalformedDocumentError("BlockUID") + + try: + sha_hash = data.group(2) + except AttributeError: + raise MalformedDocumentError("BlockHash") + + return cls(number, sha_hash) + + def __str__(self) -> str: + return "{0}-{1}".format(self.number, self.sha_hash) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BlockUID): + return False + return self.number == other.number and self.sha_hash == other.sha_hash + + def __lt__(self, other: object) -> bool: + if not isinstance(other, BlockUID): + return False + return self.number < other.number + + def __gt__(self, other: object) -> bool: + if not isinstance(other, BlockUID): + return False + return self.number > other.number + + def __le__(self, other: object) -> bool: + if not isinstance(other, BlockUID): + return False + return self.number <= other.number + + def __ge__(self, other: object) -> bool: + if not isinstance(other, BlockUID): + return False + return self.number >= other.number + + def __hash__(self) -> int: + return hash((self.number, self.sha_hash)) + + def __bool__(self) -> bool: + return self != BlockUID.empty() + + +def block_uid(value: Union[str, BlockUID, None]) -> BlockUID: + """ + Convert value to BlockUID instance + + :param value: Value to convert + :return: + """ + if isinstance(value, BlockUID): + return value + elif isinstance(value, str): + return BlockUID.from_str(value) + elif value is None: + return BlockUID.empty() + else: + raise TypeError("Cannot convert {0} to BlockUID".format(type(value))) diff --git a/duniterpy/documents/certification.py b/duniterpy/documents/certification.py index f32dc04b3bf6d458c13133edf594625cde8a2d0a..3bd30154d956b4b742cfddfe81092d2de121e252 100644 --- a/duniterpy/documents/certification.py +++ b/duniterpy/documents/certification.py @@ -1,9 +1,8 @@ import base64 import logging import re -from typing import Optional, TypeVar, Type, List - -from duniterpy.documents import BlockUID +from typing import Optional, TypeVar, Type +from .block_uid import BlockUID from ..constants import PUBKEY_REGEX, SIGNATURE_REGEX, BLOCK_ID_REGEX, BLOCK_UID_REGEX, UID_REGEX from .document import Document, MalformedDocumentError @@ -63,8 +62,6 @@ class Identity(Document): :param inline: Inline string of the Identity :return: """ - from .block import BlockUID - selfcert_data = Identity.re_inline.match(inline) if selfcert_data is None: raise MalformedDocumentError("Inline self certification") @@ -82,8 +79,6 @@ class Identity(Document): :param signed_raw: Signed raw document :return: """ - from .block import BlockUID - n = 0 lines = signed_raw.splitlines(True) @@ -141,6 +136,8 @@ Timestamp: {timestamp} # required to type hint cls in classmethod CertificationType = TypeVar('CertificationType', bound='Certification') +# todo: certification document should be created with the certified Identity document in arguments + class Certification(Document): """ @@ -197,8 +194,6 @@ class Certification(Document): :param signed_raw: Signed raw document :return: """ - from .block import BlockUID - n = 0 lines = signed_raw.splitlines(True) @@ -245,7 +240,6 @@ class Certification(Document): :param inline: Inline document :return: """ - from .block import BlockUID cert_data = Certification.re_inline.match(inline) if cert_data is None: raise MalformedDocumentError("Certification ({0})".format(inline)) @@ -325,6 +319,8 @@ CertTimestamp: {timestamp} # required to type hint cls in classmethod RevocationType = TypeVar('RevocationType', bound='Revocation') +# todo: Revocation document should be created with the revoked Identity document in arguments + class Revocation(Document): """ diff --git a/duniterpy/documents/membership.py b/duniterpy/documents/membership.py index be436afec1fdd77fe861886ff8b9d0a0f2dd0180..115148f9c43a7f8acd313e767163d1c5e4e30121 100644 --- a/duniterpy/documents/membership.py +++ b/duniterpy/documents/membership.py @@ -4,7 +4,7 @@ Created on 2 déc. 2014 @author: inso """ import re - +from .block_uid import BlockUID from .document import Document, MalformedDocumentError from ..constants import BLOCK_UID_REGEX, SIGNATURE_REGEX, PUBKEY_REGEX @@ -69,7 +69,6 @@ class Membership(Document): @classmethod def from_inline(cls, version, currency, membership_type, inline): - from .block import BlockUID data = Membership.re_inline.match(inline) if data is None: raise MalformedDocumentError("Inline membership ({0})".format(inline)) @@ -82,7 +81,6 @@ class Membership(Document): @classmethod def from_signed_raw(cls, signed_raw): - from .block import BlockUID lines = signed_raw.splitlines(True) n = 0 diff --git a/duniterpy/documents/peer.py b/duniterpy/documents/peer.py index 099751a41ce2175a55f464e15a8cfe098c74ba67..de7bc60f65e82c5a7d364d55a423f46b55a303f0 100644 --- a/duniterpy/documents/peer.py +++ b/duniterpy/documents/peer.py @@ -2,7 +2,7 @@ import re from duniterpy.api.endpoint import endpoint from .document import Document -from . import BlockUID +from .block_uid import BlockUID from ..constants import BLOCK_HASH_REGEX, PUBKEY_REGEX diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py index fd6cd7e98b1f2907478e9d1448e6ddbf41d54b75..0ebe68cb77ec4ff248500ea5b75aefe92bd44b20 100644 --- a/duniterpy/documents/transaction.py +++ b/duniterpy/documents/transaction.py @@ -1,7 +1,7 @@ import re import pypeg2 - +from .block_uid import BlockUID from .document import Document, MalformedDocumentError from ..constants import PUBKEY_REGEX, TRANSACTION_HASH_REGEX, BLOCK_ID_REGEX, BLOCK_UID_REGEX from ..grammars import output @@ -165,7 +165,6 @@ Comment: {comment} @classmethod def from_compact(cls, currency, compact): - from .block import BlockUID lines = compact.splitlines(True) n = 0 @@ -231,7 +230,6 @@ Comment: {comment} @classmethod def from_signed_raw(cls, raw): - from .block import BlockUID lines = raw.splitlines(True) n = 0 diff --git a/duniterpy/documents/ws2p/heads.py b/duniterpy/documents/ws2p/heads.py index 7d020e825d93a6a14fbec73e0152f9dbbb8eb847..78ccb9a7a98595de2e56480cd93cb8a7c7edc722 100644 --- a/duniterpy/documents/ws2p/heads.py +++ b/duniterpy/documents/ws2p/heads.py @@ -2,7 +2,7 @@ import re import attr -from ..block import BlockUID +from ..block_uid import BlockUID from ..document import MalformedDocumentError from ...constants import WS2P_PUBLIC_PREFIX_REGEX, WS2P_PRIVATE_PREFIX_REGEX, WS2P_HEAD_REGEX, \ PUBKEY_REGEX, SIGNATURE_REGEX, WS2PID_REGEX, BLOCK_UID_REGEX diff --git a/tests/documents/test_block.py b/tests/documents/test_block.py index adfd08d07cb12abfd51b1f1c2d66646219d879c5..4472edc35252c8a2fe59bd0013d070affba43295 100644 --- a/tests/documents/test_block.py +++ b/tests/documents/test_block.py @@ -5,7 +5,8 @@ Created on 12 déc. 2014 """ import unittest -from duniterpy.documents.block import Block, BlockUID, block_uid +from duniterpy.documents.block import Block +from duniterpy.documents.block_uid import BlockUID, block_uid raw_block = """Version: 2 Type: Block diff --git a/tests/documents/test_certification.py b/tests/documents/test_certification.py index 65d77284d090830199c6c7fd94e10596642f1455..f9380da9063272fc425ca3eaf0698d695cad9623 100644 --- a/tests/documents/test_certification.py +++ b/tests/documents/test_certification.py @@ -6,7 +6,8 @@ Created on 6 déc. 2014 import unittest from duniterpy.documents.certification import Identity, Certification, Revocation -from duniterpy.documents import Block, BlockUID +from duniterpy.documents.block import Block, BlockUID +from duniterpy.constants import EMPTY_HASH selfcert_inlines = ["HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:\ h/H8tDIEbfA4yxMQcvfOXVDQhi1sUa9qYtPKrM59Bulv97ouwbAvAsEkC1Uyit1IOpeAV+CQQs4IaAyjE8F1Cw==:\ @@ -73,7 +74,7 @@ J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBf self.assertEqual(cert.pubkey_from, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") self.assertEqual(cert.pubkey_to, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk") self.assertEqual(cert.timestamp.number, 0) - self.assertEqual(cert.timestamp.sha_hash, Block.Empty_Hash) + self.assertEqual(cert.timestamp.sha_hash, EMPTY_HASH) self.assertEqual(cert.signatures[0], "TgmDuMxZdyutroj9jiLJA8tQp/389JIzDKuxW5+h7GIfjDu1ZbwI7HNm5rlUDhR2KreaV/QJjEaItT4Cf75rCQ==") cert = Certification.from_inline(version, currency, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD", cert_inlines[1]) diff --git a/tests/documents/test_membership.py b/tests/documents/test_membership.py index c5eefdaa0d51a418349421f21459d973f24a5a59..4c2fe1f150d7acb90a57d07b6d674298526049f3 100644 --- a/tests/documents/test_membership.py +++ b/tests/documents/test_membership.py @@ -47,7 +47,7 @@ class Test_Membership(unittest.TestCase): def test_fromraw_toraw(self): membership = Membership.from_signed_raw(membership_raw) - rendered_membership = membership.signed_raw_for_certified() + rendered_membership = membership.signed_raw() from_rendered_membership = Membership.from_signed_raw(rendered_membership) self.assertEqual(from_rendered_membership.issuer, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk") self.assertEqual(from_rendered_membership.membership_ts.number, 0) diff --git a/tests/documents/test_peer.py b/tests/documents/test_peer.py index 46a9e9660a7b76b702f6e985f4a32911032033ed..192cb052f1aa4395bb45044c9742fd03ea4f2457 100644 --- a/tests/documents/test_peer.py +++ b/tests/documents/test_peer.py @@ -59,7 +59,7 @@ class TestPeer(unittest.TestCase): def test_fromraw_toraw(self): peer = Peer.from_signed_raw(rawpeer) - rendered_peer = peer.signed_raw_for_certified() + rendered_peer = peer.signed_raw() from_rendered_peer = Peer.from_signed_raw(rendered_peer) self.assertEqual(from_rendered_peer.currency, "beta_brousouf") @@ -87,9 +87,9 @@ class TestPeer(unittest.TestCase): self.assertEqual(from_rendered_peer.signatures[0], "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==") - self.assertEqual(rawpeer, from_rendered_peer.signed_raw_for_certified()) + self.assertEqual(rawpeer, from_rendered_peer.signed_raw()) def test_incorrect(self): peer = Peer.from_signed_raw(test_weird_ipv6_peer) - rendered_peer = peer.signed_raw_for_certified() + rendered_peer = peer.signed_raw() Peer.from_signed_raw(rendered_peer) diff --git a/tests/documents/test_transaction.py b/tests/documents/test_transaction.py index d872634b8938cd82c70affbdf281284dccf4c05b..a14fe4e117fc193e5554e663285bd478cea34521 100644 --- a/tests/documents/test_transaction.py +++ b/tests/documents/test_transaction.py @@ -299,7 +299,7 @@ class Test_Transaction(unittest.TestCase): def test_fromraw_toraw(self): tx = Transaction.from_signed_raw(tx_raw) - rendered_tx = tx.signed_raw_for_certified() + rendered_tx = tx.signed_raw() from_rendered_tx = Transaction.from_signed_raw(rendered_tx) self.assertEqual(tx.version, 2) @@ -366,7 +366,7 @@ class Test_Transaction(unittest.TestCase): def test_fromraw_toraw_v3(self): tx = Transaction.from_signed_raw(tx_raw_v3) - rendered_tx = tx.signed_raw_for_certified() + rendered_tx = tx.signed_raw() from_rendered_tx = Transaction.from_signed_raw(rendered_tx) self.assertEqual(tx.version, 3) @@ -435,7 +435,7 @@ class Test_Transaction(unittest.TestCase): def test_compact_change(self): tx = Transaction.from_compact("gtest", compact_change) - rendered_tx = tx.signed_raw_for_certified() + rendered_tx = tx.signed_raw() from_rendered_tx = Transaction.from_signed_raw(rendered_tx) def test_reduce_base(self):