diff --git a/duniterpy/documents/constants.py b/duniterpy/documents/constants.py
index 7b7271989e712dcaee7c1ca024a075b551a28760..51d34b44ac74f44850f3839242f4993f5c386577 100644
--- a/duniterpy/documents/constants.py
+++ b/duniterpy/documents/constants.py
@@ -4,6 +4,9 @@ pubkey_regex = "(?![OIl])[1-9A-Za-z]{42,45}"
 signature_regex = "[A-Za-z0-9+/]+(?:=|==)?"
 block_hash_regex = "[0-9a-fA-F]{5,64}"
 transaction_hash_regex = "[0-9a-fA-F]{5,64}"
+hash_regex = "[A-F0-9]{64}"
 block_id_regex = "[0-9]+"
 block_uid_regex = "{block_id_regex}-{block_hash_regex}".format(block_id_regex=block_id_regex,
-                                                              block_hash_regex=block_hash_regex)
\ No newline at end of file
+                                                              block_hash_regex=block_hash_regex)
+conditions_regex = "(&&|\|\|| |[()]|(SIG\({pubkey_regex}\)|(XHX\({hash_regex}\))))*"\
+    .format(pubkey_regex=pubkey_regex, hash_regex=hash_regex)
diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py
index c27330a1356f1f0435a8fcc410528b5236092beb..580c2d7fd8e7d6f54c91435094ff931453d32e4b 100644
--- a/duniterpy/documents/transaction.py
+++ b/duniterpy/documents/transaction.py
@@ -1,5 +1,5 @@
 from .document import Document, MalformedDocumentError
-from .constants import pubkey_regex, transaction_hash_regex, block_id_regex, block_uid_regex
+from .constants import pubkey_regex, transaction_hash_regex, block_id_regex, block_uid_regex, conditions_regex
 from ..grammars import output
 import pypeg2
 import re
@@ -576,7 +576,7 @@ class OutputSource:
     """
     A Transaction OUTPUT
     """
-    re_inline = re.compile("([0-9]+):([0-9]+):([A-Za-z0-9\(\)\s]+)\n")
+    re_inline = re.compile("([0-9]+):([0-9]+):(.*)\n")
 
     def __init__(self, amount, base, conditions):
         self.amount = amount
@@ -590,8 +590,9 @@ class OutputSource:
             raise MalformedDocumentError("Inline output")
         amount = int(data.group(1))
         base = int(data.group(2))
+        conditions_text = data.group(3)
         try:
-            conditions = pypeg2.parse(data.group(3), output.Condition)
+            conditions = pypeg2.parse(conditions_text, output.Condition)
         except SyntaxError:
             raise MalformedDocumentError("Output source syntax error")
         return cls(amount, base, conditions)
diff --git a/duniterpy/grammars/output.py b/duniterpy/grammars/output.py
index 183543d070ae88f38c1f871e87712a02a4ccc0d0..dd54ba09fc00956158ca0049f0ea037da54892ec 100644
--- a/duniterpy/grammars/output.py
+++ b/duniterpy/grammars/output.py
@@ -1,5 +1,5 @@
 from ..documents.constants import pubkey_regex
-from ..documents.constants import block_hash_regex as hash_regex
+from ..documents.constants import hash_regex
 from pypeg2 import *
 
 
@@ -11,6 +11,10 @@ class Hash(str):
     regex = re.compile(hash_regex)
 
 
+class Int(str):
+    regex = re.compile(r"[0-9]+")
+
+
 class SIG(str):
     grammar = "SIG(", attr('pubkey', Pubkey), ")"
 
@@ -24,6 +28,32 @@ class SIG(str):
         return "SIG({0})".format(self.pubkey)
 
 
+class CSV(str):
+    grammar = "CSV(", attr('time', Int), ")"
+
+    @classmethod
+    def token(cls, time):
+        csv = cls()
+        csv.time = str(time)
+        return csv
+
+    def compose(self, parser, grammar=None, attr_of=None):
+        return "CSV({0})".format(self.time)
+
+
+class CLTV(str):
+    grammar = "CLTV(", attr('timestamp', Int), ")"
+
+    @classmethod
+    def token(cls, timestamp):
+        cltv = cls()
+        cltv.timestamp = str(timestamp)
+        return cltv
+
+    def compose(self, parser, grammar=None, attr_of=None):
+        return "CLTV({0})".format(self.timestamp)
+
+
 class XHX(str):
     grammar = "XHX(", attr('sha_hash', Hash), ")"
 
