From 00ccf3b4b3c7d381dba0dd5ad449931e516b01d0 Mon Sep 17 00:00:00 2001
From: vtexier <vit@free.fr>
Date: Wed, 26 Sep 2018 12:32:46 +0200
Subject: [PATCH] issue #52 Refactor Revocation document to respect superclass
 overload (break BC)

Revocation methods respect documents methods arguments
Revocation accept Identity or pubkey in constructor
Revocation created from inline needs to have self.identity populated later
to use raw/sign/signed_raw methods
---
 duniterpy/documents/certification.py  | 63 +++++++++++++++++----------
 examples/save_revoke_document.py      |  6 +--
 tests/documents/test_certification.py | 10 ++---
 3 files changed, 49 insertions(+), 30 deletions(-)

diff --git a/duniterpy/documents/certification.py b/duniterpy/documents/certification.py
index 2016dbe5..9b6c5bc9 100644
--- a/duniterpy/documents/certification.py
+++ b/duniterpy/documents/certification.py
@@ -328,9 +328,6 @@ CertTimestamp: {timestamp}
 RevocationType = TypeVar('RevocationType', bound='Revocation')
 
 
-# todo: Revocation document should be created with the revoked Identity document in arguments
-
-
 class Revocation(Document):
     """
     A document describing a self-revocation.
@@ -354,23 +351,28 @@ class Revocation(Document):
         "IdtySignature": re_idtysignature,
     }}
 
-    def __init__(self, version: int, currency: str, pubkey: str, signature: str) -> None:
+    def __init__(self, version: int, currency: str, identity: Union[Identity, str], signature: str) -> None:
         """
         Init Revocation instance
 
         :param version: Version number
         :param currency: Name of the currency
-        :param pubkey: Public key of the issuer
+        :param identity: Identity instance or identity pubkey
         :param signature: Signature
         """
         super().__init__(version, currency, [signature])
-        self.pubkey = pubkey
+
+        self.identity = identity if isinstance(identity, Identity) else None
+        self.pubkey = identity.pubkey if isinstance(identity, Identity) else identity
 
     @classmethod
     def from_inline(cls: Type[RevocationType], version: int, currency: str, inline: str) -> RevocationType:
         """
         Return Revocation document instance from inline string
 
+        Only self.pubkey is populated.
+        You must populate self.identity with an Identity instance to use raw/sign/signed_raw methods
+
         :param version: Version number
         :param currency: Name of the currency
         :param inline: Inline document
@@ -405,12 +407,23 @@ class Revocation(Document):
         n += 1
 
         issuer = Revocation.parse_field("Issuer", lines[n])
-        n += 4
+        n += 1
+
+        identity_uid = Revocation.parse_field("IdtyUniqueID", lines[n])
+        n += 1
+
+        identity_timestamp = Revocation.parse_field("IdtyTimestamp", lines[n])
+        n += 1
+
+        identity_signature = Revocation.parse_field("IdtySignature", lines[n])
+        n += 1
 
         signature = Revocation.parse_field("Signature", lines[n])
         n += 1
 
-        return cls(version, currency, issuer, signature)
+        identity = Identity(version, currency, issuer, identity_uid, identity_timestamp, identity_signature)
+
+        return cls(version, currency, identity, signature)
 
     @staticmethod
     def extract_self_cert(signed_raw: str) -> Identity:
@@ -454,13 +467,15 @@ class Revocation(Document):
         """
         return "{0}:{1}".format(self.pubkey, self.signatures[0])
 
-    def raw_for_revoked(self, revoked: Identity) -> str:
+    def raw(self) -> str:
         """
-        Return Revocation raw document string from Identity instance
+        Return Revocation raw document string
 
-        :param Identity revoked: Identity instance
         :return:
         """
+        if not isinstance(self.identity, Identity):
+            raise MalformedDocumentError("Can not return full revocation document created from inline")
+
         return """Version: {version}
 Type: Revocation
 Currency: {currency}
