diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a9c77b09f9657625994ae5354d04a5b8837b6f0..8b880b7144c75c9830863559d9ccb04ff086609d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,22 @@
+## v0.54.3 (29th May 2019)
+- Upload again to PyPi as previous release haven’t been uploaded thanks to the tag
+- Transaction: fix `time` type
+
+## v0.54.2 (27th May 2019)
+- fix Transaction document generation
+- lock transaction document generation with a test
+
+## v0.54.1 (9th May 2019)
+- `Transaction`: add __eq__() and __hash__() methods
+- Transaction Unlock parameters: add __eq__() and __hash__() methods
+- Transaction: add 'time' variable for read and write but not for doc generation
+- output conditions: add __eq__() and __hash__() methods
+- test transaction equality at all levels
+
+---
+
+- Thanks @Moul, @vtexier
+
 ## v0.54.0 (5th May 2019)
 
 ### Code/tests
@@ -16,6 +35,8 @@
 - setup.py: add classifiers: Python versions, Intended Audience
 - Add CHANGELOG.md from v0.53.1
 
+---
+
 - Thanks @Moul, @vtexier
 
 ## v0.53.1 (18 April 2019)
diff --git a/docs/conf.py b/docs/conf.py
index 2dc8df5a8aa38e8e307ce854c27228f6962ca4a7..de0cd0369be407c984d536a56a597a2ab6e8ee50 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -72,9 +72,9 @@ author = 'caner & inso & vit'
 # built documents.
 #
 # The short X.Y version.
-version = '0.54.0'
+version = '0.54.3'
 # The full version, including alpha/beta/rc tags.
-release = '0.54.0'
+release = '0.54.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/duniterpy/__init__.py b/duniterpy/__init__.py
index ce53672d2fc36a4b80a67be14b51fe580d31ca40..c4b422a824c33ef9d9e2298ace3b014cda202bcd 100644
--- a/duniterpy/__init__.py
+++ b/duniterpy/__init__.py
@@ -18,7 +18,7 @@
 
 
 __author__      = 'Caner Candan & inso & vit'
-__version__     = '0.54.0'
+__version__     = '0.54.3'
 __nonsense__    = 'duniter'
 
 from . import api, documents, key
diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py
index 5545d16b7994dd597ced99a4c6dbaaeefd9bb01c..95417e49d388c9952c6902e2d7264dc82458746a 100644
--- a/duniterpy/documents/transaction.py
+++ b/duniterpy/documents/transaction.py
@@ -230,6 +230,19 @@ class SIGParameter:
         """
         self.index = index
 
+
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check SIGParameter instances equality
+        """
+        if not isinstance(other, SIGParameter):
+            return NotImplemented
+        return self.index == other.index
+
+    def __hash__(self) -> int:
+        return hash((self.index))
+
+
     @classmethod
     def from_parameter(cls: Type[SIGParameterType], parameter: str) -> Optional[SIGParameterType]:
         """
@@ -272,6 +285,19 @@ class XHXParameter:
         """
         self.integer = integer
 
+
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check XHXParameter instances equality
+        """
+        if not isinstance(other, XHXParameter):
+            return NotImplemented
+        return self.integer == other.integer
+
+    def __hash__(self) -> int:
+        return hash((self.integer))
+
+
     @classmethod
     def from_parameter(cls: Type[XHXParameterType], parameter: str) -> Optional[XHXParameterType]:
         """
@@ -348,6 +374,24 @@ class Unlock:
         self.index = index
         self.parameters = parameters
 
+
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check Unlock instances equality
+        """
+        if not isinstance(other, Unlock):
+            return NotImplemented
+
+        params_equals = True
+        for spar, opar in zip(self.parameters, other.parameters):
+            if spar != opar:
+                params_equals = False
+        return self.index == other.index and params_equals
+
+    def __hash__(self) -> int:
+        return hash((self.index, self.parameters))
+
+
     @classmethod
     def from_inline(cls: Type[UnlockType], inline: str) -> UnlockType:
         """