@@ -38,7 +68,8 @@ class XHX(str):
 
 
 class Operator(Keyword):
-    grammar = Enum(K("AND"), K("OR"))
+    grammar = Enum(K("&&"), K("||"), K("AND"), K("OR"))
+    regex = re.compile(r"[&&|\|\||\w]+")
 
     @classmethod
     def token(cls, keyword):
@@ -78,6 +109,6 @@ class Condition(str):
             result = left
         return result
 
-Condition.grammar = contiguous(attr('left', [SIG, XHX, ('(', Condition, ')')]),
+Condition.grammar = contiguous(attr('left', [SIG, XHX, CSV, CLTV, ('(', Condition, ')')]),
                      maybe_some(whitespace, attr('op', Operator), whitespace,
-                               attr('right', [SIG, XHX, ('(', Condition, ')')])))
+                               attr('right', [SIG, XHX, CSV, CLTV, ('(', Condition, ')')])))
diff --git a/tests/documents/test_block.py b/tests/documents/test_block.py
index 4c37a55ddb85899c4c9a7f728d1bb66659a02e8c..eaa81dd8e52b67e794c7febb5a0199869218ef76 100644
--- a/tests/documents/test_block.py
+++ b/tests/documents/test_block.py
@@ -80,7 +80,7 @@ D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
 5:SIG(2)
 120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(12CCC75A48B1502E4A4E5E9EC2C5153AE2DDF760D5B70262103309D4C7FA86EE))
 -----@@@----- (why not this comment?)
 42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
@@ -164,72 +164,6 @@ Nonce: 9906
 5LZCFSnm5FkFihPBTpmsPyILEdvu8MXfJOp6OR4d1s+/e2jVWg4J6YSDfO2KBBPgubASyr2QwQuiBlYD2918Bw==
 """
 
-raw_block_with_tx_v3 = """Version: 3
-Type: Block
-Currency: meta_brouzouf
-Number: 34436
-PoWMin: 5
-Time: 1443896211
-MedianTime: 1443881811
-UnitBase: 3
-Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk
-IssuersFrame: 43
-IssuersFrameVar: 2
-DifferentIssuersCount: 8
-PreviousHash: 000002B06C990DEBD5C1D947289C2CF4F4396FB2
-PreviousIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk
-MembersCount: 19
-Identities:
-Joiners:
-Actives:
-ATkjQPa4sn4LBF69jqEPzFtRdHYJs6MJQjvP8JdN7MtN:QTowsupV+uXrcomL44WCxbu3LQoJM2C2VPMet5Xg6gXGAHEtGRp47FfQLb2ok1+/588JiIHskCyazj3UOsmKDw==:34434-00000D21F80687248A8C02F16BB19A975B4F983D:34432-00000D21F80687248A8C02F16BB19A975B4F983D:urodelus
-Leavers:
-Revoked:
-Excluded:
-Certifications:
-5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of:ATkjQPa4sn4LBF69jqEPzFtRdHYJs6MJQjvP8JdN7MtN:0:6TuxRcARnpo13l3cXtgPTkjJlv8DZOUvsAzmZJMbjHZbbZfDQ6MJpH9DIuH0eyG3WGc0EX/046mbMGBrKKg9DQ==
-ATkjQPa4sn4LBF69jqEPzFtRdHYJs6MJQjvP8JdN7MtN:2qwGeLWoPG7db72bKXPfJpAtj67FYDnPaJn2JB7tyXxJ:0:LusTbb7CgwrqqacDKjtldw60swwvDBH8wVUIJN4SWRb2pZPJSpDxgqaGyjC5P9i/DendfyQWc7cfzPDqSZmZAg==
-Transactions:
-TX:3:1:3:3:1:0:0
-32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
-HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
-5:0:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:0
-1:1:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:10
-35:0:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:88
-0:SIG(0)
-1:SIG(0)
-2:SIG(0)
-30:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
-42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
-TX:3:3:6:6:3:1:0
-3-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
-HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
-CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
-9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB
-30:0:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
-25:0:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
-5:1:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:46
-10:1:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
-60:0:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
-50:0:D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
-0:SIG(0)
-1:XHX(7665798292)
-2:SIG(0)
-3:SIG(0) SIG(2)
-4:SIG(0) SIG(1) SIG(2)
-5:SIG(2)
-120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
-146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
------@@@----- (why not this comment?)
-42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
-2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
-2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
-InnerHash: DB30D958EE5CB75186972286ED3F4686B8A1C2CD
-Nonce: 581
-nY/MsFU2luiohLmSiOOimL1RIqbriOBgc22ua03Z2dhxtSJxKZeGNGDvl1jaXgmEBRnXU87yXbZ7ioOS/AAVCA==
-"""
-
 
 raw_block_with_excluded = """Version: 3
 Type: Block
