From ccc75d2feedd9f1d469a244b48c35d33ac0a6ae1 Mon Sep 17 00:00:00 2001 From: vtexier <vit@free.fr> Date: Sat, 3 Nov 2018 11:34:00 +0100 Subject: [PATCH] issue #52 fix type hint problem in outputSource.conditions (BC broken) Output instances in transactions get condition argument as text only now, and check grammar before storing condition as Condition instance. --- duniterpy/documents/transaction.py | 55 ++++++++++++++++------------- examples/send_transaction.py | 10 ++---- tests/documents/test_transaction.py | 32 ++++++++--------- 3 files changed, 49 insertions(+), 48 deletions(-) diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py index aab2eded..871cc5ac 100644 --- a/duniterpy/documents/transaction.py +++ b/duniterpy/documents/transaction.py @@ -141,17 +141,17 @@ class OutputSource: """ re_inline = re.compile("([0-9]+):([0-9]+):(.*)\n") - def __init__(self, amount: int, base: int, conditions: Union[str, Condition]) -> None: + def __init__(self, amount: int, base: int, condition: str) -> None: """ Init OutputSource instance :param amount: Amount of the output :param base: Base number - :param conditions: Conditions expression + :param condition: Condition expression """ self.amount = amount self.base = base - self.conditions = conditions + self.condition = self.condition_from_text(condition) @classmethod def from_inline(cls: Type[OutputSourceType], inline: str) -> OutputSourceType: @@ -166,14 +166,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(conditions_text, output.Condition) - except SyntaxError: - # Invalid conditions are possible, see https://github.com/duniter/duniter/issues/1156 - # In such a case, they are store "as-is" and considered unlockable - conditions = conditions_text - return cls(amount, base, conditions) + condition_text = data.group(3) + + return cls(amount, base, condition_text) def inline(self) -> str: """ @@ -181,11 +176,24 @@ class OutputSource: :return: """ - if type(self.conditions) is str: - return "{0}:{1}:{2}".format(self.amount, self.base, self.conditions) - else: - return "{0}:{1}:{2}".format(self.amount, self.base, - pypeg2.compose(self.conditions, output.Condition)) + return "{0}:{1}:{2}".format(self.amount, self.base, + pypeg2.compose(self.condition, output.Condition)) + + @staticmethod + def condition_from_text(text) -> Condition: + """ + Return a Condition instance with PEG grammar from text + + :param text: PEG parsable string + :return: + """ + try: + condition = pypeg2.parse(text, output.Condition) + except SyntaxError: + # Invalid conditions are possible, see https://github.com/duniter/duniter/issues/1156 + # In such a case, they are store as empty PEG grammar object and considered unlockable + condition = Condition(text) + return condition # required to type hint cls in classmethod @@ -759,7 +767,7 @@ class SimpleTransaction(Transaction): simple "SIG" functions, and the outputs must be simple SIG conditions. - :param duniterpy.documents.Transaction tx: the transaction to check + :param tx: the transaction to check :return: True if a simple transaction """ @@ -772,12 +780,11 @@ class SimpleTransaction(Transaction): elif type(unlock.parameters[0]) is not SIGParameter: simple = False for o in tx.outputs: - # If condition is str type, it is unlockable - if type(o.conditions) is str: + # if right condition is not None... + if getattr('right', o.condition, None): simple = False - else: - if getattr('right', o.conditions, None): - simple = False - elif type(o.conditions.left) is not output.SIG: - simple = False + # if left is not SIG... + elif type(o.condition.left) is not output.SIG: + simple = False + return simple diff --git a/examples/send_transaction.py b/examples/send_transaction.py index e169d782..6ce68a65 100644 --- a/examples/send_transaction.py +++ b/examples/send_transaction.py @@ -5,7 +5,6 @@ from duniterpy.api import bma from duniterpy.api.client import Client from duniterpy.documents import BlockUID, Transaction from duniterpy.documents.transaction import InputSource, OutputSource, Unlock, SIGParameter -from duniterpy.grammars.output import Condition, SIG from duniterpy.key import SigningKey # CONFIG ####################################### @@ -61,12 +60,7 @@ def get_transaction_document(current_block: dict, source: dict, from_pubkey: str # lists of outputs outputs = [ - OutputSource( - amount=source['amount'], - base=source['base'], - # only the receiver of the output can use it as input in another transaction - conditions=Condition.token(SIG.token(to_pubkey)) - ) + OutputSource(amount=source['amount'], base=source['base'], condition="SIG({0})".format(to_pubkey)) ] transaction = Transaction( @@ -79,7 +73,7 @@ def get_transaction_document(current_block: dict, source: dict, from_pubkey: str unlocks=unlocks, outputs=outputs, comment='', - signatures=None + signatures=[] ) return transaction diff --git a/tests/documents/test_transaction.py b/tests/documents/test_transaction.py index a14fe4e1..e27f659a 100644 --- a/tests/documents/test_transaction.py +++ b/tests/documents/test_transaction.py @@ -191,13 +191,13 @@ class Test_Transaction(unittest.TestCase): 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(pypeg2.compose(tx.outputs[0].condition, 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(pypeg2.compose(tx.outputs[1].condition, 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) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") + self.assertEqual(pypeg2.compose(tx.outputs[2].condition, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)") @@ -225,12 +225,12 @@ class Test_Transaction(unittest.TestCase): self.assertEqual(tx.outputs[0].amount, 90) self.assertEqual(tx.outputs[0].base, 0) - self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition), "SIG(5zDvFjJB1PGDQNiExpfzL9c1tQGs6xPA8mf1phr3VoVi)") - self.assertEqual(type(tx.outputs[0].conditions.left), output.SIG) + self.assertEqual(pypeg2.compose(tx.outputs[0].condition, output.Condition), "SIG(5zDvFjJB1PGDQNiExpfzL9c1tQGs6xPA8mf1phr3VoVi)") + self.assertEqual(type(tx.outputs[0].condition.left), output.SIG) self.assertEqual(tx.outputs[1].amount, 10) self.assertEqual(tx.outputs[1].base, 0) - self.assertEqual(pypeg2.compose(tx.outputs[1].conditions, output.Condition), "SIG(GNPdPNwSJAYw7ixkDeibo3YpdELgLmrZ2Q86HF4cyg92)") - self.assertEqual(type(tx.outputs[1].conditions.left), output.SIG) + self.assertEqual(pypeg2.compose(tx.outputs[1].condition, output.Condition), "SIG(GNPdPNwSJAYw7ixkDeibo3YpdELgLmrZ2Q86HF4cyg92)") + self.assertEqual(type(tx.outputs[1].condition.left), output.SIG) self.assertEqual(tx.signatures[0], "XDQeEMcJDd+XVGaFIZc8d4kKRJgsPuWAPVNG5UKNk8mDZx2oE1kTP/hbxiFx6yDouBELCswuf/X6POK9ES7JCA==") def test_fromraw(self): @@ -283,13 +283,13 @@ class Test_Transaction(unittest.TestCase): 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(pypeg2.compose(tx.outputs[0].condition, 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(pypeg2.compose(tx.outputs[1].condition, 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) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") + self.assertEqual(pypeg2.compose(tx.outputs[2].condition, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)") @@ -350,13 +350,13 @@ class Test_Transaction(unittest.TestCase): 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(pypeg2.compose(tx.outputs[0].condition, 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(pypeg2.compose(tx.outputs[1].condition, 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) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") + self.assertEqual(pypeg2.compose(tx.outputs[2].condition, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)") @@ -419,13 +419,13 @@ class Test_Transaction(unittest.TestCase): 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(pypeg2.compose(tx.outputs[0].condition, 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(pypeg2.compose(tx.outputs[1].condition, 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) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") + self.assertEqual(pypeg2.compose(tx.outputs[2].condition, output.Condition), "(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(8FAA0ED653CA4D2C1156D511F0D0036F5168ABA4DAC2929676D279C8A2A12E36))") self.assertEqual(tx.comment, "-----@@@----- (why not this comment?)") -- GitLab