@@ -470,33 +485,37 @@ IdtyTimestamp: {timestamp}
 IdtySignature: {signature}
 """.format(version=self.version,
            currency=self.currency,
-           pubkey=revoked.pubkey,
-           uid=revoked.uid,
-           timestamp=revoked.timestamp,
-           signature=revoked.signatures[0])
+           pubkey=self.identity.pubkey,
+           uid=self.identity.uid,
+           timestamp=self.identity.timestamp,
+           signature=self.identity.signatures[0])
 
-    def sign_for_revoked(self, revoked: Identity, keys: list) -> None:
+    def sign(self, keys: list) -> None:
         """
         Sign the current document.
         Warning : current signatures will be replaced with the new ones.
 
-        :param revoked: Identity instance
         :param keys: List of libnacl key instances
         :return:
         """
+        if not isinstance(self.identity, Identity):
+            raise MalformedDocumentError("Can not return full revocation document created from inline")
+
         self.signatures = []
         for key in keys:
-            signing = base64.b64encode(key.signature(bytes(self.raw_for_revoked(revoked), 'ascii')))
+            signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii')))
             self.signatures.append(signing.decode("ascii"))
 
-    def signed_raw_for_revoked(self, revoked: Identity) -> str:
+    def signed_raw(self) -> str:
         """
-        Return Revocation signed raw document string for revoked Identity instance
+        Return Revocation signed raw document string
 
-        :param revoked: Identity instance
         :return:
         """
-        raw = self.raw_for_revoked(revoked)
+        if not isinstance(self.identity, Identity):
+            raise MalformedDocumentError("Can not return full revocation document created from inline")
+
+        raw = self.raw()
         signed = "\n".join(self.signatures)
         signed_raw = raw + signed + "\n"
         return signed_raw
diff --git a/examples/save_revoke_document.py b/examples/save_revoke_document.py
index 8522cfb8..4584d04c 100644
--- a/examples/save_revoke_document.py
+++ b/examples/save_revoke_document.py
@@ -84,11 +84,11 @@ def get_signed_raw_revocation_document(identity: Identity, salt: str, password:
 
     :rtype: str
     """
-    revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity.pubkey, "")
+    revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity, "")
 
     key = SigningKey(salt, password)
-    revocation.sign_for_revoked(identity, [key])
-    return revocation.signed_raw_for_revoked(identity)
+    revocation.sign([key])
+    return revocation.signed_raw()
 
 
 async def main():
diff --git a/tests/documents/test_certification.py b/tests/documents/test_certification.py
index 9bc3ad1c..7372f7c4 100644
--- a/tests/documents/test_certification.py
+++ b/tests/documents/test_certification.py
@@ -126,10 +126,10 @@ SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneN
         currency = "beta_brousouf"
         pubkey = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
         signature = "SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk"
-        revokation = Revocation(version, currency, pubkey, signature)
-        selfcert = Identity(version, currency, pubkey, "lolcat",
+        identity = Identity(version, currency, pubkey, "lolcat",
                             BlockUID(32, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD"),
                                      "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci")
+        revokation = Revocation(version, currency, identity, signature)
 
         result = """Version: 2
 Type: Revocation
@@ -140,7 +140,7 @@ IdtyTimestamp: 32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
 IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci
 SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
 """
-        self.assertEqual(revokation.signed_raw_for_revoked(selfcert), result)
+        self.assertEqual(revokation.signed_raw(), result)
 
     def test_revokation_from_signed_raw(self):
         signed_raw = """Version: 2
@@ -153,5 +153,5 @@ IdtySignature: J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6B
 SoKwoa8PFfCDJWZ6dNCv7XstezHcc2BbKiJgVDXv82R5zYR83nis9dShLgWJ5w48noVUHimdngzYQneNYSMV3rk
 """
         revocation = Revocation.from_signed_raw(signed_raw)
-        selfcert = Revocation.extract_self_cert(signed_raw)
-        self.assertEqual(revocation.signed_raw_for_revoked(selfcert), signed_raw)
+        self.assertTrue(isinstance(Revocation.extract_self_cert(signed_raw), Identity))
+        self.assertEqual(revocation.signed_raw(), signed_raw)
-- 
GitLab