From f498415657b9664bc3b5cb096130a367e185f682 Mon Sep 17 00:00:00 2001 From: andrei Date: Fri, 25 Mar 2022 15:58:58 +0200 Subject: [PATCH] adopted for subconditions. fixed issues with functions --- planetmint/backend/tarantool/connection.py | 7 +- planetmint/backend/tarantool/query.py | 92 +++++++++--------- planetmint/common/transaction.py | 103 ++++++++++++++++++++- planetmint/lib.py | 2 +- tests/assets/test_divisible_assets.py | 1 - 5 files changed, 155 insertions(+), 50 deletions(-) diff --git a/planetmint/backend/tarantool/connection.py b/planetmint/backend/tarantool/connection.py index 8165bd4..b74e4f7 100644 --- a/planetmint/backend/tarantool/connection.py +++ b/planetmint/backend/tarantool/connection.py @@ -30,8 +30,11 @@ class TarantoolDB: self.drop_database() self.init_database() - def get_connection(self, space_name: str = None): - return self.db_connect if space_name is None else self.db_connect.space(space_name) + def space(self, space_name: str): + return self.db_connect.space(space_name) + + def get_connection(self): + return self.db_connect def __read_commands(self, file_path): with open(file_path, "r") as cmd_file: diff --git a/planetmint/backend/tarantool/query.py b/planetmint/backend/tarantool/query.py index c6fc14e..c1f1009 100644 --- a/planetmint/backend/tarantool/query.py +++ b/planetmint/backend/tarantool/query.py @@ -11,6 +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 register_query = module_dispatch_registrar(query) @@ -46,15 +47,25 @@ def _group_transaction_by_ids(connection, txids: list): _in[4]) > 0 else None, "fulfillment": _in[1] } for _in in _txinputs - ], - "outputs": [ + ] + } + 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]} } 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]} + } for _out in _txoutputs + ] + if len(_txobject[3]) > 0: _obj["asset"] = { "id": _txobject[3] @@ -70,20 +81,21 @@ def _group_transaction_by_ids(connection, txids: list): def __asset_check(object: dict, connection): - res = object.get("asset").get("id") - res = "" if res is None else res - data = object.get("asset").get("data") + _asset = object.get("asset") + data = None + _id = None + if _asset is not None: + _id = _asset.get("id") + data = _asset.get("data") if _id is None else None + if data is not None: store_asset(connection=connection, asset=object["asset"], tx_id=object["id"], is_data=True) + elif _id is not None: + data = _id + else: + data = "" - return res - - -def __metadata_check(object: dict, connection): - metadata = object.get("metadata") - if metadata is not None: - space = connection.space("meta_data") - space.insert((object["id"], metadata)) + return data @register_query(TarantoolDB) @@ -92,33 +104,29 @@ def store_transactions(connection, signed_transactions: list): inxspace = connection.space("inputs") outxspace = connection.space("outputs") keysxspace = connection.space("keys") + metadatasxspace = connection.space("meta_data") + assetsxspace = connection.space("assets") + for transaction in signed_transactions: - __metadata_check(object=transaction, connection=connection) - txspace.insert((transaction["id"], - transaction["operation"], - transaction["version"], - __asset_check(object=transaction, connection=connection) - )) - for _in in transaction["inputs"]: - input_id = token_hex(7) - inxspace.insert((transaction["id"], - _in["fulfillment"], - _in["owners_before"], - _in["fulfills"]["transaction_id"] if _in["fulfills"] is not None else "", - str(_in["fulfills"]["output_index"]) if _in["fulfills"] is not None else "", - input_id)) - for _out in transaction["outputs"]: - output_id = token_hex(7) - outxspace.insert((transaction["id"], - _out["amount"], - _out["condition"]["uri"], - _out["condition"]["details"]["type"], - _out["condition"]["details"]["public_key"], - output_id - )) - for _key in _out["public_keys"]: - unique_id = token_hex(8) - keysxspace.insert((unique_id, transaction["id"], output_id, _key)) + txprepare = TransactionPrepare(transaction) + txtuples = txprepare.convert_to_tuple() + + txspace.insert(txtuples["transactions"]) + + for _in in txtuples["inputs"]: + inxspace.insert(_in) + + for _out in txtuples["outputs"]: + outxspace.insert(_out) + + for _key in txtuples["keys"]: + keysxspace.insert(_key) + + if len(txtuples["metadata"]) > 0: + metadatasxspace.insert(txtuples["metadata"]) + + if txtuples["is_data"]: + assetsxspace.insert(txtuples["asset_data"]) @register_query(TarantoolDB) @@ -178,8 +186,8 @@ def store_assets(connection, assets: list): def get_asset(connection, asset_id: str): space = connection.space("assets") _data = space.select(asset_id, index="assetid_search") - _data = _data.data[0] - return {"data": _data[1]} + _data = _data.data + return {"data": _data[0][1]} if len(_data) == 1 else [] @register_query(TarantoolDB) diff --git a/planetmint/common/transaction.py b/planetmint/common/transaction.py index c7301f6..2b9a519 100644 --- a/planetmint/common/transaction.py +++ b/planetmint/common/transaction.py @@ -20,11 +20,13 @@ import base58 from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256 from cryptoconditions.exceptions import ( ParsingError, ASN1DecodeError, ASN1EncodeError, UnsupportedTypeError) + try: from hashlib import sha3_256 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, @@ -34,7 +36,6 @@ from planetmint.common.exceptions import (KeypairMismatchException, from planetmint.common.utils import serialize from .memoize import memoize_from_dict, memoize_to_dict - UnspentOutput = namedtuple( 'UnspentOutput', ( # TODO 'utxo_hash': sha3_256(f'{txid}{output_index}'.encode()) @@ -544,7 +545,7 @@ class Transaction(object): raise TypeError(('`asset` must be None or a dict holding a `data` ' " property instance for '{}' Transactions".format(operation))) elif (operation == self.TRANSFER and - not (isinstance(asset, dict) and 'id' in asset)): + not (isinstance(asset, dict) and 'id' in asset)): raise TypeError(('`asset` must be a dict holding an `id` property ' 'for \'TRANSFER\' Transactions')) @@ -834,7 +835,7 @@ class Transaction(object): return public_key.decode() key_pairs = {gen_public_key(PrivateKey(private_key)): - PrivateKey(private_key) for private_key in private_keys} + PrivateKey(private_key) for private_key in private_keys} tx_dict = self.to_dict() tx_dict = Transaction._remove_signatures(tx_dict) @@ -1294,7 +1295,7 @@ class Transaction(object): .format(input_txid)) spent = planet.get_spent(input_txid, input_.fulfills.output, - current_transactions) + current_transactions) if spent: raise DoubleSpend('input `{}` was already spent' .format(input_txid)) @@ -1328,3 +1329,97 @@ 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 + } + + 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"] = "" + + _id = _asset.get("id") + data = _asset.get("data") + if _id is not None: + self._tuple_transaction["asset"] = _id + + if data is not None: + self._tuple_transaction["is_data"] = True + self._tuple_transaction["asset_data"] = (self._transaction["id"], data) + self._tuple_transaction["asset"] = self._transaction["id"] + + def __prepare_inputs(self): + _inputs = [] + 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))) + return _inputs + + def __prepare_outputs(self): + _outputs = [] + _keys = [] + 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 + )) + 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"] + )) + 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/planetmint/lib.py b/planetmint/lib.py index 767f5bd..9f2a229 100644 --- a/planetmint/lib.py +++ b/planetmint/lib.py @@ -77,7 +77,7 @@ class Planetmint(object): else: self.validation = BaseValidationRules # planetmint.backend.tarantool.connection_tarantool.connect(**Config().get()['database']) - self.connection = connection if connection is not None else planetmint.backend.Connection().get_connection() + self.connection = connection if connection is not None else planetmint.backend.Connection() def post_transaction(self, transaction, mode): """Submit a valid transaction to the mempool.""" diff --git a/tests/assets/test_divisible_assets.py b/tests/assets/test_divisible_assets.py index 437da55..a0de76d 100644 --- a/tests/assets/test_divisible_assets.py +++ b/tests/assets/test_divisible_assets.py @@ -197,7 +197,6 @@ def test_single_in_single_own_single_out_multiple_own_transfer(alice, b, user_pk assert len(condition['condition']['details']['subconditions']) == 2 assert len(tx_transfer_signed.inputs) == 1 - b.store_bulk_transactions([tx_transfer_signed]) with pytest.raises(DoubleSpend): tx_transfer_signed.validate(b)