From 7aa6350740c5a1e583aad42161db19ee72dc7281 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Sat, 13 Dec 2014 22:29:23 +0100 Subject: [PATCH] from_raw is now from_signed_raw More units tests From_raw -> to_raw -> from_raw testing --- _ucoinpy_test/documents/test_block.py | 87 ++++++++++++++++--- _ucoinpy_test/documents/test_certification.py | 4 +- _ucoinpy_test/documents/test_membership.py | 31 ++++++- _ucoinpy_test/documents/test_peer.py | 45 ++++++++++ _ucoinpy_test/documents/test_status.py | 28 ++++++ _ucoinpy_test/documents/test_transaction.py | 2 + ucoinpy/documents/__init__.py | 23 +++++ ucoinpy/documents/block.py | 55 ++++++------ ucoinpy/documents/certification.py | 47 +++++----- ucoinpy/documents/membership.py | 78 +++++++++++++---- ucoinpy/documents/peer.py | 72 ++++++++++++--- ucoinpy/documents/status.py | 50 +++++++++-- ucoinpy/documents/transaction.py | 37 +++++--- 13 files changed, 444 insertions(+), 115 deletions(-) create mode 100644 _ucoinpy_test/documents/test_peer.py create mode 100644 _ucoinpy_test/documents/test_status.py diff --git a/_ucoinpy_test/documents/test_block.py b/_ucoinpy_test/documents/test_block.py index cd8990b9..7eb44319 100644 --- a/_ucoinpy_test/documents/test_block.py +++ b/_ucoinpy_test/documents/test_block.py @@ -7,15 +7,27 @@ import pytest from ucoinpy.documents.block import Block from mock import Mock -raw_block = "Version: 1\nType: \ -Block\nCurrency: zeta_brouzouf\n\ -Nonce: 45079\nNumber: 15\nPoWMin: 4\n\ -Time: 1418083330\nMedianTime: 1418080208\n\ -Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n\ -PreviousHash: 0000E73C340601ACA1AD5AAA5B5E56B03E178EF8\n\ -PreviousIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n\ -MembersCount: 4\nIdentities:\nJoiners:\nActives:\nLeavers:\n\ -Excluded:\nCertifications:\nTransactions:\n" +raw_block = """Version: 1 +Type: Block +Currency: zeta_brouzouf +Nonce: 45079 +Number: 15 +PoWMin: 4 +Time: 1418083330 +MedianTime: 1418080208 +Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +PreviousHash: 0000E73C340601ACA1AD5AAA5B5E56B03E178EF8 +PreviousIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +MembersCount: 4 +Identities: +Joiners: +Actives: +Leavers: +Excluded: +Certifications: +Transactions: +42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r +""" raw_block_zero = """Version: 1 @@ -56,12 +68,13 @@ HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:RdrHvL179Rw62UuyBrqy2M1crx7RPajaViB RdrHvL179Rw62UuyBrqy2M1crx7RPajaViBatS59EGS:9fx25FmeBDJcikZLWxK5HuzKNbY6MaWYXoK1ajteE42Y:0:90w2HrbdsKIc6YJq3Ksa4sSgjpYSMM05+UuowAlYjrk1ixHIyWyg5odyZPRwO50aiIyUsbikoOWsMc3G8ob/Cg== HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:9fx25FmeBDJcikZLWxK5HuzKNbY6MaWYXoK1ajteE42Y:0:28lv0p8EPHpVgAMiPvXvIe5lMvYJxwko2tv5bPO4voHRHSaDcTz5BR7Oe69S6wjANIEAMfebXiFMqZdj+mWRAA== Transactions: +42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r """ class Test_Block: def test_fromraw(self): - block = Block.from_raw(raw_block) + block = Block.from_signed_raw(raw_block) assert block.version == 1 assert block.currency == "zeta_brouzouf" assert block.noonce == 45079 @@ -81,8 +94,8 @@ class Test_Block: assert block.certifications == [] assert block.transactions == [] - def test_from_raw_block_zero(self): - block = Block.from_raw(raw_block_zero) + def test_from_signed_raw_block_zero(self): + block = Block.from_signed_raw(raw_block_zero) assert block.version == 1 assert block.currency == "zeta_brouzouf" assert block.noonce == 2125 @@ -101,3 +114,53 @@ class Test_Block: assert block.excluded == [] assert len(block.certifications) == 12 assert block.transactions == [] + + def test_to_raw_from_signed_raw(self): + block = Block.from_signed_raw(raw_block) + rendered_raw = block.signed_raw() + from_rendered_raw = Block.from_signed_raw(rendered_raw) + + assert from_rendered_raw.version == 1 + assert from_rendered_raw.currency == "zeta_brouzouf" + assert from_rendered_raw.noonce == 45079 + assert from_rendered_raw.number == 15 + assert from_rendered_raw.powmin == 4 + assert from_rendered_raw.time == 1418083330 + assert from_rendered_raw.mediantime == 1418080208 + assert from_rendered_raw.issuer == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" + assert from_rendered_raw.prev_hash == "0000E73C340601ACA1AD5AAA5B5E56B03E178EF8" + assert from_rendered_raw.prev_issuer == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" + assert from_rendered_raw.members_count == 4 + assert from_rendered_raw.identities == [] + assert from_rendered_raw.joiners == [] + assert from_rendered_raw.actives == [] + assert from_rendered_raw.leavers == [] + assert from_rendered_raw.excluded == [] + assert from_rendered_raw.certifications == [] + assert from_rendered_raw.transactions == [] + + def test_to_raw_from_signed_raw_zero(self): + block = Block.from_signed_raw(raw_block_zero) + rendered_raw = block.signed_raw() + from_rendered_raw = block.from_signed_raw(rendered_raw) + + assert from_rendered_raw.version == 1 + assert from_rendered_raw.currency == "zeta_brouzouf" + assert from_rendered_raw.noonce == 2125 + assert from_rendered_raw.number == 0 + assert from_rendered_raw.powmin == 3 + assert from_rendered_raw.time == 1418077277 + assert from_rendered_raw.mediantime == 1418077277 + assert from_rendered_raw.issuer == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" + assert from_rendered_raw.parameters == ('0.01','302400','100','5259600','2629800','3','5', + '2629800','3','11','600','10','20','0.67') + assert from_rendered_raw.members_count == 4 + assert len(from_rendered_raw.identities) == 4 + assert len(from_rendered_raw.joiners) == 4 + assert from_rendered_raw.actives == [] + assert from_rendered_raw.leavers == [] + assert from_rendered_raw.excluded == [] + assert len(from_rendered_raw.certifications) == 12 + assert from_rendered_raw.transactions == [] + + diff --git a/_ucoinpy_test/documents/test_certification.py b/_ucoinpy_test/documents/test_certification.py index 96814464..e2cf7278 100644 --- a/_ucoinpy_test/documents/test_certification.py +++ b/_ucoinpy_test/documents/test_certification.py @@ -33,13 +33,13 @@ class Test_SelfCertification: assert selfcert.pubkey == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" assert selfcert.signatures[0] == "h/H8tDIEbfA4yxMQcvfOXVDQhi1sUa9qYtPKrM59Bulv97ouwbAvAsEkC1Uyit1IOpeAV+CQQs4IaAyjE8F1Cw==" assert selfcert.timestamp == 1416335620 - assert selfcert.identifier == "cgeek" + assert selfcert.uid == "cgeek" selfcert = SelfCertification.from_inline(version, currency, selfcert_inlines[1]) assert selfcert.pubkey == "RdrHvL179Rw62UuyBrqy2M1crx7RPajaViBatS59EGS" assert selfcert.signatures[0] == "Ah55O8cvdkGS4at6AGOKUjy+wrFwAq8iKRJ5xLIb6Xdi3M8WfGOUdMjwZA6GlSkdtlMgEhQPm+r2PMebxKrCBg==" assert selfcert.timestamp == 1416428323 - assert selfcert.identifier == "vit" + assert selfcert.uid == "vit" def test_certifications(self): version = 1 diff --git a/_ucoinpy_test/documents/test_membership.py b/_ucoinpy_test/documents/test_membership.py index 2881952a..5cf246bb 100644 --- a/_ucoinpy_test/documents/test_membership.py +++ b/_ucoinpy_test/documents/test_membership.py @@ -7,12 +7,39 @@ import pytest from ucoinpy.documents.membership import Membership from mock import Mock -inline_membership = "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:\ +membership_inline = "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:\ dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==:\ 0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1416335620:cgeek\n" +membership_raw = """Version: 1 +Type: Membership +Currency: beta_brousouf +Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +Block: 0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709 +Membership: IN +UserID: cgeek +CertTS: 1416335620 +dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg== +""" + class Test_Membership: def test_frominline(self): - membership = Membership.from_inline(1, "zeta_brousouf", 'IN', inline_membership) + membership = Membership.from_inline(1, "zeta_brousouf", 'IN', membership_inline) + assert membership.issuer == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" + assert membership.block_number == 0 + assert membership.block_hash == "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" + assert membership.cert_ts == 1416335620 + assert membership.uid == "cgeek" + assert membership.signatures[0] == "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==" + assert membership.membership_type == 'IN' + def test_fromraw(self): + membership = Membership.from_signed_raw(membership_raw) + assert membership.issuer == "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" + assert membership.block_number == 0 + assert membership.block_hash == "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" + assert membership.cert_ts == 1416335620 + assert membership.uid == "cgeek" + assert membership.signatures[0] == "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==" + assert membership.membership_type == 'IN' diff --git a/_ucoinpy_test/documents/test_peer.py b/_ucoinpy_test/documents/test_peer.py new file mode 100644 index 00000000..b9e62385 --- /dev/null +++ b/_ucoinpy_test/documents/test_peer.py @@ -0,0 +1,45 @@ +''' +Created on 13 déc. 2014 + +@author: inso +''' +import pytest +from ucoinpy.documents.peer import Peer, BMAEndpoint, UnknownEndpoint + + +rawpeer = """Version: 1 +Type: Peer +Currency: beta_brousouf +PublicKey: HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY +Block: 8-1922C324ABC4AF7EF7656734A31F5197888DDD52 +Endpoints: +BASIC_MERKLED_API some.dns.name 88.77.66.55 2001:0db8:0000:85a3:0000:0000:ac1f 9001 +BASIC_MERKLED_API some.dns.name 88.77.66.55 2001:0db8:0000:85a3:0000:0000:ac1f 9002 +OTHER_PROTOCOL 88.77.66.55 9001 +dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg== +""" + + +class TestPeer: + def test_fromraw(self): + peer = Peer.from_signed_raw(rawpeer) + assert peer.currency == "beta_brousouf" + assert peer.pubkey == "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY" + assert peer.blockid == "8-1922C324ABC4AF7EF7656734A31F5197888DDD52" + assert len(peer.endpoints) == 3 + assert type(peer.endpoints[0]) is BMAEndpoint + assert type(peer.endpoints[1]) is BMAEndpoint + assert type(peer.endpoints[2]) is UnknownEndpoint + + assert peer.endpoints[0].server == "some.dns.name" + assert peer.endpoints[0].ipv4 == "88.77.66.55" + assert peer.endpoints[0].ipv6 == "2001:0db8:0000:85a3:0000:0000:ac1f" + assert peer.endpoints[0].port == 9001 + + assert peer.endpoints[1].server == "some.dns.name" + assert peer.endpoints[1].ipv4 == "88.77.66.55" + assert peer.endpoints[1].ipv6 == "2001:0db8:0000:85a3:0000:0000:ac1f" + assert peer.endpoints[1].port == 9002 + + assert peer.signatures[0] == "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==" + diff --git a/_ucoinpy_test/documents/test_status.py b/_ucoinpy_test/documents/test_status.py new file mode 100644 index 00000000..2e85e078 --- /dev/null +++ b/_ucoinpy_test/documents/test_status.py @@ -0,0 +1,28 @@ +''' +Created on 13 déc. 2014 + +@author: inso +''' +import pytest +from ucoinpy.documents.status import Status + +raw_status = """Version: 1 +Type: Status +Currency: beta_brousouf +Status: UP +Block: 8-1922C324ABC4AF7EF7656734A31F5197888DDD52 +From: HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY +To: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU +dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg== +""" + + +class Test_Status: + def test_fromraw(self): + status = Status.from_signed_raw(raw_status) + assert status.status == 'UP' + assert status.blockid == "8-1922C324ABC4AF7EF7656734A31F5197888DDD52" + assert status.sender == "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY" + assert status.recipient == "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU" + assert status.signatures[0] == "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==" + diff --git a/_ucoinpy_test/documents/test_transaction.py b/_ucoinpy_test/documents/test_transaction.py index f24cac9a..9ca03946 100644 --- a/_ucoinpy_test/documents/test_transaction.py +++ b/_ucoinpy_test/documents/test_transaction.py @@ -48,3 +48,5 @@ class Test_Transaction: assert tx.outputs[0].pubkey == "BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g" assert tx.outputs[0].amount == 30 + + assert tx.signatures[0] == "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r" diff --git a/ucoinpy/documents/__init__.py b/ucoinpy/documents/__init__.py index 37411ace..14a7f83d 100644 --- a/ucoinpy/documents/__init__.py +++ b/ucoinpy/documents/__init__.py @@ -6,13 +6,36 @@ Created on 3 déc. 2014 import base58 import re from ..key import Base58Encoder +from nacl.encoding import Base64Encoder class Document: re_version = re.compile("Version: ([0-9]+)\n") re_currency = re.compile("Currency: ([^\n]+)\n") + re_signature = re.compile("([A-Za-z0-9+/]+(?:=|==)?)\n") def __init__(self, version, currency, signatures): self.version = version self.currency = currency self.signatures = signatures + + def sign(self, keys): + ''' + Sign the current document. + Warning : current signatures will be replaced with the new ones. + ''' + self.signatures = [] + for k in keys: + self.signatures.append(k.sign(self.raw(), Base64Encoder)) + + def signed_raw(self): + ''' + If keys are None, returns the raw + current signatures + If keys are present, returns the raw signed by these keys + ''' + raw = self.raw() + signed_raw = raw + for s in self.signatures: + if s is not None: + signed_raw += s + "\n" + return signed_raw diff --git a/ucoinpy/documents/block.py b/ucoinpy/documents/block.py index cbf7c133..1bf08afe 100644 --- a/ucoinpy/documents/block.py +++ b/ucoinpy/documents/block.py @@ -74,7 +74,6 @@ BOTTOM_SIGNATURE re_excluded = re.compile("Excluded:\n") re_certifications = re.compile("Certifications:\n") re_transactions = re.compile("Transactions:\n") - re_sign = re.compile("([A-Za-z0-9+/]+)") def __init__(self, version, currency, noonce, number, powmin, time, mediantime, ud, issuer, prev_hash, prev_issuer, @@ -84,7 +83,10 @@ BOTTOM_SIGNATURE ''' Constructor ''' - super(Block, self).__init__(version, currency, [signature]) + if signature: + super().__init__(version, currency, [signature]) + else: + super().__init__(version, currency, []) self.noonce = noonce self.number = number self.powmin = powmin @@ -105,14 +107,14 @@ BOTTOM_SIGNATURE self.transactions = transactions @classmethod - def from_raw(cls, raw): + def from_signed_raw(cls, raw, signature=None): lines = raw.splitlines(True) n = 0 version = int(Block.re_version.match(lines[n]).group(1)) n = n + 1 - doctype = Block.re_type.match(lines[n]).group(1) + Block.re_type.match(lines[n]).group(1) n = n + 1 currency = Block.re_currency.match(lines[n]).group(1) @@ -211,16 +213,12 @@ BOTTOM_SIGNATURE if Block.re_transactions.match(lines[n]): n = n + 1 - while n < len(lines) and not Block.re_sign.match(lines[n]): + while not Block.re_signature.match(lines[n]): transaction = Transaction.from_compact(version, lines[n]) transactions.append(transaction) n = n + 1 - signature = None - if n < len(lines): - signature = Block.re_sign.match(lines[n]) - - n = n + 1 + signature = Block.re_signature.match(lines[n]).group(1) return cls(version, currency, noonce, number, powmin, time, mediantime, ud, issuer, prev_hash, prev_issuer, @@ -229,8 +227,7 @@ BOTTOM_SIGNATURE transactions, signature) def raw(self): - doc = """ -Version: {0} + doc = """Version: {0} Type: Block Currency: {1} Nonce: {2} @@ -238,26 +235,28 @@ Number: {3} PoWMin: {4} Time: {5} MedianTime: {6} -UniversalDividend: {7} -Issuer: {8} -PreviousHash: {9} -PreviousIssuer: {10} -Parameters: {11} -MembersCount: {12} -Identities:""".format(self.version, +""".format(self.version, self.currency, self.noonce, self.number, self.powmin, self.time, - self.mediantime, - self.ud, - self.issuer, - self.prev_hash, - self.prev_issuer, - self.parameters, - self.members_count) + self.mediantime) + if self.ud: + doc += "UniversalDividend: {0}\n".format(self.ud) + + doc += "Issuer: {0}\n".format(self.issuer) + + if self.number == 0: + str_params = ":".join(self.parameters) + doc += "Parameters: {0}\n".format(str_params) + else: + doc += "PreviousHash: {0}\n\ +PreviousIssuer: {1}\n".format(self.prev_hash, self.prev_issuer) + + doc += "MembersCount: {0}\n".format(self.members_count) + doc += "Identities:\n" for identity in self.identities: doc += "{0}\n".format(identity.inline()) @@ -274,7 +273,7 @@ Identities:""".format(self.version, doc += "{0]\n".format(leaver.inline()) doc += "Excluded:\n" - for exclude in self.exclude: + for exclude in self.excluded: doc += "{0}\n".format(exclude.inline()) doc += "Certifications:\n" @@ -285,4 +284,4 @@ Identities:""".format(self.version, for transaction in self.transactions: doc += "{0}\n".format(transaction.inline()) - doc += self.signatures[0] + return doc diff --git a/ucoinpy/documents/certification.py b/ucoinpy/documents/certification.py index e7df7abd..3d758409 100644 --- a/ucoinpy/documents/certification.py +++ b/ucoinpy/documents/certification.py @@ -14,12 +14,17 @@ class SelfCertification(Document): ''' re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):([0-9]+):([^\n]+)\n") - - def __init__(self, version, currency, pubkey, ts, identifier, signature): - super().__init__(version, currency, [signature]) + re_uid = re.compile("UID:([^\n]+)\n") + re_timestamp = re.compile("META:TS:([0-9]+)\n") + + def __init__(self, version, currency, pubkey, ts, uid, signature): + if signature: + super().__init__(version, currency, [signature]) + else: + super().__init__(version, currency, []) self.pubkey = pubkey self.timestamp = ts - self.identifier = identifier + self.uid = uid @classmethod def from_inline(cls, version, currency, inline): @@ -27,22 +32,16 @@ class SelfCertification(Document): pubkey = selfcert_data.group(1) signature = selfcert_data.group(2) ts = int(selfcert_data.group(3)) - identifier = selfcert_data.group(4) - return cls(version, currency, pubkey, ts, identifier, signature) - - @classmethod - def from_raw(cls, raw): - #TODO : Parsing - return cls() - - def ts(self): - return "META:TS:{0}".format(self.timestamp) - - def uid(self): - return "UID:{0}".format(self.identifier) + uid = selfcert_data.group(4) + return cls(version, currency, pubkey, ts, uid, signature) def raw(self): - return "{0}\n{1}\n{2}".format(self.uid(), self.ts(), self.signatures[0]) + return """UID:{0} +META:TS:{1}""".format(self.uid(), self.ts()) + + def inline(self): + return "{0}:{1}:{2}:{3}".format(self.pubkey, self.signatures[0], + self.timestamp, self.uid) class Certification(Document): @@ -52,6 +51,7 @@ class Certification(Document): re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):\ ([1-9A-Za-z][^OIl]{42,45}):([0-9]+):([A-Za-z0-9+/]+(?:=|==)?)\n") + re_timestamp = re.compile("META:TS:([0-9]+)-([0-9a-fA-F]{5,40})\n") def __init__(self, version, currency, pubkey_from, pubkey_to, blockhash, blocknumber, signature): @@ -76,8 +76,11 @@ class Certification(Document): return cls(version, currency, pubkey_from, pubkey_to, blockhash, blocknumber, signature) - def ts(self): - return "META:TS:{0}-{1}".format(self.blockhash, self.blocknumber) - def raw(self, selfcert): - return "{0}\n{1}\n{2}".format(selfcert.raw(), self.ts(), self.signatures[0]) + return """{0} +META:TS:{1}-{2}""".format(selfcert.signed_raw(), self.blockhash, self.blocknumber) + + def inline(self): + return "{0}:{1}:{2}:{3}".format(self.pubkey_from, self.pubkey_to, + self.blocknumber, self.signatures[0]) + diff --git a/ucoinpy/documents/membership.py b/ucoinpy/documents/membership.py index cd355262..7dae36ca 100644 --- a/ucoinpy/documents/membership.py +++ b/ucoinpy/documents/membership.py @@ -25,18 +25,29 @@ class Membership(Document): # PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID re_inline = re.compile("([1-9A-Za-z][^OIl]{42,45}):([A-Za-z0-9+/]+(?:=|==)?):\ ([0-9]+):([0-9a-fA-F]{5,40}):([0-9]+):([^\n]+)\n") + re_type = re.compile("Type: (Membership)") + re_issuer = re.compile("Issuer: ([1-9A-Za-z][^OIl]{42,45})\n") + re_block = re.compile("Block: ([0-9]+)-([0-9a-fA-F]{5,40})\n") + re_membership_type = re.compile("Membership: (IN|OUT)") + re_userid = re.compile("UserID: ([^\n]+)\n") + re_certts = re.compile("CertTS: ([0-9]+)\n") + + def __init__(self, version, currency, issuer, block_number, block_hash, - membership_type, userid, cert_ts, signature): + membership_type, uid, cert_ts, signature): ''' Constructor ''' - super().__init__(version, currency, [signature]) + if signature: + super().__init__(version, currency, [signature]) + else: + super().__init__(version, currency, []) self.issuer = issuer self.block_number = block_number self.block_hash = block_hash self.membership_type = membership_type - self.userid = userid + self.uid = uid self.cert_ts = cert_ts @classmethod @@ -44,17 +55,49 @@ class Membership(Document): data = Membership.re_inline.match(inline) issuer = data.group(1) signature = data.group(2) - block_number = data.group(3) + block_number = int(data.group(3)) block_hash = data.group(4) - cert_ts = data.group(5) - userid = data.group(6) + cert_ts = int(data.group(5)) + uid = data.group(6) return cls(version, currency, issuer, block_number, - block_hash, membership_type, userid, cert_ts, signature) + block_hash, membership_type, uid, cert_ts, signature) @classmethod - def from_raw(cls, raw): - #TODO : Parsing - return cls() + def from_signed_raw(cls, raw, signature=None): + lines = raw.splitlines(True) + n = 0 + + version = int(Membership.re_version.match(lines[n]).group(1)) + n = n + 1 + + Membership.re_type.match(lines[n]).group(1) + n = n + 1 + + currency = Membership.re_currency.match(lines[n]).group(1) + n = n + 1 + + issuer = Membership.re_issuer.match(lines[n]).group(1) + n = n + 1 + + blockid = Membership.re_block.match(lines[n]) + blocknumber = int(blockid.group(1)) + blockhash = blockid.group(2) + n = n + 1 + + membership_type = Membership.re_membership_type.match(lines[n]).group(1) + n = n + 1 + + uid = Membership.re_userid.match(lines[n]).group(1) + n = n + 1 + + cert_ts = int(Membership.re_certts.match(lines[n]).group(1)) + n = n + 1 + + signature = Membership.re_signature.match(lines[n]).group(1) + n = n + 1 + + return cls(version, currency, issuer, blocknumber, blockhash, + membership_type, uid, cert_ts, signature) def raw(self): return """ @@ -65,19 +108,18 @@ Issuer: {2} Block: {3}-{4} Membership: {5} UserID: {6} -CertTS: {7} -{8}""".format(self.version, +CertTS: {7}""".format(self.version, self.currency, self.issuer, self.block_number, self.block_hash, self.membership_type, - self.userid, - self.cert_ts, - self.signatures[0]) + self.uid, + self.cert_ts) def inline(self): - return "{0}:{1}:{2}:{3}".format(self.issuer, - self.sign, + return "{0}:{1}:{2}:{3}:{4}:{5}".format(self.issuer, + self.signatures[0], self.block_number, self.block_hash, - self.cert_ts) + self.cert_ts, + self.uid) diff --git a/ucoinpy/documents/peer.py b/ucoinpy/documents/peer.py index e315a5f9..50eb939a 100644 --- a/ucoinpy/documents/peer.py +++ b/ucoinpy/documents/peer.py @@ -24,16 +24,54 @@ class Peer(Document): [...] """ - def __init__(self, version, currency, pubkey, blockid, endpoints, signature): - super().__init__(version, currency, [signature]) + re_type = re.compile("Type: (Peer)") + re_pubkey = re.compile("PublicKey: ([1-9A-Za-z][^OIl]{42,45})\n") + re_block = re.compile("Block: ([0-9]+-[0-9a-fA-F]{5,40})\n") + re_endpoints = re.compile("Endpoints:\n") + + def __init__(self, version, currency, pubkey, blockid, + endpoints, signature): + if signature: + super().__init__(version, currency, [signature]) + else: + super().__init__(version, currency, []) + self.pubkey = pubkey self.blockid = blockid self.endpoints = endpoints @classmethod - def from_raw(cls, raw): - #TODO : Parsing - return cls() + def from_signed_raw(cls, raw): + lines = raw.splitlines(True) + n = 0 + + version = int(Peer.re_version.match(lines[n]).group(1)) + n = n + 1 + + Peer.re_type.match(lines[n]).group(1) + n = n + 1 + + currency = Peer.re_currency.match(lines[n]).group(1) + n = n + 1 + + pubkey = Peer.re_pubkey.match(lines[n]).group(1) + n = n + 1 + + blockid = Peer.re_block.match(lines[n]).group(1) + n = n + 1 + + Peer.re_endpoints.match(lines[n]) + n = n + 1 + + endpoints = [] + while not Peer.re_signature.match(lines[n]): + endpoint = Endpoint.from_inline(lines[n]) + endpoints.append(endpoint) + n = n + 1 + + signature = Peer.re_signature.match(lines[n]).group(1) + + return cls(version, currency, pubkey, blockid, endpoints, signature) def raw(self): doc = """ @@ -63,18 +101,32 @@ class Endpoint(): if (inline.startswith(api)): if (api == "BASIC_MERKLED_API"): return BMAEndpoint.from_inline(inline) + return UnknownEndpoint.from_inline(inline) + + +class UnknownEndpoint(Endpoint): + + def __init__(self, api, properties): + self.api = api + self.properties = properties + + @classmethod + def from_inline(cls, inline): + api = inline.split()[0] + properties = inline.split()[1:] + return cls(api, properties) class BMAEndpoint(Endpoint): - re_inline = re.compile('^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]+))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$') + re_inline = re.compile('^BASIC_MERKLED_API(?: ([a-z_][a-z0-9-_.]+))?(?: ([0-9.]+))?(?: ([0-9a-f:]+))?(?: ([0-9]+))$') @classmethod def from_inline(cls, inline): m = BMAEndpoint.re_inline.match(inline) - server = m.group(2) - ipv4 = m.group(4) - ipv6 = m.group(6) - port = int(m.group(8)) + server = m.group(1) + ipv4 = m.group(2) + ipv6 = m.group(3) + port = int(m.group(4)) return cls(server, ipv4, ipv6, port) def __init__(self, server, ipv4, ipv6, port): diff --git a/ucoinpy/documents/status.py b/ucoinpy/documents/status.py index e12a6b3e..21df9013 100644 --- a/ucoinpy/documents/status.py +++ b/ucoinpy/documents/status.py @@ -4,8 +4,8 @@ Created on 2 déc. 2014 @author: inso ''' +import re from . import Document -from .. import PROTOCOL_VERSION class Status(Document): @@ -19,21 +19,58 @@ class Status(Document): To: RECIPIENT ''' + re_type = re.compile("Type: (Status)") + re_status = re.compile("Status: (NEW|NEW_BACK|UP|UP_BACK|DOWN)") + re_block = re.compile("Block: ([0-9]+-[0-9a-fA-F]{5,40})\n") + re_from = re.compile("From: ([1-9A-Za-z][^OIl]{42,45})\n") + re_to = re.compile("To: ([1-9A-Za-z][^OIl]{42,45})\n") + def __init__(self, version, currency, status, blockid, sender, recipient, signature): ''' Constructor ''' - super().__init__(version, currency, [signature]) + if signature: + super().__init__(version, currency, [signature]) + else: + super().__init__(version, currency, []) + self.status = status self.blockid = blockid self.sender = sender self.recipient = recipient @classmethod - def from_raw(cls, raw): - #TODO : Parsing - return cls() + def from_signed_raw(cls, raw): + lines = raw.splitlines(True) + n = 0 + + version = int(Status.re_version.match(lines[n]).group(1)) + n = n + 1 + + Status.re_type.match(lines[n]).group(1) + n = n + 1 + + currency = Status.re_currency.match(lines[n]).group(1) + n = n + 1 + + status = Status.re_status.match(lines[n]).group(1) + n = n + 1 + + blockid = Status.re_block.match(lines[n]).group(1) + n = n + 1 + + sender = Status.re_from.match(lines[n]).group(1) + n = n + 1 + + recipient = Status.re_to.match(lines[n]).group(1) + n = n + 1 + + signature = Status.re_signature.match(lines[n]).group(1) + n = n + 1 + + return cls(version, currency, status, blockid, + sender, recipient, signature) def raw(self): return ''' @@ -44,6 +81,5 @@ Status: {2} Block: {3} From: {4} To: {5} -{6} '''.format(self.version, self.currency, self.status, - self.blockid, self.sender, self.recipient, self.signatures[0]) + self.blockid, self.sender, self.recipient) diff --git a/ucoinpy/documents/transaction.py b/ucoinpy/documents/transaction.py index 6040f186..235a5a02 100644 --- a/ucoinpy/documents/transaction.py +++ b/ucoinpy/documents/transaction.py @@ -45,6 +45,8 @@ SIGNATURE re_issuers = re.compile("Issuers:\n") re_inputs = re.compile("Inputs:\n") re_outputs = re.compile("Outputs:\n") + re_compact_comment = re.compile("-----@@@-----([^\n]+)\n") + re_comment = re.compile("Comment: ([^\n]+)\n") re_pubkey = re.compile("([1-9A-Za-z][^OIl]{42,45})\n") def __init__(self, version, currency, issuers, inputs, outputs, @@ -52,7 +54,11 @@ SIGNATURE ''' Constructor ''' - super().__init__(version, currency, signatures) + if signatures: + super().__init__(version, currency, signatures) + else: + super().__init__(version, currency, []) + self.issuers = issuers self.inputs = inputs self.outputs = outputs @@ -90,10 +96,19 @@ SIGNATURE outputs.append(output_source) n = n + 1 - return cls(version, currency, issuers, inputs, outputs, None, signatures) + comment = None + if Transaction.re_comment.match(lines[n]): + comment = Transaction.re_compact_comment.match(lines[n]).group(1) + n = n + 1 + + while n < len(lines): + signatures.append(Transaction.re_signature.match(lines[n]).group(1)) + n = n + 1 + + return cls(version, currency, issuers, inputs, outputs, comment, signatures) @classmethod - def from_raw(cls, raw): + def from_signed_raw(cls, raw): lines = raw.splitlines(True) n = 0 @@ -126,18 +141,21 @@ SIGNATURE lines = lines + 1 if Transaction.re_outputs.match(lines[n]) is not None: - while Transaction.re_sign.match(lines[n]) is None: + while not Transaction.re_comment.match(lines[n]): output = OutputSource.from_inline(lines[n]) outputs.append(output) lines = lines + 1 + comment = Transaction.re_comment.match(lines[n]).group(1) + if Transaction.re_sign.match(lines[n]) is not None: while n < lines.len: sign = Transaction.re_sign.match(lines[n]).group(1) signatures.append(sign) lines = lines + 1 - return cls(version, currency, issuers, inputs, outputs, signatures) + return cls(version, currency, issuers, inputs, outputs, + comment, signatures) def raw(self): doc = """ @@ -242,15 +260,6 @@ class InputSource(): amount = int(data.group(5)) return cls(index, source, number, txhash, amount) - @classmethod - def from_compact(cls, number, compact): - data = InputSource.re_compact.match(compact) - index = int(data.group(1)) - source = data.group(2) - txhash = data.group(3) - amount = int(data.group(4)) - return cls(index, source, number, txhash, amount) - def inline(self): return "{0}:{1}:{2}:{3}:{4}".format(self.index, self.source, -- GitLab