From 21d17a43b9f76bee15279628fa26e28b84e5447d Mon Sep 17 00:00:00 2001 From: cryptcoin-junkey Date: Sat, 12 Feb 2022 23:22:23 +0000 Subject: [PATCH] Support XMPIP-0015 --- counterpartylib/lib/messages/trigger.py | 112 ++++++++++++++---------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/counterpartylib/lib/messages/trigger.py b/counterpartylib/lib/messages/trigger.py index 598f9d39bb..dce5f6848e 100644 --- a/counterpartylib/lib/messages/trigger.py +++ b/counterpartylib/lib/messages/trigger.py @@ -18,9 +18,41 @@ LENGTH = 32 ID = 120 -receivers = [ - asset_metadata.asset_metadata_receiver, -] +_INITIALISER_RECEIVERS = [] +_PARSER_RECEIVERS = {} + +class NoTargetTableNameError(Exception): + pass +class DuplicateTargetTableNameError(Exception): + pass + +def trigger_initiailser(): + def wrapper(func): + global _INITIALISER_RECEIVERS + _INITIALISER_RECEIVERS += func + return func + return wrapper + +def trigger_parser(trigger_id = 0, target_table_name = None): + def wrapper(func): + global _PARSER_RECEIVERS + + def _wrapper(*args, **kwargs): + return func(*args, **kwargs) + + if target_table_name is None: + raise NoTargetTableNameError + if 'target_table_name' in _PARSER_RECEIVERS: + raise DuplicateTargetTableNameError + _PARSER_RECEIVERS += { + trigger_id: { + 'target_table_name': target_table_name, + 'func': func + } + } + return _wrapper + return wrapper + def initialise (db): cursor = db.cursor() @@ -42,37 +74,26 @@ def initialise (db): source_idx ON triggers (source) ''') - for receiver in receivers: - receiver.initialise(db) + for init in _INITIALISER_RECEIVERS: + init() def validate (db, source, target_hash, payload_bytes): problems = [] - targets = [] - target_type = None + target_table_name = None cursor = db.cursor() - receiver_instance = None - for receiver in receivers: - sql = 'SELECT tx_hash FROM ' + receiver.target_table_name() + ' WHERE tx_hash = ?' - cursor.execute(sql, (target_hash,)) + for receiver in _PARSER_RECEIVERS: + sql = 'SELECT tx_hash FROM ' + receiver.target_table_name + ' WHERE tx_hash = ? AND status = ?' + cursor.execute(sql, (target_hash,'valid')) targets = cursor.fetchall() - if targets: - assert receiver_instance is None - assert len(targets) == 1 - receiver_instance = receiver(db, source, target_hash, payload_bytes) - problems += receiver_instance.validate() - - if receiver_instance is None: - problems.append('no trigger target with that hash') + assert len(targets) <= 1 + if len(targets) == 1: + target_table_name = receiver.target_table_name - fee = int(0.001 * config.UNIT) - cursor.execute('''SELECT * FROM balances - WHERE (address = ? AND asset = ?)''', (source, config.XCP)) - balances = cursor.fetchall() - if not balances or balances[0]['quantity'] < fee: - problems.append('insufficient funds') + if target_table_name is None: + problems.append('no valid trigger target') - return receiver_instance, fee, problems + return target_table_name, problems def compose (db, source, target_hash, payload, payload_is_hex): # convert memo to memo_bytes based on memo_is_hex setting @@ -85,12 +106,14 @@ def compose (db, source, target_hash, payload, payload_is_hex): raise exceptions.ComposeError(['failed to convert the payload']) else: - payload_bytes = struct.pack(">{}s".format(len(payload)), + payload_bytes = struct.pack( + ">{}s".format(len(payload)), payload.encode('utf-8')) # Check that target exists. - _, _, problems = validate(db, source, target_hash, payload_bytes) - if problems: raise exceptions.ComposeError(problems) + _, problems = validate(db, source, target_hash, payload_bytes) + if problems: + raise exceptions.ComposeError(problems) target_hash_bytes = binascii.unhexlify(bytes(target_hash, 'utf-8')) @@ -122,17 +145,16 @@ def parse (db, tx, message): status = 'invalid: could not unpack' if status == 'valid': - receiver, fee, problems = validate(db, tx['source'], target_hash, payload_bytes) - if problems: - status = 'invalid: ' + '; '.join(problems) + target_table_name, problems = validate(db, tx['source'], target_hash, payload_bytes) if status == 'valid': try: - problems += receiver.execute(tx) - if problems: - status = 'invalid: ' + '; '.join(problems) + problems += _PARSER_RECEIVERS[target_table_name](db, tx, payload_bytes) except: - status = 'invalid: execution failed' + problems += [ 'invalid: trigger execution failed' ] + + if status == 'valid' and problems: + status = 'invalid: ' + '; '.join(problems) # Add parsed transaction to message-type–specific table. bindings = { @@ -144,14 +166,14 @@ def parse (db, tx, message): 'payload': payload_bytes, 'status': status, } - if "integer overflow" not in status: - sql='INSERT INTO triggers VALUES (:tx_index, :tx_hash, :block_index, :source, :target_hash, :payload, :status)' - cursor.execute(sql, bindings) - else: - logger.warn("Not storing [trigger] tx [%s]: %s", tx['tx_hash'], status) - logger.debug("Bindings: %s", json.dumps(bindings)) - - if status == 'valid': - util.debit(db, tx['source'], config.XCP, fee, action="trigger fee", event=tx['tx_hash']) + sql = '''INSERT INTO triggers VALUES ( + :tx_index, + :tx_hash, + :block_index, + :source, + :target_hash, + :payload, + :status)''' + cursor.execute(sql, bindings) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4