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