@@ -452,31 +386,6 @@ class Test_Block(unittest.TestCase):
 
         self.assertEqual(block.signed_raw(), raw_block_with_tx)
 
-    def test_raw_with_tx_v3(self):
-        block = Block.from_signed_raw(raw_block_with_tx_v3)
-        rendered_raw = block.signed_raw()
-        from_rendered_raw = block.from_signed_raw(rendered_raw)
-
-        self.assertEqual(from_rendered_raw.version, 3)
-        self.assertEqual(from_rendered_raw.currency, "meta_brouzouf")
-        self.assertEqual(from_rendered_raw.noonce, 581)
-        self.assertEqual(from_rendered_raw.number, 34436)
-        self.assertEqual(from_rendered_raw.powmin, 5)
-        self.assertEqual(from_rendered_raw.time, 1443896211)
-        self.assertEqual(from_rendered_raw.mediantime, 1443881811)
-        self.assertEqual(from_rendered_raw.issuer, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk")
-        self.assertEqual(from_rendered_raw.parameters, None)
-        self.assertEqual(from_rendered_raw.members_count, 19)
-        self.assertEqual(from_rendered_raw.identities, [])
-        self.assertEqual(from_rendered_raw.joiners, [])
-        self.assertEqual(len(from_rendered_raw.actives), 1)
-        self.assertEqual(from_rendered_raw.leavers, [])
-        self.assertEqual(from_rendered_raw.excluded, [])
-        self.assertEqual(len(from_rendered_raw.certifications), 2)
-        self.assertEqual(len(from_rendered_raw.transactions), 2)
-
-        self.assertEqual(block.signed_raw(), raw_block_with_tx_v3)
-
     def test_raw_with_leavers(self):
         block = Block.from_signed_raw(raw_block_with_leavers)
         rendered_raw = block.signed_raw()
diff --git a/tests/documents/test_transaction.py b/tests/documents/test_transaction.py
index 24246811b86b4df7bc74b78d6262515c2da111c3..a14fe4e117fc193e5554e663285bd478cea34521 100644
--- a/tests/documents/test_transaction.py
+++ b/tests/documents/test_transaction.py
@@ -9,6 +9,35 @@ from duniterpy.grammars import output
 from duniterpy.documents.transaction import Transaction, reduce_base, SimpleTransaction
 
 
+compact_change = """TX:10:1:1:1:1:1:0
+13410-000041DF0CCA173F09B5FBA48F619D4BC934F12ADF1D0B798639EB2149C4A8CC
+D8BsQZN9hangHVuqwD6McfxM1xvGJ8DPuPYrswwnSif3
+1500:1:T:0D0264F324BC4A23C4B2C696CD1907BD6E70FD1F409BB1D42E84847AA4C1E87C:0
+0:SIG(0)
+1500:1:(XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB) && SIG(36j6pCNzKDPo92m7UXJLFpgDbcLFAZBgThD2TCwTwGrd)) || (SIG(D8BsQZN9hangHVuqwD6McfxM1xvGJ8DPuPYrswwnSif3) && SIG(36j6pCNzKDPo92m7UXJLFpgDbcLFAZBgThD2TCwTwGrd))
+META tic to toc
+eNAZpJjhZaPKbx5pUvuDDM1j4XNWJ4ABK48ouTvimvg3ceIcoZUvgLHmXuSwk2bgxZaB5qSKP9H6T7qsBcLtBg==
+"""
+
+
+xhx_output = """Version: 10
+Type: Transaction
+Currency: gtest
+Blockstamp: 13739-000087835A9B746C1A6E173DB13A2C3D23DBDE8B2C5E93565B644313FE3D179B
+Locktime: 0
+Issuers:
+95ApcNEeoFnjUYPwh4fbGqKPDe5mCJysfJfezLngEZcu
+Inputs:
+250:1:T:7AEDF83C99071E040698ED6E1445BF02FADEF37DA380795684D5C9271C037D5A:0
+Unlocks:
+0:SIG(0)
+Outputs:
+250:1:(XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36) && SIG(DCYELkvV1aAsxFv58SbfRerHy5giJwKA1i4ZKTTcVGZe)) || (SIG(95ApcNEeoFnjUYPwh4fbGqKPDe5mCJysfJfezLngEZcu) && SIG(DCYELkvV1aAsxFv58SbfRerHy5giJwKA1i4ZKTTcVGZe))
+Comment: XHX for pubkey DCYELkvV1aAsxFv58SbfRerHy5giJwKA1i4ZKTTcVGZe
+GXGephqTSJfb+8xsG/UMKRW0y+edL4RoMHM+OlgFq1aYOuaQ3/CtBKVSA01n2mkI7zwepeIABSjS94iVH4vZDg==
+"""
+
+
 tx_compact = """TX:2:3:6:6:3:1:0
 HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
 CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
@@ -27,13 +56,14 @@ D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
 5:SIG(2)
 120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))
 -----@@@----- (why not this comment?)
 42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
 2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 """
 
+
 tx_compact_2 = """TX:2:1:1:1:2:0:0
 GNPdPNwSJAYw7ixkDeibo3YpdELgLmrZ2Q86HF4cyg92
 D:GNPdPNwSJAYw7ixkDeibo3YpdELgLmrZ2Q86HF4cyg92:471
@@ -43,32 +73,6 @@ D:GNPdPNwSJAYw7ixkDeibo3YpdELgLmrZ2Q86HF4cyg92:471
 XDQeEMcJDd+XVGaFIZc8d4kKRJgsPuWAPVNG5UKNk8mDZx2oE1kTP/hbxiFx6yDouBELCswuf/X6POK9ES7JCA==
 """
 
-tx_compact_v3 = """TX:3:3:6:6:3:1:0
-32-DB30D958EE5CB75186972286ED3F4686B8A1C2CD
-HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY
-CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp
-9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB
-30:0:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2
-25:0:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8
-5:1:D:HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY:46
-10:1:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3
-60:0:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5
-50:0:D:9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB:46
-0:SIG(0)
-1:XHX(7665798292)
-2:SIG(0)
-3:SIG(0) SIG(2)
-4:SIG(0) SIG(1) SIG(2)
-5:SIG(2)
-120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
-146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
------@@@----- (why not this comment?)
-42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
-2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
-2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
-"""
-
 tx_raw = """Version: 2
 Type: Transaction
 Currency: beta_brousouf
@@ -94,13 +98,14 @@ Unlocks:
 Outputs:
 120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))
 Comment: -----@@@----- (why not this comment?)
 42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
 2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 """
 
+
 tx_raw_v3 = """Version: 3
 Type: Transaction
 Currency: beta_brousouf
@@ -127,13 +132,14 @@ Unlocks:
 Outputs:
 120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)
 146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)
-49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))
+49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))
 Comment: -----@@@----- (why not this comment?)
 42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r
 2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX
 2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk
 """
 
+
 class Test_Transaction(unittest.TestCase):
     def test_fromcompact(self):
         tx = Transaction.from_compact("zeta_brousouf", tx_compact)
@@ -191,7 +197,7 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)")
         self.assertEqual(tx.outputs[2].amount, 49)
         self.assertEqual(tx.outputs[2].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))")
