From 43fe80818b82de4f7690f876da0e05f871edecf1 Mon Sep 17 00:00:00 2001 From: andrei Date: Fri, 1 Apr 2022 12:00:41 +0300 Subject: [PATCH] Transaction Compose/Decompose for tarantool usages, move to backend.tarantool folder --- planetmint/backend/tarantool/database.py | 45 ------ planetmint/backend/tarantool/query.py | 38 +++-- .../backend/tarantool/transaction/tools.py | 134 ++++++++++++++++++ planetmint/common/transaction.py | 105 +------------- tests/tendermint/test_lib.py | 1 + 5 files changed, 159 insertions(+), 164 deletions(-) delete mode 100644 planetmint/backend/tarantool/database.py create mode 100644 planetmint/backend/tarantool/transaction/tools.py diff --git a/planetmint/backend/tarantool/database.py b/planetmint/backend/tarantool/database.py deleted file mode 100644 index aeb2584..0000000 --- a/planetmint/backend/tarantool/database.py +++ /dev/null @@ -1,45 +0,0 @@ -import tarantool -import os -from planetmint.backend.tarantool.utils import run - - -def init_tarantool(): - if os.path.exists(os.path.join(os.getcwd(), 'tarantool', 'init.lua')) is not True: - path = os.getcwd() - run(["mkdir", "tarantool_snap"]) - run(["ln", "-s", path + "/init.lua", "init.lua"], path + "/tarantool_snap") - run(["tarantool", "init.lua"], path + "/tarantool") - else: - raise Exception("There is a instance of tarantool already created in %s" + os.getcwd() + "/tarantool_snap") - - -def drop_tarantool(): - if os.path.exists(os.path.join(os.getcwd(), 'tarantool', 'init.lua')) is not True: - path = os.getcwd() - run(["ln", "-s", path + "/drop_db.lua", "drop_db.lua"], path + "/tarantool_snap") - run(["tarantool", "drop_db.lua"]) - else: - raise Exception("There is no tarantool spaces to drop") - - -class TarantoolDB: - def __init__(self, host: str, port: int, username: str, password: str): - self.db_connect = tarantool.connect(host=host, port=port, user=username, password=password) - self._spaces = { - "abci_chains": self.db_connect.space("abci_chains"), - "assets": self.db_connect.space("assets"), - "blocks": {"blocks": self.db_connect.space("blocks"), "blocks_tx": self.db_connect.space("blocks_tx")}, - "elections": self.db_connect.space("elections"), - "meta_data": self.db_connect.space("meta_data"), - "pre_commits": self.db_connect.space("pre_commits"), - "validators": self.db_connect.space("validators"), - "transactions": { - "transactions": self.db_connect.space("transactions"), - "inputs": self.db_connect.space("inputs"), - "outputs": self.db_connect.space("outputs"), - "keys": self.db_connect.space("keys") - } - } - - def get_space(self, spacename: str): - return self._spaces[spacename] diff --git a/planetmint/backend/tarantool/query.py b/planetmint/backend/tarantool/query.py index efe5322..cb6e6cc 100644 --- a/planetmint/backend/tarantool/query.py +++ b/planetmint/backend/tarantool/query.py @@ -11,7 +11,7 @@ from operator import itemgetter from planetmint.backend import query from planetmint.backend.utils import module_dispatch_registrar from planetmint.backend.tarantool.connection import TarantoolDB -from planetmint.common.transaction import TransactionPrepare +from planetmint.backend.tarantool.transaction.tools import TransactionCompose, TransactionDecompose register_query = module_dispatch_registrar(query) @@ -38,15 +38,25 @@ def _group_transaction_by_ids(connection, txids: list): _txinputs = sorted(_txinputs, key=itemgetter(6), reverse=False) _txoutputs = sorted(_txoutputs, key=itemgetter(8), reverse=False) + result_map = { + "transaction": _txobject, + "inputs": _txinputs, + "outputs": _txoutputs, + "keys": _txkeys, + "assets": _txassets, + "metadata": _txmeta, + } + tx_compose = TransactionCompose() + _transaction = tx_compose.convert_to_dict(db_results=result_map) _obj = { "inputs": [ { - "owners_before": _in[2], + "fulfillment": _in[1], "fulfills": {"transaction_id": _in[3], "output_index": int(_in[4])} if len(_in[3]) > 0 and len( # TODO Now it is working because of data type cast to INTEGER for field "output_index" _in[4]) > 0 else None, - "fulfillment": _in[1] + "owners_before": _in[2] } for _in in _txinputs ], "outputs": [], @@ -59,18 +69,18 @@ def _group_transaction_by_ids(connection, txids: list): if _txoutputs[0][7] is None: _obj["outputs"] = [ { - "public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]], + "amount": _out[1], "condition": {"details": {"type": _out[3], "public_key": _out[4]}, "uri": _out[2]}, - "amount": _out[1] + "public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]] } for _out in _txoutputs ] else: _obj["outputs"] = [ { - "public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]], "amount": _out[1], "condition": {"uri": _out[2], "details": {"subconditions": _out[7]}, "type": _out[3], - "treshold": _out[6]} + "treshold": _out[6]}, + "public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]] } for _out in _txoutputs ] @@ -78,10 +88,8 @@ def _group_transaction_by_ids(connection, txids: list): _obj["asset"] = { "id": _txobject[3] } - elif len(_txassets) == 1: - _obj["asset"] = { - "data": _txassets[0][1] - } + elif len(_txassets) > 0: + _obj["asset"] = _txassets[0][1] _obj["metadata"] = _txmeta[0][1] if len(_txmeta) == 1 else None _transactions.append(_obj) return _transactions @@ -97,7 +105,7 @@ def store_transactions(connection, signed_transactions: list): assetsxspace = connection.space("assets") for transaction in signed_transactions: - txprepare = TransactionPrepare(transaction) + txprepare = TransactionDecompose(transaction) txtuples = txprepare.convert_to_tuple() txspace.insert(txtuples["transactions"]) @@ -154,9 +162,9 @@ def store_asset(connection, asset: dict, tx_id=None, is_data=False): # TODO con space = connection.space("assets") try: if is_data and tx_id is not None: - space.insert((tx_id, asset["data"])) + space.insert((tx_id, asset)) else: - space.insert((str(asset["id"]), asset["data"])) + space.insert((str(asset["id"]), asset)) except: # TODO Add Raise For Duplicate print("DUPLICATE ERROR") @@ -176,7 +184,7 @@ def get_asset(connection, asset_id: str): space = connection.space("assets") _data = space.select(asset_id, index="assetid_search") _data = _data.data - return {"data": _data[0][1]} if len(_data) == 1 else [] + return _data[0][1] if len(_data) == 1 else [] @register_query(TarantoolDB) diff --git a/planetmint/backend/tarantool/transaction/tools.py b/planetmint/backend/tarantool/transaction/tools.py new file mode 100644 index 0000000..9e6ca30 --- /dev/null +++ b/planetmint/backend/tarantool/transaction/tools.py @@ -0,0 +1,134 @@ +from secrets import token_hex + + +def _save_keys_order(dictionary): + if type(dictionary) is dict: + keys = list(dictionary.keys()) + _map = {} + for key in keys: + _map[key] = _save_keys_order(dictionary=dictionary[key]) + + return _map + elif type(dictionary) is list: + dictionary = next(iter(dictionary), None) + if dictionary is not None and type(dictionary) is dict: + _map = {} + keys = list(dictionary.keys()) + for key in keys: + _map[key] = _save_keys_order(dictionary=dictionary[key]) + + return _map + else: + return None + + +class TransactionDecompose: + def __init__(self, _transaction): + self._transaction = _transaction + self._tuple_transaction = { + "transactions": (), + "inputs": [], + "outputs": [], + "keys": [], + "metadata": (), + "asset": "", + "asset_data": (), + "is_data": False + } + self.if_key = lambda dct, key: False if not key in dct.keys() else dct[key] + + def get_map(self, dictionary: dict = None): + return _save_keys_order(dictionary=dictionary) if dictionary is not None else _save_keys_order( + dictionary=self._transaction) + + def __create_hash(self, n: int): + return token_hex(n) + + def _metadata_check(self): + metadata = self._transaction.get("metadata") + self._tuple_transaction["metadata"] = (self._transaction["id"], metadata) if metadata is not None else () + + def __asset_check(self): + _asset = self._transaction.get("asset") + if _asset is None: + self._tuple_transaction["asset"] = "" + return + + _id = self.if_key(dct=_asset, key="id") + if _id is not False: + self._tuple_transaction["asset"] = _id + return + + self._tuple_transaction["is_data"] = True + self._tuple_transaction["asset_data"] = (self._transaction["id"], _asset) + self._tuple_transaction["asset"] = "" + + def __prepare_inputs(self): + _inputs = [] + input_index = 0 + for _input in self._transaction["inputs"]: + _inputs.append((self._transaction["id"], + _input["fulfillment"], + _input["owners_before"], + _input["fulfills"]["transaction_id"] if _input["fulfills"] is not None else "", + str(_input["fulfills"]["output_index"]) if _input["fulfills"] is not None else "", + self.__create_hash(7), + input_index)) + input_index = input_index + 1 + return _inputs + + def __prepare_outputs(self): + _outputs = [] + _keys = [] + output_index = 0 + for _output in self._transaction["outputs"]: + output_id = self.__create_hash(7) + if _output["condition"]["details"].get("subconditions") is None: + _outputs.append((self._transaction["id"], + _output["amount"], + _output["condition"]["uri"], + _output["condition"]["details"]["type"], + _output["condition"]["details"]["public_key"], + output_id, + None, + None, + output_index + )) + else: + _outputs.append((self._transaction["id"], + _output["amount"], + _output["condition"]["uri"], + _output["condition"]["details"]["type"], + None, + output_id, + _output["condition"]["details"]["threshold"], + _output["condition"]["details"]["subconditions"], + output_index + )) + output_index = output_index + 1 + for _key in _output["public_keys"]: + key_id = self.__create_hash(7) + _keys.append((key_id, self._transaction["id"], output_id, _key)) + return _keys, _outputs + + def __prepare_transaction(self): + return (self._transaction["id"], + self._transaction["operation"], + self._transaction["version"], + self._tuple_transaction["asset"], + self.get_map()) + + def convert_to_tuple(self): + self._metadata_check() + self.__asset_check() + self._tuple_transaction["transactions"] = self.__prepare_transaction() + self._tuple_transaction["inputs"] = self.__prepare_inputs() + keys, outputs = self.__prepare_outputs() + self._tuple_transaction["outputs"] = outputs + self._tuple_transaction["keys"] = keys + return self._tuple_transaction + + +class TransactionCompose: + def convert_to_dict(self, db_results): + transaction_map = db_results["transaction"][4] diff --git a/planetmint/common/transaction.py b/planetmint/common/transaction.py index 3b924e4..a7ce183 100644 --- a/planetmint/common/transaction.py +++ b/planetmint/common/transaction.py @@ -26,7 +26,6 @@ try: except ImportError: from sha3 import sha3_256 -from secrets import token_hex from planetmint.common.crypto import PrivateKey, hash_data from planetmint.common.exceptions import (KeypairMismatchException, InputDoesNotExist, DoubleSpend, @@ -1182,6 +1181,7 @@ class Transaction(object): tx_body_serialized = Transaction._to_str(tx_body) valid_tx_id = Transaction._to_hash(tx_body_serialized) + print("VALIDTX " + tx_body_serialized) if proposed_tx_id != valid_tx_id: err_msg = ("The transaction's id '{}' isn't equal to " "the hash of its body, i.e. it's not valid.") @@ -1328,106 +1328,3 @@ class Transaction(object): raise InvalidSignature('Transaction signature is invalid.') return True - - -class TransactionPrepare: - def __init__(self, _transaction): - self._transaction = _transaction - self._tuple_transaction = { - "transactions": (), - "inputs": [], - "outputs": [], - "keys": [], - "metadata": (), - "asset": "", - "asset_data": (), - "is_data": False - } - self.if_key = lambda dct, key: False if not key in dct.keys() else dct[key] - - def __create_hash(self, n: int): - return token_hex(n) - - def _metadata_check(self): - metadata = self._transaction.get("metadata") - self._tuple_transaction["metadata"] = (self._transaction["id"], metadata) if metadata is not None else () - - def __asset_check(self): - _asset = self._transaction.get("asset") - if _asset is None: - self._tuple_transaction["asset"] = "" - return - - _id = self.if_key(dct=_asset, key="id") - # data = self.if_key(dct=_asset, key="data") - if _id is not False: - self._tuple_transaction["asset"] = _id - else: - self._tuple_transaction["is_data"] = True - _key = list(_asset.keys())[0] - self._tuple_transaction["asset_data"] = (self._transaction["id"], _asset[_key]) - self._tuple_transaction["asset"] = "" - - def __prepare_inputs(self): - _inputs = [] - input_index = 0 - for _input in self._transaction["inputs"]: - _inputs.append((self._transaction["id"], - _input["fulfillment"], - _input["owners_before"], - _input["fulfills"]["transaction_id"] if _input["fulfills"] is not None else "", - str(_input["fulfills"]["output_index"]) if _input["fulfills"] is not None else "", - self.__create_hash(7), - input_index)) - input_index = input_index + 1 - return _inputs - - def __prepare_outputs(self): - _outputs = [] - _keys = [] - output_index = 0 - for _output in self._transaction["outputs"]: - output_id = self.__create_hash(7) - if _output["condition"]["details"].get("subconditions") is None: - _outputs.append((self._transaction["id"], - _output["amount"], - _output["condition"]["uri"], - _output["condition"]["details"]["type"], - _output["condition"]["details"]["public_key"], - output_id, - None, - None, - output_index - )) - else: - _outputs.append((self._transaction["id"], - _output["amount"], - _output["condition"]["uri"], - _output["condition"]["details"]["type"], - None, - output_id, - _output["condition"]["details"]["threshold"], - _output["condition"]["details"]["subconditions"], - output_index - )) - output_index = output_index + 1 - for _key in _output["public_keys"]: - key_id = self.__create_hash(7) - _keys.append((key_id, self._transaction["id"], output_id, _key)) - return _keys, _outputs - - def __prepare_transaction(self): - return (self._transaction["id"], - self._transaction["operation"], - self._transaction["version"], - self._tuple_transaction["asset"]) - - def convert_to_tuple(self): - self._metadata_check() - self.__asset_check() - self._tuple_transaction["transactions"] = self.__prepare_transaction() - self._tuple_transaction["inputs"] = self.__prepare_inputs() - keys, outputs = self.__prepare_outputs() - self._tuple_transaction["outputs"] = outputs - self._tuple_transaction["keys"] = keys - return self._tuple_transaction diff --git a/tests/tendermint/test_lib.py b/tests/tendermint/test_lib.py index 3a454ce..0d5b4bc 100644 --- a/tests/tendermint/test_lib.py +++ b/tests/tendermint/test_lib.py @@ -380,6 +380,7 @@ def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol): [([bob.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) + print("PROPOSEDTX " + str(same_input_double_spend.to_dict())) b.store_bulk_transactions([tx])