@@ -448,7 +492,7 @@ class Transaction(Document):
 
     def __init__(self, version: int, currency: str, blockstamp: Optional[BlockUID], locktime: int, issuers: List[str],
                  inputs: List[InputSource], unlocks: List[Unlock], outputs: List[OutputSource],
-                 comment: str, signatures: List[str]) -> None:
+                 comment: str, signatures: List[str], time: Optional[int] = None) -> None:
         """
         Init Transaction instance
 
@@ -461,6 +505,7 @@ class Transaction(Document):
         :param unlocks: List of Unlock instances
         :param outputs: List of OutputSource instances
         :param comment: Comment field
+        :param time: time when the transaction enters the blockchain
         :param signatures: List of signatures
         """
         super().__init__(version, currency, signatures)
@@ -471,6 +516,31 @@ class Transaction(Document):
         self.unlocks = unlocks
         self.outputs = outputs
         self.comment = comment
+        self.time = time
+
+
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check Transaction instances equality
+        """
+        if not isinstance(other, Transaction):
+            return NotImplemented
+        return self.version == other.version and \
+            self.currency == other.currency and \
+            self.signatures == other.signatures and \
+            self.blockstamp == other.blockstamp and \
+            self.locktime == other.locktime and \
+            self.issuers == other.issuers and \
+            self.inputs == other.inputs and \
+            self.unlocks == other.unlocks and \
+            self.outputs == other.outputs and \
+            self.comment == other.comment and \
+            self.time == other.time
+
+
+    def __hash__(self) -> int:
+        return hash((self.version, self.currency, self.signatures, self.blockstamp, self.locktime, self.issuers, self.inputs, self.unlocks, self.outputs, self.comment, self.time))
+
 
     @classmethod
     def from_bma_history(cls: Type[TransactionType], currency: str, tx_data: Dict) -> TransactionType:
@@ -501,7 +571,7 @@ Outputs:
 {multiline_outputs}
 Comment: {comment}
 {multiline_signatures}
-""".format(**tx_data))
+""".format(**tx_data), tx_data["time"])
 
     @classmethod
     def from_compact(cls: Type[TransactionType], currency: str, compact: str) -> TransactionType:
@@ -575,11 +645,12 @@ Comment: {comment}
         return cls(version, currency, blockstamp, locktime, issuers, inputs, unlocks, outputs, comment, signatures)
 
     @classmethod
-    def from_signed_raw(cls: Type[TransactionType], raw: str) -> TransactionType:
+    def from_signed_raw(cls: Type[TransactionType], raw: str, time: int = 0) -> TransactionType:
         """
         Return a Transaction instance from a raw string format
 
         :param raw: Raw string format
+        :param time: time when the transaction enters the blockchain
 
         :return:
         """
@@ -645,7 +716,7 @@ Comment: {comment}
                 n += 1
 
         return cls(version, currency, blockstamp, locktime, issuers, inputs, unlocks, outputs,
-                   comment, signatures)
+                   comment, signatures, time)
 
     def raw(self) -> str:
         """
@@ -732,7 +803,7 @@ class SimpleTransaction(Transaction):
 
     def __init__(self, version: int, currency: str, blockstamp: BlockUID, locktime: int, issuer: str,
                  single_input: InputSource, unlocks: List[Unlock], outputs: List[OutputSource], comment: str,
-                 signature: str) -> None:
+                 signature: str, time: int) -> None:
         """
         Init instance
 
@@ -745,10 +816,11 @@ class SimpleTransaction(Transaction):
         :param unlocks: List of Unlock instances
         :param outputs: List of OutputSource instances
         :param comment: Comment field
+        :param time: time when the transaction enters the blockchain
         :param signature: Signature
         """
         super().__init__(version, currency, blockstamp, locktime, [issuer], [single_input], unlocks,
-                         outputs, comment, [signature])
+                         outputs, comment, [signature], time)
 
     @staticmethod
     def is_simple(tx: Transaction) -> bool:
diff --git a/duniterpy/grammars/output.py b/duniterpy/grammars/output.py
index c180f5307e809cd96d8833cc0078245c64180715..ef4b7f5bfc0985271e13ce5fad048ea3d91baaef 100644
--- a/duniterpy/grammars/output.py
+++ b/duniterpy/grammars/output.py
@@ -48,6 +48,19 @@ class SIG:
     def __str__(self) -> str:
         return self.value
 
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check SIG instances equality
+        """
+        if not isinstance(other, SIG):
+            return NotImplemented
+        return self.value == other.value and \
+            self.pubkey == other.pubkey
+
+    def __hash__(self) -> int:
+        return hash((self.value, self.pubkey))
+
+
     @classmethod
     def token(cls: Type[SIGType], pubkey: str) -> SIGType:
         """
@@ -94,6 +107,19 @@ class CSV:
     def __str__(self) -> str:
         return self.value
 
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check CSV instances equality
+        """
+        if not isinstance(other, CSV):
+            return NotImplemented
+        return self.value == other.value and \
+            self.time == other.time
+
+    def __hash__(self) -> int:
+        return hash((self.value, self.time))
+
+
     @classmethod
     def token(cls: Type[CSVType], time: int) -> CSVType:
         """
@@ -139,6 +165,19 @@ class CLTV:
     def __str__(self) -> str:
         return self.value
 
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check CLTV instances equality
+        """
+        if not isinstance(other, CLTV):
+            return NotImplemented
+        return self.value == other.value and \
+            self.timestamp == other.timestamp
+
+    def __hash__(self) -> int:
+        return hash((self.value, self.timestamp))
+
+
     @classmethod
     def token(cls: Type[CLTVType], timestamp: int) -> CLTVType:
         """
@@ -184,6 +223,19 @@ class XHX:
     def __str__(self) -> str:
         return self.value
 
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check XHX instances equality
+        """
+        if not isinstance(other, XHX):
+            return NotImplemented
+        return self.value == other.value and \
+            self.sha_hash == other.sha_hash
+
+    def __hash__(self) -> int:
+        return hash((self.value, self.sha_hash))
+
+
     @classmethod
     def token(cls: Type[XHXType], sha_hash: str) -> XHXType:
         """
@@ -262,6 +314,21 @@ class Condition:
         self.right = ''
         self.op = ''
 
+
+    def __eq__(self, other: Any) -> bool:
+        """
+        Check Condition instances equality
+        """
+        if not isinstance(other, Condition):
+            return NotImplemented
+        return self.value == other.value and \
+            self.left == other.left and \
+            self.right == other.right and \
+            self.op == other.op
+
+    def __hash__(self) -> int:
+        return hash((self.value, self.left, self.right, self.op))
+
     def __str__(self) -> str:
         return self.value
 
diff --git a/duniterpy/key/encryption_key.py b/duniterpy/key/encryption_key.py
index 0ee82549e397d6f8b44cb79bb5f88d8ff4c75aa4..4c534a4e4a12f1d64b4e00ecc374d64abbc25538 100644
--- a/duniterpy/key/encryption_key.py
+++ b/duniterpy/key/encryption_key.py
@@ -25,7 +25,7 @@ class SecretKey(libnacl.public.SecretKey):
 
         :param salt: Salt credential
         :param password: Password credential