+        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))")
 
         self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)")
 
@@ -227,72 +233,6 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(type(tx.outputs[1].conditions.left), output.SIG)
         self.assertEqual(tx.signatures[0], "XDQeEMcJDd+XVGaFIZc8d4kKRJgsPuWAPVNG5UKNk8mDZx2oE1kTP/hbxiFx6yDouBELCswuf/X6POK9ES7JCA==")
 
-    def test_fromcompact_v3(self):
-        tx = Transaction.from_compact("zeta_brousouf", tx_compact_v3)
-        self.assertEqual(tx.version, 3)
-        self.assertEqual(tx.currency, "zeta_brousouf")
-        self.assertEqual(tx.blockstamp.number, 32)
-        self.assertEqual(tx.blockstamp.sha_hash, "DB30D958EE5CB75186972286ED3F4686B8A1C2CD")
-        self.assertEqual(len(tx.issuers), 3)
-        self.assertEqual(len(tx.inputs), 6)
-        self.assertEqual(len(tx.unlocks), 6)
-        self.assertEqual(len(tx.outputs), 3)
-
-        self.assertEqual(tx.issuers[0], "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY")
-        self.assertEqual(tx.issuers[1], "CYYjHsNyg3HMRMpTHqCJAN9McjH5BwFLmDKGV3PmCuKp")
-        self.assertEqual(tx.issuers[2], "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB")
-
-        self.assertEqual(tx.inputs[0].source, 'T')
-        self.assertEqual(tx.inputs[0].origin_id, "6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3")
-        self.assertEqual(tx.inputs[0].index, 2)
-        self.assertEqual(tx.inputs[1].source, 'T')
-        self.assertEqual(tx.inputs[1].origin_id, "3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435")
-        self.assertEqual(tx.inputs[1].index, 8)
-        self.assertEqual(tx.inputs[2].source, 'D')
-        self.assertEqual(tx.inputs[2].origin_id, "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY")
-        self.assertEqual(tx.inputs[2].index, 46)
-        self.assertEqual(tx.inputs[3].source, 'T')
-        self.assertEqual(tx.inputs[3].origin_id, "A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956")
-        self.assertEqual(tx.inputs[3].index, 3)
-        self.assertEqual(tx.inputs[4].source, 'T')
-        self.assertEqual(tx.inputs[4].origin_id, "67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B")
-        self.assertEqual(tx.inputs[4].index, 5)
-        self.assertEqual(tx.inputs[5].source, 'D')
-        self.assertEqual(tx.inputs[5].origin_id, "9WYHTavL1pmhunFCzUwiiq4pXwvgGG5ysjZnjz9H8yB")
-        self.assertEqual(tx.inputs[5].index, 46)
-
-        self.assertEqual(tx.unlocks[0].index, 0)
-        self.assertEqual(str(tx.unlocks[0].parameters[0]), "SIG(0)")
-        self.assertEqual(tx.unlocks[1].index, 1)
-        self.assertEqual(str(tx.unlocks[1].parameters[0]), "XHX(7665798292)")
-        self.assertEqual(tx.unlocks[2].index, 2)
-        self.assertEqual(str(tx.unlocks[2].parameters[0]), "SIG(0)")
-        self.assertEqual(tx.unlocks[3].index, 3)
-        self.assertEqual(str(tx.unlocks[3].parameters[0]), "SIG(0)")
-        self.assertEqual(str(tx.unlocks[3].parameters[1]), "SIG(2)")
-        self.assertEqual(tx.unlocks[4].index, 4)
-        self.assertEqual(str(tx.unlocks[4].parameters[0]), "SIG(0)")
-        self.assertEqual(str(tx.unlocks[4].parameters[1]), "SIG(1)")
-        self.assertEqual(str(tx.unlocks[4].parameters[2]), "SIG(2)")
-        self.assertEqual(tx.unlocks[5].index, 5)
-        self.assertEqual(str(tx.unlocks[5].parameters[0]), "SIG(2)")
-
-        self.assertEqual(tx.outputs[0].amount, 120)
-        self.assertEqual(tx.outputs[0].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition), "SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)")
-        self.assertEqual(tx.outputs[1].amount, 146)
-        self.assertEqual(tx.outputs[1].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)")
-        self.assertEqual(tx.outputs[2].amount, 49)
-        self.assertEqual(tx.outputs[2].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))")
-
-        self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)")
-
-        self.assertEqual(tx.signatures[0], "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r")
-        self.assertEqual(tx.signatures[1], "2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX")
-        self.assertEqual(tx.signatures[2], "2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk")
-
     def test_fromraw(self):
         tx = Transaction.from_signed_raw(tx_raw)
         self.assertEqual(tx.version, 2)
