diff --git a/_ucoinpy_test/documents/test_status.py b/_ucoinpy_test/documents/test_status.py deleted file mode 100644 index a995b01cfee2984396939c5b4cd291abbfcdaf53..0000000000000000000000000000000000000000 --- a/_ucoinpy_test/documents/test_status.py +++ /dev/null @@ -1,38 +0,0 @@ -''' -Created on 13 déc. 2014 - -@author: inso -''' -import unittest -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(unittest.TestCase): - def test_fromraw(self): - status = Status.from_signed_raw(raw_status) - self.assertEqual(status.status, 'UP') - self.assertEqual(status.blockid, "8-1922C324ABC4AF7EF7656734A31F5197888DDD52") - self.assertEqual(status.sender, "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY") - self.assertEqual(status.recipient, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") - self.assertEqual(status.signatures[0], "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==") - - def test_fromraw_toraw(self): - status = Status.from_signed_raw(raw_status) - rendered_status = status.signed_raw() - from_rendered_status = Status.from_signed_raw(rendered_status) - self.assertEqual(from_rendered_status.status, 'UP') - self.assertEqual(from_rendered_status.blockid, "8-1922C324ABC4AF7EF7656734A31F5197888DDD52") - self.assertEqual(from_rendered_status.sender, "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY") - self.assertEqual(from_rendered_status.recipient, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") - self.assertEqual(from_rendered_status.signatures[0], "dkaXIiCYUJtCg8Feh/BKvPYf4uFH9CJ/zY6J4MlA9BsjmcMe4YAblvNt/gJy31b1aGq3ue3h14mLMCu84rraDg==") - diff --git a/ucoinpy/__init__.py b/ucoinpy/__init__.py index c0e54afbce24808a7f8f86b4dbd40484f9f1a7a0..1efe04b77f4f7ad617ddaafafedc9f3f4cd190f8 100644 --- a/ucoinpy/__init__.py +++ b/ucoinpy/__init__.py @@ -21,7 +21,7 @@ PROTOCOL_VERSION="1" MANAGED_API=["BASIC_MERKLED_API"] __author__ = 'Caner Candan & inso' -__version__ = '0.14.2' +__version__ = '0.14.3' __nonsense__ = 'uCoin' from . import api, documents, key \ No newline at end of file diff --git a/ucoinpy/documents/__init__.py b/ucoinpy/documents/__init__.py index 2291e35a028452620bc12c2101ee2fd5a4bb8220..e229fd5fca387d0a527a901afe81c1f55d45a40f 100644 --- a/ucoinpy/documents/__init__.py +++ b/ucoinpy/documents/__init__.py @@ -2,6 +2,5 @@ from .block import Block, BlockId from .certification import SelfCertification, Certification from .membership import Membership from .peer import Endpoint, BMAEndpoint, UnknownEndpoint, Peer -from .status import Status from .transaction import SimpleTransaction, Transaction -from .document import Document \ No newline at end of file +from .document import Document, MalformedDocumentError \ No newline at end of file diff --git a/ucoinpy/documents/block.py b/ucoinpy/documents/block.py index a7c4e35a1c434f1b878f1b0ff9ef8b91587b3a6d..6341118620e25d2180af3cddfc5bdb13acce9fd7 100644 --- a/ucoinpy/documents/block.py +++ b/ucoinpy/documents/block.py @@ -1,4 +1,4 @@ -from .document import Document +from .document import Document, MalformedDocumentError from .certification import SelfCertification, Certification from .membership import Membership from .transaction import Transaction @@ -28,6 +28,8 @@ class BlockId: :param str blockid: The block id """ data = blockid.split("-") + if len(data) != 2: + raise MalformedDocumentError('BlockId') number = int(data[0]) sha_hash = data[1] return cls(number, sha_hash) @@ -107,6 +109,28 @@ The class Block handles Block documents. re_certifications = re.compile("Certifications:\n") re_transactions = re.compile("Transactions:\n") + fields_parsers = {**Document.fields_parsers, **{ + 'Type': re_type, + 'Noonce': re_noonce, + 'Number': re_number, + 'PoWMin': re_powmin, + 'Time': re_time, + 'MedianTime': re_mediantime, + 'UD': re_universaldividend, + 'Issuer': re_issuer, + 'PreviousIssuer': re_previousissuer, + 'PreviousHash': re_previoushash, + 'Parameters': re_parameters, + 'MembersCount': re_memberscount, + 'Identities': re_identities, + 'Joiners': re_joiners, + 'Actives': re_actives, + 'Leavers': re_leavers, + 'Certifications': re_certifications, + 'Transactions': re_transactions + } + } + Empty_Hash = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709" def __init__(self, version, currency, noonce, number, powmin, time, @@ -167,54 +191,54 @@ The class Block handles Block documents. lines = raw.splitlines(True) n = 0 - version = int(Block.re_version.match(lines[n]).group(1)) - n = n + 1 + version = int(Block.parse_field("Version", lines[n])) + n += 1 - Block.re_type.match(lines[n]).group(1) - n = n + 1 + Block.parse_field("Type", lines[n]) + n += 1 - currency = Block.re_currency.match(lines[n]).group(1) - n = n + 1 + currency = Block.parse_field("Currency", lines[n]) + n += 1 - noonce = int(Block.re_noonce.match(lines[n]).group(1)) - n = n + 1 + noonce = int(Block.parse_field("Noonce", lines[n])) + n += 1 - number = int(Block.re_number.match(lines[n]).group(1)) - n = n + 1 + number = int(Block.parse_field("Number", lines[n])) + n += 1 - powmin = int(Block.re_powmin.match(lines[n]).group(1)) - n = n + 1 + powmin = int(Block.parse_field("PoWMin", lines[n])) + n += 1 - time = int(Block.re_time.match(lines[n]).group(1)) - n = n + 1 + time = int(Block.parse_field("Time", lines[n])) + n += 1 - mediantime = int(Block.re_mediantime.match(lines[n]).group(1)) - n = n + 1 + mediantime = int(Block.parse_field("MedianTime", lines[n])) + n += 1 ud = Block.re_universaldividend.match(lines[n]) if ud is not None: ud = int(ud.group(1)) - n = n + 1 + n += 1 - issuer = Block.re_issuer.match(lines[n]).group(1) - n = n + 1 + issuer = Block.parse_field("Issuer", lines[n]) + n += 1 prev_hash = None prev_issuer = None if number > 0: - prev_hash = Block.re_previoushash.match(lines[n]).group(1) - n = n + 1 + prev_hash = Block.parse_field("PreviousHash", lines[n]) + n += 1 - prev_issuer = Block.re_previousissuer.match(lines[n]).group(1) - n = n + 1 + prev_issuer = Block.parse_field("PreviousIssuer", lines[n]) + n += 1 parameters = None if number == 0: parameters = Block.re_parameters.match(lines[n]).groups() - n = n + 1 + n += 1 - members_count = int(Block.re_memberscount.match(lines[n]).group(1)) - n = n + 1 + members_count = int(Block.parse_field("MembersCount", lines[n])) + n += 1 identities = [] joiners = [] @@ -225,50 +249,50 @@ The class Block handles Block documents. transactions = [] if Block.re_identities.match(lines[n]) is not None: - n = n + 1 + n += 1 while Block.re_joiners.match(lines[n]) is None: selfcert = SelfCertification.from_inline(version, currency, lines[n]) identities.append(selfcert) - n = n + 1 + n += 1 if Block.re_joiners.match(lines[n]): - n = n + 1 + n += 1 while Block.re_actives.match(lines[n]) is None: membership = Membership.from_inline(version, currency, "IN", lines[n]) joiners.append(membership) - n = n + 1 + n += 1 if Block.re_actives.match(lines[n]): - n = n + 1 + n += 1 while Block.re_leavers.match(lines[n]) is None: membership = Membership.from_inline(version, currency, "IN", lines[n]) actives.append(membership) - n = n + 1 + n += 1 if Block.re_leavers.match(lines[n]): - n = n + 1 + n += 1 while Block.re_excluded.match(lines[n]) is None: membership = Membership.from_inline(version, currency, "OUT", lines[n]) leavers.append(membership) - n = n + 1 + n += 1 if Block.re_excluded.match(lines[n]): - n = n + 1 + n += 1 while Block.re_certifications.match(lines[n]) is None: membership = Block.re_exclusion.match(lines[n]).group(1) excluded.append(membership) - n = n + 1 + n += 1 if Block.re_certifications.match(lines[n]): - n = n + 1 + n += 1 while Block.re_transactions.match(lines[n]) is None: certification = Certification.from_inline(version, currency, prev_hash, lines[n]) certifications.append(certification) - n = n + 1 + n += 1 if Block.re_transactions.match(lines[n]): - n = n + 1 + n += 1 while not Block.re_signature.match(lines[n]): tx_lines = "" header_data = Transaction.re_header.match(lines[n]) @@ -277,14 +301,14 @@ The class Block handles Block documents. inputs_num = int(header_data.group(3)) outputs_num = int(header_data.group(4)) has_comment = int(header_data.group(5)) - tx_max = n+issuers_num*2+inputs_num+outputs_num+has_comment+1 + tx_max = n + issuers_num * 2 + inputs_num + outputs_num + has_comment + 1 for i in range(n, tx_max): tx_lines += lines[n] - n = n + 1 + n += 1 transaction = Transaction.from_compact(currency, tx_lines) transactions.append(transaction) - signature = Block.re_signature.match(lines[n]).group(1) + signature = Block.parse_field("Signature", lines[n]) return cls(version, currency, noonce, number, powmin, time, mediantime, ud, issuer, prev_hash, prev_issuer, diff --git a/ucoinpy/documents/certification.py b/ucoinpy/documents/certification.py index 96a4a81f9bbf8fcd1f4e13881048be45dfa811de..33349b4b232c66513fb5d9b7d9ee438ab0d64508 100644 --- a/ucoinpy/documents/certification.py +++ b/ucoinpy/documents/certification.py @@ -2,7 +2,7 @@ import re import base64 import logging -from .document import Document +from .document import Document, MalformedDocumentError class SelfCertification(Document): @@ -26,6 +26,8 @@ class SelfCertification(Document): @classmethod def from_inline(cls, version, currency, inline): selfcert_data = SelfCertification.re_inline.match(inline) + if selfcert_data is None: + raise MalformedDocumentError("Inline self certification") pubkey = selfcert_data.group(1) signature = selfcert_data.group(2) ts = int(selfcert_data.group(3)) @@ -63,6 +65,8 @@ class Certification(Document): @classmethod def from_inline(cls, version, currency, blockhash, inline): cert_data = Certification.re_inline.match(inline) + if cert_data is None: + raise MalformedDocumentError("Certification") pubkey_from = cert_data.group(1) pubkey_to = cert_data.group(2) blocknumber = int(cert_data.group(3)) diff --git a/ucoinpy/documents/document.py b/ucoinpy/documents/document.py index 8298d181f550be7071a1c9407099f1816b998097..c390bdee5aa3ba66864d9af834b6a05612a77b9d 100644 --- a/ucoinpy/documents/document.py +++ b/ucoinpy/documents/document.py @@ -1,15 +1,42 @@ -import base58 import base64 import re import logging import hashlib +class MalformedDocumentError(Exception): + """ + Malformed document exception + """ + def __init__(self, field_name): + super().__init__("Could not parse field {0}".format(field_name)) + + 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") + fields_parsers = { + "Version": re_version, + "Currency": re_currency, + "Signature": re_signature + } + + @classmethod + def parse_field(cls, field_name, line): + """ + + :param field_name: + :param line: + :return: + """ + try: + value = cls.fields_parsers[field_name].match(line).group(1) + except AttributeError: + raise MalformedDocumentError(field_name) + return value + def __init__(self, version, currency, signatures): self.version = version self.currency = currency diff --git a/ucoinpy/documents/membership.py b/ucoinpy/documents/membership.py index ca9b89978c7ca07aeedc19af1ad6794bed626e42..7cc787576e1bb580e1329f431ab3e728b8100ae9 100644 --- a/ucoinpy/documents/membership.py +++ b/ucoinpy/documents/membership.py @@ -3,7 +3,7 @@ Created on 2 déc. 2014 @author: inso """ -from .document import Document +from .document import Document, MalformedDocumentError import re @@ -28,11 +28,20 @@ class Membership(Document): ([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_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") + fields_parsers = {**Document.fields_parsers, **{ + "Type": re_type, + "Issuer": re_issuer, + "Block": re_block, + "Membership": re_membership_type, + "UserID": re_userid, + "CertTS": re_certts + }} + def __init__(self, version, currency, issuer, blockid, membership_type, uid, cert_ts, signature): """ @@ -48,6 +57,8 @@ class Membership(Document): @classmethod def from_inline(cls, version, currency, membership_type, inline): data = Membership.re_inline.match(inline) + if data is None: + raise MalformedDocumentError("Inline membership") issuer = data.group(1) signature = data.group(2) block_number = int(data.group(3)) @@ -59,40 +70,38 @@ class Membership(Document): @classmethod def from_signed_raw(cls, raw, signature=None): + from .block import BlockId lines = raw.splitlines(True) n = 0 - version = int(Membership.re_version.match(lines[n]).group(1)) - n = n + 1 + version = int(Membership.parse_field("Version", lines[n])) + n += 1 - Membership.re_type.match(lines[n]).group(1) - n = n + 1 + Membership.parse_field("Type", lines[n]) + n += 1 - currency = Membership.re_currency.match(lines[n]).group(1) - n = n + 1 + currency = Membership.parse_field("Currency", lines[n]) + n += 1 - issuer = Membership.re_issuer.match(lines[n]).group(1) - n = n + 1 + issuer = Membership.parse_field("Issuer", lines[n]) + n += 1 - blockid = Membership.re_block.match(lines[n]) - blocknumber = int(blockid.group(1)) - blockhash = blockid.group(2) - n = n + 1 + blockid = BlockId.from_str(Membership.parse_field("Block", lines[n])) + n += 1 - membership_type = Membership.re_membership_type.match(lines[n]).group(1) - n = n + 1 + membership_type = Membership.parse_field("Membership", lines[n]) + n += 1 - uid = Membership.re_userid.match(lines[n]).group(1) - n = n + 1 + uid = Membership.parse_field("UserID", lines[n]) + n += 1 - cert_ts = int(Membership.re_certts.match(lines[n]).group(1)) - n = n + 1 + cert_ts = int(Membership.parse_field("CertTS", lines[n])) + n += 1 - signature = Membership.re_signature.match(lines[n]).group(1) - n = n + 1 + signature = Membership.parse_field("Signature", lines[n]) + n += 1 - from .block import BlockId - return cls(version, currency, issuer, BlockId(blocknumber, blockhash), + return cls(version, currency, issuer, blockid, membership_type, uid, cert_ts, signature) def raw(self): diff --git a/ucoinpy/documents/peer.py b/ucoinpy/documents/peer.py index 838c55b3e476673650f3c45950eaea88dea5e05b..5a2cf0ae3ac80ee20b056917f56a2d8aaa00fcec 100644 --- a/ucoinpy/documents/peer.py +++ b/ucoinpy/documents/peer.py @@ -1,7 +1,7 @@ import re from ..api.bma import ConnectionHandler -from .document import Document +from .document import Document, MalformedDocumentError from . import BlockId from .. import PROTOCOL_VERSION, MANAGED_API @@ -26,7 +26,14 @@ class Peer(Document): 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") + re_endpoints = re.compile("(Endpoints:)\n") + + fields_parsers = {**Document.fields_parsers, **{ + "Type": re_type, + "Pubkey": re_pubkey, + "Block": re_block, + "Endpoints": re_endpoints + }} def __init__(self, version, currency, pubkey, blockid, endpoints, signature): @@ -41,29 +48,29 @@ class Peer(Document): lines = raw.splitlines(True) n = 0 - version = int(Peer.re_version.match(lines[n]).group(1)) - n = n + 1 + version = int(Peer.parse_field("Version", lines[n])) + n += 1 - Peer.re_type.match(lines[n]).group(1) - n = n + 1 + Peer.parse_field("Type", lines[n]) + n += 1 - currency = Peer.re_currency.match(lines[n]).group(1) - n = n + 1 + currency = Peer.parse_field("Currency", lines[n]) + n += 1 - pubkey = Peer.re_pubkey.match(lines[n]).group(1) - n = n + 1 + pubkey = Peer.parse_field("Pubkey", lines[n]) + n += 1 - blockid = BlockId.from_str(Peer.re_block.match(lines[n]).group(1)) - n = n + 1 + blockid = BlockId.from_str(Peer.parse_field("Block", lines[n])) + n += 1 - Peer.re_endpoints.match(lines[n]) - n = n + 1 + Peer.parse_field("Endpoints", lines[n]) + n += 1 endpoints = [] while not Peer.re_signature.match(lines[n]): endpoint = Endpoint.from_inline(lines[n]) endpoints.append(endpoint) - n = n + 1 + n += 1 signature = Peer.re_signature.match(lines[n]).group(1) @@ -123,6 +130,8 @@ class BMAEndpoint(Endpoint): @classmethod def from_inline(cls, inline): m = BMAEndpoint.re_inline.match(inline) + if m is None: + raise MalformedDocumentError("BMAEndpoint") server = m.group(1) ipv4 = m.group(2) ipv6 = m.group(3) diff --git a/ucoinpy/documents/status.py b/ucoinpy/documents/status.py deleted file mode 100644 index 547683a58fa811b76c415694f6f7d10d473e2093..0000000000000000000000000000000000000000 --- a/ucoinpy/documents/status.py +++ /dev/null @@ -1,78 +0,0 @@ -import re -from .document import Document - - -class Status(Document): - """ -.. note:: A status document is specified by the following format : - - | Version: VERSION - | Type: Status - | Currency: CURRENCY_NAME - | Status: STATUS - | Block: BLOCK - | From: SENDER - | 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]) - - self.status = status - self.blockid = blockid - self.sender = sender - self.recipient = recipient - - @classmethod - 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 """Version: {0} -Type: Status -Currency: {1} -Status: {2} -Block: {3} -From: {4} -To: {5} -""".format(self.version, self.currency, self.status, - self.blockid, self.sender, self.recipient) diff --git a/ucoinpy/documents/transaction.py b/ucoinpy/documents/transaction.py index d9b532d22d7615e17514c808ca1e143684797b3c..0bca5d368f3f34e7425b9a02a1fe27491c7d355a 100644 --- a/ucoinpy/documents/transaction.py +++ b/ucoinpy/documents/transaction.py @@ -1,6 +1,5 @@ -from .document import Document +from .document import Document, MalformedDocumentError import re -import logging class Transaction(Document): @@ -47,6 +46,18 @@ class Transaction(Document): re_comment = re.compile("Comment: ([^\n]*)\n") re_pubkey = re.compile("([1-9A-Za-z][^OIl]{42,45})\n") + fields_parsers = {**Document.fields_parsers, **{ + "Type": re_type, + "TX": re_header, + "Issuers": re_issuers, + "Inputs": re_inputs, + "Outputs": re_outputs, + "Comment": re_comment, + "Compact comment": re_compact_comment, + "Pubkey": re_pubkey + } + } + def __init__(self, version, currency, issuers, inputs, outputs, comment, signatures): """ @@ -65,40 +76,42 @@ class Transaction(Document): n = 0 header_data = Transaction.re_header.match(lines[n]) + if header_data is None: + raise MalformedDocumentError("Compact TX header") version = int(header_data.group(1)) issuers_num = int(header_data.group(2)) inputs_num = int(header_data.group(3)) outputs_num = int(header_data.group(4)) has_comment = int(header_data.group(5)) - n = n + 1 + n += 1 issuers = [] inputs = [] outputs = [] signatures = [] for i in range(0, issuers_num): - issuer = Transaction.re_pubkey.match(lines[n]).group(1) + issuer = Transaction.parse_field("Pubkey", lines[n]) issuers.append(issuer) - n = n + 1 + n += 1 for i in range(0, inputs_num): input_source = InputSource.from_inline(lines[n]) inputs.append(input_source) - n = n + 1 + n += 1 for i in range(0, outputs_num): output_source = OutputSource.from_inline(lines[n]) outputs.append(output_source) - n = n + 1 + n += 1 comment = "" if has_comment == 1: comment = Transaction.re_compact_comment.match(lines[n]).group(1) - n = n + 1 + n += 1 while n < len(lines): signatures.append(Transaction.re_signature.match(lines[n]).group(1)) - n = n + 1 + n += 1 return cls(version, currency, issuers, inputs, outputs, comment, signatures) @@ -107,14 +120,14 @@ class Transaction(Document): lines = raw.splitlines(True) n = 0 - version = int(Transaction.re_version.match(lines[n]).group(1)) - n = n + 1 + version = int(Transaction.parse_field("Version", lines[n])) + n += 1 - Transaction.re_type.match(lines[n]).group(1) - n = n + 1 + Transaction.parse_field("Type", lines[n]) + n += 1 - currency = Transaction.re_currency.match(lines[n]).group(1) - n = n + 1 + currency = Transaction.parse_field("Currency", lines[n]) + n += 1 issuers = [] inputs = [] @@ -122,34 +135,34 @@ class Transaction(Document): signatures = [] if Transaction.re_issuers.match(lines[n]): - n = n + 1 + n += 1 while Transaction.re_inputs.match(lines[n]) is None: - issuer = Transaction.re_pubkey.match(lines[n]).group(1) + issuer = Transaction.parse_field("Pubkey", lines[n]) issuers.append(issuer) - n = n + 1 + n += 1 if Transaction.re_inputs.match(lines[n]): - n = n + 1 + n += 1 while Transaction.re_outputs.match(lines[n]) is None: input_source = InputSource.from_inline(lines[n]) inputs.append(input_source) - n = n + 1 + n += 1 if Transaction.re_outputs.match(lines[n]) is not None: - n = n + 1 + n += 1 while not Transaction.re_comment.match(lines[n]): output = OutputSource.from_inline(lines[n]) outputs.append(output) - n = n + 1 + n += 1 - comment = Transaction.re_comment.match(lines[n]).group(1) - n = n + 1 + comment = Transaction.parse_field("Comment", lines[n]) + n += 1 if Transaction.re_signature.match(lines[n]) is not None: while n < len(lines): - sign = Transaction.re_signature.match(lines[n]).group(1) + sign = Transaction.parse_field("Signature", lines[n]) signatures.append(sign) - n = n + 1 + n += 1 return cls(version, currency, issuers, inputs, outputs, comment, signatures) @@ -245,6 +258,8 @@ class InputSource: @classmethod def from_inline(cls, inline): data = InputSource.re_inline.match(inline) + if data is None: + raise MalformedDocumentError("Inline input") index = int(data.group(1)) source = data.group(2) number = int(data.group(3)) @@ -282,6 +297,8 @@ class OutputSource(): @classmethod def from_inline(cls, inline): data = OutputSource.re_inline.match(inline) + if data is None: + raise MalformedDocumentError("Inline output") pubkey = data.group(1) amount = int(data.group(2)) return cls(pubkey, amount)