-        :param scrypt_params: Optional ScriptParams instance
+        :param scrypt_params: Optional ScryptParams instance
         """
         if scrypt_params is None:
             scrypt_params = ScryptParams()
diff --git a/tests/documents/test_transaction.py b/tests/documents/test_transaction.py
index 415397522a0ff1099e03e5e1dd6f324517fb1bff..0901eb8db90115485999b3cb9426f12d1571f0f3 100644
--- a/tests/documents/test_transaction.py
+++ b/tests/documents/test_transaction.py
@@ -6,7 +6,8 @@ Created on 12 déc. 2014
 import unittest
 import pypeg2
 from duniterpy.grammars import output
-from duniterpy.documents.transaction import Transaction, reduce_base, SimpleTransaction, InputSource, OutputSource
+from duniterpy.documents import BlockUID
+from duniterpy.documents.transaction import Transaction, reduce_base, SimpleTransaction, InputSource, OutputSource, Unlock, SIGParameter
 
 compact_change = """TX:10:1:1:1:1:1:0
 13410-000041DF0CCA173F09B5FBA48F619D4BC934F12ADF1D0B798639EB2149C4A8CC
@@ -104,6 +105,9 @@ Comment: -----@@@----- (why not this comment?)
 2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 """
 
+input_source_str = "30:0:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2"
+
+output_source_str = "460:0:SIG(8kXygUHh1vLjmcRzXVM86t38EL8dfFJgfBeHmkaWLamu)"
 
 class TestTransaction(unittest.TestCase):
     def test_fromcompact(self):
@@ -291,18 +295,59 @@ class TestTransaction(unittest.TestCase):
 
 
     def test_inputsource_from_inline(self):
-        input_source_str = "30:0:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2"
         i = InputSource.from_inline(input_source_str)
         self.assertEqual(i.inline(), input_source_str)
 
 
     def test_outputsource_from_inline(self):
-        output_source_str = "460:0:SIG(8kXygUHh1vLjmcRzXVM86t38EL8dfFJgfBeHmkaWLamu)"
         o = OutputSource.from_inline(output_source_str)
         self.assertEqual(o.inline(), output_source_str)
 
 
     def test_outputsource_inline_condition(self):
-        output_source_str = "460:0:SIG(8kXygUHh1vLjmcRzXVM86t38EL8dfFJgfBeHmkaWLamu)"
         o = OutputSource.from_inline(output_source_str)
         self.assertEqual(o.inline_condition(), output_source_str.split(":")[2])
+
+
+    def test_transaction_equality(self):
+        t1 = Transaction.from_signed_raw(tx_raw)
+        t2 = Transaction.from_signed_raw(tx_raw)
+
+        self.assertTrue(t1 == t2)
+
+        t2.signatures = ["NSTN"]
+        self.assertFalse(t1 == t2)
+
+        t2 = Transaction.from_signed_raw(tx_raw)
+        t2.issuers = ["NSTRNRST"]
+        self.assertFalse(t1 == t2)
+
+        t2 = Transaction.from_signed_raw(tx_raw)
+        t2.time = 123
+        self.assertFalse(t1 == t2)
+
+        t2 = Transaction.from_signed_raw(tx_raw)
+        t2.inputs = InputSource.from_inline(input_source_str)
+        self.assertFalse(t1 == t2)
+
+        t2 = Transaction.from_signed_raw(tx_raw)
+        t2.outputs = OutputSource.from_inline(output_source_str)
+        self.assertFalse(t1 == t2)
+
+
+    def test_transaction_document_generation(self):
+        transaction = Transaction(
+            version=10,
+            currency="gtest",
+            blockstamp=BlockUID(8979, "000041DF0CCA173F09B5FBA48F619D4BC934F12ADF1D0B798639EB2149C4A8CC"),
+            locktime=0,
+            issuers=list("8kXygUHh1vLjmcRzXVM86t38EL8dfFJgfBeHmkaWLamu"),
+            inputs=[InputSource.from_inline(input_source_str)],
+            unlocks=[Unlock(index=0, parameters=[SIGParameter(0)])],
+            outputs=[OutputSource.from_inline(output_source_str)],
+            comment='',
+            signatures=[]
+        )
+        self.assertTrue(transaction.time == None)
+        self.assertTrue(transaction.currency == "gtest")
+        self.assertTrue(transaction.inputs[0].amount == 30)