@@ -349,7 +289,7 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)")
         self.assertEqual(tx.outputs[2].amount, 49)
         self.assertEqual(tx.outputs[2].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))")
+        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))")
 
         self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)")
 
@@ -416,7 +356,7 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)")
         self.assertEqual(tx.outputs[2].amount, 49)
         self.assertEqual(tx.outputs[2].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))")
+        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))")
 
         self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)")
 
@@ -485,7 +425,7 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)")
         self.assertEqual(tx.outputs[2].amount, 49)
         self.assertEqual(tx.outputs[2].base, 2)
-        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) OR XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85))")
+        self.assertEqual(pypeg2.compose(tx.outputs[2].conditions, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))")
 
         self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)")
 
@@ -493,6 +433,11 @@ class Test_Transaction(unittest.TestCase):
         self.assertEqual(tx.signatures[1], "2D96KZwNUvVtcapQPq2mm7J9isFcDCfykwJpVEZwBc7tCgL4qPyu17BT5ePozAE9HS6Yvj51f62Mp4n9d9dkzJoX")
         self.assertEqual(tx.signatures[2], "2XiBDpuUdu6zCPWGzHXXy8c4ATSscfFQG9DjmqMZUxDZVt1Dp4m2N5oHYVUfoPdrU9SLk4qxi65RNrfCVnvQtQJk")
 
+    def test_compact_change(self):
+        tx = Transaction.from_compact("gtest", compact_change)
+        rendered_tx = tx.signed_raw()
+        from_rendered_tx = Transaction.from_signed_raw(rendered_tx)
+
     def test_reduce_base(self):
         amount = 1200
         base = 0
diff --git a/tests/grammars/test_output.py b/tests/grammars/test_output.py
index f6835060428a4abb95c21cd38274c6a7fe24b376..b3a1c9506cca05525a2787033c39b6f8497dc88f 100644
--- a/tests/grammars/test_output.py
+++ b/tests/grammars/test_output.py
@@ -34,44 +34,54 @@ class Test_OutputGrammar(unittest.TestCase):
         self.assertEqual(pypeg2.compose(result, output.Condition), condition)
 
     def test_simple_and_condition(self):
-        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) AND XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6))"
+        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) && XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6))"
         result = pypeg2.parse(condition, output.Condition)
         self.assertEqual(result.left.left.pubkey, "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd")
-        self.assertEqual(result.left.op.name, "AND")
+        self.assertEqual(result.left.op.name, "&&")
         self.assertEqual(result.left.right.sha_hash, "309BC5E644F797F53E5A2065EAF38A173437F2E6")
         self.assertEqual(pypeg2.compose(result, output.Condition), condition)
 
     def test_simple_or_condition(self):
-        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) OR XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6))"
+        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6))"
         result = pypeg2.parse(condition, output.Condition)
         self.assertEqual(result.left.left.pubkey, "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd")
-        self.assertEqual(result.left.op.name, "OR")
+        self.assertEqual(result.left.op.name, "||")
         self.assertEqual(result.left.right.sha_hash, "309BC5E644F797F53E5A2065EAF38A173437F2E6")
         self.assertEqual(pypeg2.compose(result, output.Condition), condition)
 
     def test_complex_condition(self):
-        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) OR (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) AND XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6)))"
+        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) && XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6)))"
         result = pypeg2.parse(condition, output.Condition)
         self.assertEqual(result.left.left.pubkey, "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd")
-        self.assertEqual(result.left.op.name, "OR")
+        self.assertEqual(result.left.op.name, "||")
         self.assertEqual(result.left.right.left.pubkey, "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
-        self.assertEqual(result.left.right.op.name, "AND")
+        self.assertEqual(result.left.right.op.name, "&&")
         self.assertEqual(result.left.right.right.sha_hash, "309BC5E644F797F53E5A2065EAF38A173437F2E6")
         self.assertEqual(pypeg2.compose(result, output.Condition), condition)
 
+    def test_csv_cltv_condition(self):
+        condition = "(CSV(1654300) || (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) && CLTV(2594024)))"
+        result = pypeg2.parse(condition, output.Condition)
+        self.assertEqual(result.left.left.time, "1654300")
+        self.assertEqual(result.left.op.name, "||")
+        self.assertEqual(result.left.right.left.pubkey, "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
+        self.assertEqual(result.left.right.op.name, "&&")
+        self.assertEqual(result.left.right.right.timestamp, "2594024")
+        self.assertEqual(pypeg2.compose(result, output.Condition), condition)
+
     def test_instanciate_condition(self):
         inst = output.Condition.token(output.SIG.token("HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"),
-                                           output.Operator.token("OR"),
+                                           output.Operator.token("||"),
                                            output.Condition.token(
                                                output.SIG.token("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV"),
-                                               output.Operator.token("AND"),
+                                               output.Operator.token("&&"),
                                                output.XHX.token("309BC5E644F797F53E5A2065EAF38A173437F2E6")
                                            ))
-        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) OR (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) AND XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6)))"
+        condition = "(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || (SIG(DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV) && XHX(309BC5E644F797F53E5A2065EAF38A173437F2E6)))"
         inst = pypeg2.parse(condition, output.Condition)
         self.assertEqual(inst.left.left.pubkey, "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd")
-        self.assertEqual(inst.left.op.name, "OR")
+        self.assertEqual(inst.left.op.name, "||")
         self.assertEqual(inst.left.right.left.pubkey, "DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV")
-        self.assertEqual(inst.left.right.op.name, "AND")
+        self.assertEqual(inst.left.right.op.name, "&&")
         self.assertEqual(inst.left.right.right.sha_hash, "309BC5E644F797F53E5A2065EAF38A173437F2E6")
         self.assertEqual(pypeg2.compose(inst, output.Condition), condition)