From cd74ea578d29931f65de284b9bec538cbdfd33b6 Mon Sep 17 00:00:00 2001 From: cybnon Date: Wed, 7 Dec 2022 11:44:01 +0100 Subject: [PATCH] Adapt models Signed-off-by: cybnon --- planetmint/backend/models/asset.py | 16 +- planetmint/backend/models/dbtransaction.py | 23 ++- planetmint/backend/models/input.py | 6 +- planetmint/backend/models/metadata.py | 8 +- planetmint/backend/models/output.py | 114 +++++++---- planetmint/backend/models/script.py | 10 +- planetmint/backend/tarantool/const.py | 3 + planetmint/backend/tarantool/query.py | 218 ++++----------------- planetmint/lib.py | 18 +- tests/backend/tarantool/test_queries.py | 57 ------ 10 files changed, 156 insertions(+), 317 deletions(-) diff --git a/planetmint/backend/models/asset.py b/planetmint/backend/models/asset.py index d1b3674..9a1a8f6 100644 --- a/planetmint/backend/models/asset.py +++ b/planetmint/backend/models/asset.py @@ -10,21 +10,21 @@ from dataclasses import dataclass @dataclass class Asset: - id: str = "" - tx_id: str = "" - data: dict = "" + data: str = "" @staticmethod - def from_tuple(asset_tuple: tuple) -> Asset: - return Asset(asset_tuple[2], asset_tuple[1], json.loads(asset_tuple[0])["data"]) + def from_dict(asset_tuple: dict) -> Asset: + return Asset(asset_tuple["data"]) def to_dict(self) -> dict: return { - "id": self.id, - "tx_id": self.tx_id, "data": self.data } + @staticmethod + def from_list_dict(asset_tuple_list: list[tuple]) -> list[Asset]: + return [Asset.from_dict(asset_tuple) for asset_tuple in asset_tuple_list] + @staticmethod def list_to_dict(asset_list: list[Asset]) -> list[dict]: - return [asset.to_dict() for asset in asset_list] + return [asset.to_dict() for asset in asset_list or []] diff --git a/planetmint/backend/models/dbtransaction.py b/planetmint/backend/models/dbtransaction.py index 9022aa3..fd86cfc 100644 --- a/planetmint/backend/models/dbtransaction.py +++ b/planetmint/backend/models/dbtransaction.py @@ -15,12 +15,9 @@ class DbTransaction: id: str = "" operation: str = "" version: str = "" - raw_transaction: dict = dict - assets: list[Asset] = None metadata: MetaData = None + assets: list[Asset] = None inputs: list[Input] = None - outputs: list[Output] = None - keys: Keys = None script: Script = None @staticmethod @@ -29,8 +26,10 @@ class DbTransaction: id=transaction["id"], operation=transaction["operation"], version=transaction["version"], - inputs=transaction["inputs"], - raw_transaction=transaction["transaction"], + inputs=Input.from_list_dict(transaction["inputs"]), + assets=Asset.from_list_dict(transaction["assets"]), + metadata=MetaData.from_dict(transaction["metadata"]), + script=Script.from_dict(transaction["script"]), ) @staticmethod @@ -39,7 +38,10 @@ class DbTransaction: id=transaction[0], operation=transaction[1], version=transaction[2], - raw_transaction=transaction[3], + metadata=MetaData.from_dict(transaction[3]), + assets=Asset.from_list_dict(transaction[4]), + inputs=Input.from_list_dict(transaction[5]), + script=Script.from_dict(transaction[6]), ) def to_dict(self) -> dict: @@ -49,9 +51,6 @@ class DbTransaction: "version": self.version, "inputs": Input.list_to_dict(self.inputs), "assets": Asset.list_to_dict(self.assets), - "metadata": self.metadata.to_dict(), - "outputs": Output.list_to_dict(self.outputs), - "keys": self.keys.to_dict(), - "script": self.script.to_dict(), - "transaction": self.raw_transaction, + "metadata": self.metadata.to_dict() if self.metadata is not None else None, + "script": self.script.to_dict() if self.script is not None else None, } diff --git a/planetmint/backend/models/input.py b/planetmint/backend/models/input.py index 51047a7..9f2d824 100644 --- a/planetmint/backend/models/input.py +++ b/planetmint/backend/models/input.py @@ -49,8 +49,12 @@ class Input: return {"fulfills": fulfills, "fulfillment": self.fulfillment, "owners_before": self.owners_before} + @staticmethod + def from_list_dict(input_tuple_list: list[dict]) -> list[Input]: + return [Input.from_dict(input_tuple) for input_tuple in input_tuple_list] + @staticmethod def list_to_dict(input_list: list[Input]) -> list[dict]: - return [input.to_dict() for input in input_list] + return [input.to_dict() for input in input_list or []] diff --git a/planetmint/backend/models/metadata.py b/planetmint/backend/models/metadata.py index c362a3f..3470728 100644 --- a/planetmint/backend/models/metadata.py +++ b/planetmint/backend/models/metadata.py @@ -11,15 +11,15 @@ from typing import Optional @dataclass class MetaData: - id: str = "" metadata: Optional[str] = None @staticmethod - def from_tuple(meta_data_tuple: tuple) -> MetaData: - return MetaData(meta_data_tuple[0], json.loads(meta_data_tuple[1])) + def from_dict(meta_data_tuple: dict) -> MetaData: + if meta_data_tuple is None: + return MetaData() + return MetaData(meta_data_tuple["meta_data"]) def to_dict(self) -> dict: return { - "id": self.id, "metadata": self.metadata } diff --git a/planetmint/backend/models/output.py b/planetmint/backend/models/output.py index b8e72b5..5bdf674 100644 --- a/planetmint/backend/models/output.py +++ b/planetmint/backend/models/output.py @@ -15,6 +15,12 @@ class SubCondition: type: str public_key: str + def to_tuple(self) -> tuple: + return self.type, self.public_key + + @staticmethod + def from_dict(subcondition_dict: dict) -> SubCondition: + return SubCondition(subcondition_dict["type"], subcondition_dict["public_key"]) @dataclass class ConditionDetails: @@ -23,11 +29,17 @@ class ConditionDetails: threshold: int = 0 sub_conditions: list[SubCondition] = None - def sub_conditions_to_list_dict(self): - if self.sub_conditions is None: - return None - return [sub_condition.__dict__ for sub_condition in self.sub_conditions] - + @staticmethod + def from_dict(data: dict) -> ConditionDetails: + sub_conditions = None + if data["sub_conditions"] is not None: + sub_conditions = [SubCondition.from_dict(sub_condition) for sub_condition in data["sub_conditions"]] + return ConditionDetails( + type=data.get("type"), + public_key=data.get("public_key"), + threshold=data.get("threshold"), + sub_conditions=sub_conditions, + ) @dataclass class Condition: @@ -35,43 +47,60 @@ class Condition: details: ConditionDetails = field(default_factory=ConditionDetails) -@dataclass -class Output: - id: str = "" - tx_id: str = "" - amount: str = '0' - public_keys: List[str] = field(default_factory=list) - condition: Condition = field(default_factory=Condition) - @staticmethod - def outputs_and_keys_dict(output: dict, tx_id: str = "") -> (Output, Keys): - out_dict: Output - if output["condition"]["details"].get("subconditions") is None: - out_dict = output_with_public_key(output, tx_id) - else: - out_dict = output_with_sub_conditions(output, tx_id) - return out_dict, Keys.from_dict(output, tx_id) - - @staticmethod - def from_tuple(output: tuple) -> Output: - return Output( - id=output[5], - tx_id=output[0], - amount=output[1], - condition=Condition( - uri=output[2], - details=ConditionDetails( - type=output[3], - public_key=output[4], - threshold=output[6], - sub_conditions=output[7], - ), - ), + def from_dict(data: dict) -> Condition: + return Condition( + uri=data.get("uri"), + details=ConditionDetails.from_dict(data.get("details")), ) def to_dict(self) -> dict: return { - "id": self.tx_id, + "uri": self.uri, + "details": self.details.__dict__, + } + + @staticmethod + def list_of_sub_conditions_to_tuple(sub_conditions: List[SubCondition]) -> tuple: + sub_con_tuple = None + if sub_conditions is not None: + sub_con_tuple = [sub_condition.to_tuple() for sub_condition in sub_conditions] + return sub_con_tuple + +@dataclass +class Output: + id: str = "" + amount: int = 0 + transaction_id: str = "" + public_keys: List[str] = field(default_factory=list) + index: int = 0 + condition: Condition = field(default_factory=Condition) + + @staticmethod + def outputs_dict(output: dict, transaction_id: str = "") -> Output: + out_dict: Output + if output["condition"]["details"].get("subconditions") is None: + out_dict = output_with_public_key(output, transaction_id) + else: + out_dict = output_with_sub_conditions(output, transaction_id) + return out_dict + + @staticmethod + def from_tuple(output: tuple) -> Output: + return Output( + id=output[0], + amount=output[1], + public_keys=output[2], + condition=Condition.from_dict( + output[3], + ), + index=output[4], + transaction_id=output[5], + ) + + def to_dict(self) -> dict: + return { + "id": self.transaction_id, "amount": self.amount, "public_keys": self.public_keys, "condition": { @@ -85,14 +114,15 @@ class Output: }, } + @staticmethod def list_to_dict(output_list: list[Output]) -> list[dict]: - return [output.to_dict() for output in output_list] + return [output.to_dict() for output in output_list or []] -def output_with_public_key(output, tx_id) -> Output: +def output_with_public_key(output, transaction_id) -> Output: return Output( - tx_id=tx_id, + transaction_id=transaction_id, public_keys=output["public_keys"], amount=output["amount"], condition=Condition( @@ -105,9 +135,9 @@ def output_with_public_key(output, tx_id) -> Output: ) -def output_with_sub_conditions(output, tx_id) -> Output: +def output_with_sub_conditions(output, transaction_id) -> Output: return Output( - tx_id=tx_id, + transaction_id=transaction_id, public_keys=output["public_keys"], amount=output["amount"], condition=Condition( diff --git a/planetmint/backend/models/script.py b/planetmint/backend/models/script.py index 682a728..f4fe267 100644 --- a/planetmint/backend/models/script.py +++ b/planetmint/backend/models/script.py @@ -10,15 +10,15 @@ from typing import Optional @dataclass class Script: - id: str = "" - script: Optional[str] = None + script: dict = None @staticmethod - def from_tuple(script_tuple: tuple) -> Script: - return Script(script_tuple[0], script_tuple[1]) + def from_dict(script_dict: dict) -> Script: + if script_dict is None: + return Script() + return Script(script_dict["script"]) def to_dict(self) -> dict: return { - "id": self.id, "script": self.script } diff --git a/planetmint/backend/tarantool/const.py b/planetmint/backend/tarantool/const.py index 68fe11a..83fa856 100644 --- a/planetmint/backend/tarantool/const.py +++ b/planetmint/backend/tarantool/const.py @@ -12,3 +12,6 @@ TARANT_TABLE_OUTPUT = "outputs" TARANT_TABLE_SCRIPT = "scripts" TARANT_TX_ID_SEARCH = "transaction_id" TARANT_ID_SEARCH = "id" + +TARANT_INDEX_TX_BY_ASSET_ID = "transactions_by_asset" +TARANT_INDEX_SPENDING_BY_ID_AND_OUTPUT_INDEX = "spending_transaction_by_id_and_output_index" diff --git a/planetmint/backend/tarantool/query.py b/planetmint/backend/tarantool/query.py index 39717d5..0033998 100644 --- a/planetmint/backend/tarantool/query.py +++ b/planetmint/backend/tarantool/query.py @@ -8,10 +8,10 @@ import json from uuid import uuid4 from hashlib import sha256 from operator import itemgetter -from typing import Union -from tarantool.error import DatabaseError + +from planetmint.backend.models.output import Condition + from planetmint.backend import query -from planetmint.backend.models.keys import Keys from planetmint.backend.models.dbtransaction import DbTransaction from planetmint.backend.tarantool.const import ( TARANT_TABLE_META_DATA, @@ -22,7 +22,7 @@ from planetmint.backend.tarantool.const import ( TARANT_TABLE_OUTPUT, TARANT_TABLE_SCRIPT, TARANT_TX_ID_SEARCH, - TARANT_ID_SEARCH, + TARANT_ID_SEARCH, TARANT_INDEX_TX_BY_ASSET_ID, TARANT_INDEX_SPENDING_BY_ID_AND_OUTPUT_INDEX, ) from planetmint.backend.utils import module_dispatch_registrar from planetmint.backend.models import Asset, Block, MetaData, Input, Script, Output @@ -38,144 +38,61 @@ def _group_transaction_by_ids(connection, txids: list) -> list[DbTransaction]: tx = get_transaction_space_by_id(connection, txid) if tx is None: continue - - tx.inputs = get_inputs_by_tx_id(connection, txid) - _output = get_outputs_by_tx_id(connection, txid) - _keys = get_keys_by_tx_id(connection, txid) - tx.outputs = [_enricht_output_with_public_keys(_keys, output) for output in _output] - tx.assets = get_assets_by_tx_id(connection, txid) - tx.metadata = get_metadata_by_tx_id(connection, txid) - tx.script = get_script_by_tx_id(connection, txid) - _transactions.append(tx) return _transactions -def _enricht_output_with_public_keys(keys: list[Keys], output: Output) -> Output: - output.public_keys = [key.public_keys for key in keys if key.output_id == output.id] - return output - -@register_query(TarantoolDBConnection) -def get_inputs_by_tx_id(connection, tx_id: str) -> list[Input]: - _inputs = connection.run(connection.space(TARANT_TABLE_INPUT).select(tx_id, index=TARANT_ID_SEARCH)) - _sorted_inputs = sorted(_inputs, key=itemgetter(6)) - return [Input.from_tuple(input) for input in _sorted_inputs] - - @register_query(TarantoolDBConnection) def get_outputs_by_tx_id(connection, tx_id: str) -> list[Output]: - _outputs = connection.run(connection.space(TARANT_TABLE_OUTPUT).select(tx_id, index=TARANT_ID_SEARCH)) - _sorted_outputs = sorted(_outputs, key=itemgetter(8)) + _outputs = connection.run(connection.space(TARANT_TABLE_OUTPUT).select(tx_id, index=TARANT_TX_ID_SEARCH)) + _sorted_outputs = sorted(_outputs, key=itemgetter(4)) return [Output.from_tuple(output) for output in _sorted_outputs] -@register_query(TarantoolDBConnection) -def get_keys_by_tx_id(connection, tx_id: str) -> list[Keys]: - _keys = connection.run(connection.space(TARANT_TABLE_KEYS).select(tx_id, index=TARANT_TX_ID_SEARCH)) - _sorted_keys = sorted(_keys, key=itemgetter(4)) - return [Keys.from_tuple(key) for key in _sorted_keys] - - @register_query(TarantoolDBConnection) def get_transaction(connection, tx_id: str) -> DbTransaction: return NotImplemented -@register_query(TarantoolDBConnection) -def store_transaction_inputs(connection, input: Input, index: int): - connection.run( - connection.space(TARANT_TABLE_INPUT).insert( - ( - input.tx_id, - input.fulfillment, - input.owners_before, - input.fulfills.transaction_id if input.fulfills else "", - # TODO: the output_index should be an unsigned int - str(input.fulfills.output_index) if input.fulfills else "", - uuid4().hex, - index, - ) - ) - ) - - -@register_query(TarantoolDBConnection) -def store_transaction_outputs_and_keys(connection, output_key: (Output, Keys), index: int): - output_id = store_transaction_outputs(connection, output_key[0], index) - store_transaction_keys(connection, output_key[1], output_id, index) - def store_transaction_outputs(connection, output: Output, index: int) -> str: + # TODO: store public keys as well + output_id = uuid4().hex - if output.condition.details.sub_conditions is None: - tmp_output = ( - output.tx_id, - output.amount, - output.condition.uri if output.condition else "", - output.condition.details.type if output.condition.details else "", - output.condition.details.public_key if output.condition.details else "", - output_id, - None, - None, - index, - ) - else: - tmp_output = ( - output.tx_id, - output.amount, - output.condition.uri if output.condition else "", - output.condition.details.type if output.condition.details else "", - None, - output_id, - output.condition.details.threshold if output.condition.details else "", - output.condition.details.sub_conditions_to_list_dict() if output.condition.details.sub_conditions else "", - index, - ) - - connection.run(connection.space(TARANT_TABLE_OUTPUT).insert((tmp_output))) + connection.run(connection.space(TARANT_TABLE_OUTPUT).insert(( + output_id, + int(output.amount), + output.public_keys, + output.condition.to_dict(), + index, + output.transaction_id, + ))) return output_id -@register_query(TarantoolDBConnection) -def store_transaction_keys(connection, keys: Keys, output_id: str, index: int): - for key in keys.public_keys: - connection.run(connection.space(TARANT_TABLE_KEYS).insert((uuid4().hex, keys.tx_id, output_id, key, index))) - - @register_query(TarantoolDBConnection) def store_transactions(connection, signed_transactions: list): for transaction in signed_transactions: store_transaction(connection, transaction) [ - store_transaction_inputs(connection, Input.from_dict(input, transaction["id"]), index) - for index, input in enumerate(transaction[TARANT_TABLE_INPUT]) - ] - - [ - store_transaction_outputs_and_keys( - connection, Output.outputs_and_keys_dict(output, transaction["id"]), index - ) + store_transaction_outputs(connection, Output.outputs_dict(output, transaction["id"]), index) for index, output in enumerate(transaction[TARANT_TABLE_OUTPUT]) ] - # store_metadatas(connection, [MetaData(transaction["id"], transaction["metadata"])]) - - # assets = [] - # for asset in transaction[TARANT_TABLE_ASSETS]: - # id = transaction["id"] if "id" not in asset else asset["id"] - # assets.append(Asset(id, transaction["id"], asset)) - # store_assets(connection, assets) - - if TARANT_TABLE_SCRIPT in transaction: - connection.run( - connection.space(TARANT_TABLE_SCRIPT).insert((transaction["id"], transaction[TARANT_TABLE_SCRIPT])), - only_data=False, - ) - @register_query(TarantoolDBConnection) def store_transaction(connection, transaction): - tx = (transaction["id"], transaction["operation"], transaction["version"], [transaction]) + scripts = None + if TARANT_TABLE_SCRIPT in transaction: + scripts = transaction[TARANT_TABLE_SCRIPT] + tx = ( + transaction["id"], + transaction["operation"], + transaction["version"], + transaction["metadata"], + transaction["assets"], + transaction["inputs"], + scripts) connection.run(connection.space(TARANT_TABLE_TRANSACTION).insert(tx), only_data=False) @@ -197,87 +114,30 @@ def get_transactions(connection, transactions_ids: list) -> list[DbTransaction]: return _group_transaction_by_ids(txids=transactions_ids, connection=connection) -@register_query(TarantoolDBConnection) -def store_metadatas(connection, metadata: list[MetaData]): - for meta in metadata: - connection.run( - connection.space(TARANT_TABLE_META_DATA).insert((meta.id, json.dumps(meta.metadata))) # noqa: E713 - ) - - -@register_query(TarantoolDBConnection) -def get_metadata(connection, transaction_ids: list) -> list[MetaData]: - _returned_data = [] - for _id in transaction_ids: - metadata = connection.run(connection.space(TARANT_TABLE_META_DATA).select(_id, index=TARANT_ID_SEARCH)) - if metadata is not None: - if len(metadata) > 0: - metadata[0] = list(metadata[0]) - metadata[0][1] = json.loads(metadata[0][1]) - metadata = MetaData(metadata[0][0], metadata[0][1]) - _returned_data.append(metadata) - return _returned_data - - -@register_query(TarantoolDBConnection) -def get_metadata_by_tx_id(connection, transaction_id: str) -> MetaData: - metadata = connection.run(connection.space(TARANT_TABLE_META_DATA).select(transaction_id, index=TARANT_ID_SEARCH)) - return MetaData.from_tuple(metadata[0]) if len(metadata) > 0 else MetaData(transaction_id) - - -@register_query(TarantoolDBConnection) -def store_asset(connection, asset: Asset): - tuple_asset = (json.dumps(asset.data), asset.tx_id, asset.id) - try: - return connection.run(connection.space(TARANT_TABLE_ASSETS).insert(tuple_asset), only_data=False) - except DatabaseError: - pass - - -@register_query(TarantoolDBConnection) -def store_assets(connection, assets: list): - for asset in assets: - store_asset(connection, asset) - - @register_query(TarantoolDBConnection) def get_asset(connection, asset_id: str) -> Asset: - _data = connection.run(connection.space(TARANT_TABLE_ASSETS).select(asset_id, index=TARANT_TX_ID_SEARCH)) - return Asset.from_tuple(_data[0]) + _data = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset_id, index=TARANT_INDEX_TX_BY_ASSET_ID)) + return Asset.from_dict(_data[0]) @register_query(TarantoolDBConnection) def get_assets(connection, assets_ids: list) -> list[Asset]: _returned_data = [] for _id in list(set(assets_ids)): - res = connection.run(connection.space(TARANT_TABLE_ASSETS).select(_id, index=TARANT_TX_ID_SEARCH)) + res = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(_id, index=TARANT_INDEX_TX_BY_ASSET_ID)) if len(res) is 0: continue _returned_data.append(res[0]) sorted_assets = sorted(_returned_data, key=lambda k: k[1], reverse=False) - return [Asset.from_tuple(asset) for asset in sorted_assets] - - -@register_query(TarantoolDBConnection) -def get_assets_by_tx_id(connection, tx_id: str) -> list[Asset]: - res = connection.run(connection.space(TARANT_TABLE_ASSETS).select(tx_id, index=TARANT_TX_ID_SEARCH)) - if len(res) > 1: - return _from_tuple_list_to_asset_list(res) - - sorted_assets = sorted(res, key=lambda k: k[1], reverse=False) - return _from_tuple_list_to_asset_list(sorted_assets) - - -def _from_tuple_list_to_asset_list(_data: list) -> list[Asset]: - return [Asset.from_tuple(asset) for asset in _data] + return [Asset.from_dict(asset) for asset in sorted_assets] @register_query(TarantoolDBConnection) def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str): _inputs = connection.run( - connection.space(TARANT_TABLE_INPUT).select( - [fullfil_transaction_id, str(fullfil_output_index)], index="spent_search" + connection.space(TARANT_TABLE_TRANSACTION).select( + [fullfil_transaction_id, fullfil_output_index], index=TARANT_INDEX_SPENDING_BY_ID_AND_OUTPUT_INDEX ) ) return _group_transaction_by_ids(txids=[inp[0] for inp in _inputs], connection=connection) @@ -301,7 +161,7 @@ def store_block(connection, block: dict): @register_query(TarantoolDBConnection) def get_txids_filtered( - connection, asset_ids: list[str], operation: str = None, last_tx: any = None + connection, asset_ids: list[str], operation: str = None, last_tx: any = None ): # TODO here is used 'OR' operator actions = { "CREATE": {"sets": ["CREATE", asset_ids], "index": "transaction_search"}, @@ -555,7 +415,7 @@ def get_election(connection, election_id: str): @register_query(TarantoolDBConnection) def get_asset_tokens_for_public_key( - connection, asset_id: str, public_key: str + connection, asset_id: str, public_key: str ): # FIXME Something can be wrong with this function ! (public_key) is not used # noqa: E501 # space = connection.space("keys") # _keys = space.select([public_key], index="keys_search") @@ -589,9 +449,3 @@ def get_latest_abci_chain(connection): return None _chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0] return {"chain_id": _chain[0], "height": _chain[1], "is_synced": _chain[2]} - - -@register_query(TarantoolDBConnection) -def get_script_by_tx_id(connection, tx_id: str) -> Script: - script = connection.run(connection.space(TARANT_TABLE_SCRIPT).select(tx_id, index=TARANT_TX_ID_SEARCH)) - return Script.from_tuple(script[0]) if len(script) < 0 else Script(tx_id) diff --git a/planetmint/lib.py b/planetmint/lib.py index 8f958f2..7626583 100644 --- a/planetmint/lib.py +++ b/planetmint/lib.py @@ -239,6 +239,9 @@ class Planetmint(object): for txid in txids: yield self.get_transaction(txid) + def get_outputs_by_tx_id(self, txid): + return backend.query.get_outputs_by_tx_id(self.connection, txid) + def get_outputs_filtered(self, owner, spent=None): """Get a list of output links filtered on some criteria @@ -337,17 +340,15 @@ class Planetmint(object): return blocks - def validate_transaction(self, tx, current_transactions=[]): + def validate_transaction(self, transaction, current_transactions=[]): """Validate a transaction against the current status of the database.""" - transaction = tx - # CLEANUP: The conditional below checks for transaction in dict format. # It would be better to only have a single format for the transaction # throught the code base. if isinstance(transaction, dict): try: - transaction = Transaction.from_dict(tx, False) + transaction = Transaction.from_dict(transaction, False) except SchemaValidationError as e: logger.warning("Invalid transaction schema: %s", e.__cause__.message) return False @@ -371,6 +372,7 @@ class Planetmint(object): for input_ in tx.inputs: input_txid = input_.fulfills.txid input_tx = self.get_transaction(input_txid) + _output = self.get_outputs_by_tx_id(input_txid) if input_tx is None: for ctxn in current_transactions: if ctxn.id == input_txid: @@ -383,9 +385,13 @@ class Planetmint(object): if spent: raise DoubleSpend("input `{}` was already spent".format(input_txid)) - output = input_tx.outputs[input_.fulfills.output] + + output = _output[input_.fulfills.output] input_conditions.append(output) - input_txs.append(input_tx) + tx_dict = input_tx.to_dict() + tx_dict["outputs"] = output.to_dict() + pm_transaction = Transaction.from_dict(tx_dict, False) + input_txs.append(pm_transaction) # Validate that all inputs are distinct links = [i.fulfills.to_uri() for i in tx.inputs] diff --git a/tests/backend/tarantool/test_queries.py b/tests/backend/tarantool/test_queries.py index d6e7eac..ce1e23e 100644 --- a/tests/backend/tarantool/test_queries.py +++ b/tests/backend/tarantool/test_queries.py @@ -208,63 +208,6 @@ def test_get_owned_ids(signed_create_tx, user_pk, db_conn): assert founded[0]["transactions"].raw_transaction == tx_dict -def test_get_spending_transactions(user_pk, user_sk, db_conn): - from planetmint.backend.tarantool import query - - out = [([user_pk], 1)] - tx1 = Create.generate([user_pk], out * 3) - tx1.sign([user_sk]) - inputs = tx1.to_inputs() - tx2 = Transfer.generate([inputs[0]], out, [tx1.id]).sign([user_sk]) - tx3 = Transfer.generate([inputs[1]], out, [tx1.id]).sign([user_sk]) - tx4 = Transfer.generate([inputs[2]], out, [tx1.id]).sign([user_sk]) - txns = [deepcopy(tx.to_dict()) for tx in [tx1, tx2, tx3, tx4]] - query.store_transactions(signed_transactions=txns, connection=db_conn) - - links = [inputs[0].fulfills.to_dict(), inputs[2].fulfills.to_dict()] - txns = list(query.get_spending_transactions(connection=db_conn, inputs=links)) - - # tx3 not a member because input 1 not asked for - assert txns[0]["transactions"].raw_transaction == tx2.to_dict() - assert txns[1]["transactions"].raw_transaction == tx4.to_dict() - - -def test_get_spending_transactions_multiple_inputs(db_conn): - from transactions.common.crypto import generate_key_pair - from planetmint.backend.tarantool import query - - (alice_sk, alice_pk) = generate_key_pair() - (bob_sk, bob_pk) = generate_key_pair() - (carol_sk, carol_pk) = generate_key_pair() - - out = [([alice_pk], 9)] - tx1 = Create.generate([alice_pk], out).sign([alice_sk]) - - inputs1 = tx1.to_inputs() - tx2 = Transfer.generate([inputs1[0]], [([alice_pk], 6), ([bob_pk], 3)], [tx1.id]).sign([alice_sk]) - - inputs2 = tx2.to_inputs() - tx3 = Transfer.generate([inputs2[0]], [([bob_pk], 3), ([carol_pk], 3)], [tx1.id]).sign([alice_sk]) - - inputs3 = tx3.to_inputs() - tx4 = Transfer.generate([inputs2[1], inputs3[0]], [([carol_pk], 6)], [tx1.id]).sign([bob_sk]) - - txns = [deepcopy(tx.to_dict()) for tx in [tx1, tx2, tx3, tx4]] - query.store_transactions(signed_transactions=txns, connection=db_conn) - - links = [ - ({"transaction_id": tx2.id, "output_index": 0}, 1, [tx3.id]), - ({"transaction_id": tx2.id, "output_index": 1}, 1, [tx4.id]), - ({"transaction_id": tx3.id, "output_index": 0}, 1, [tx4.id]), - ({"transaction_id": tx3.id, "output_index": 1}, 0, None), - ] - for li, num, match in links: - txns = list(query.get_spending_transactions(connection=db_conn, inputs=[li])) - assert len(txns) == num - if len(txns): - assert [tx["transactions"].id for tx in txns] == match - - def test_store_block(db_conn): from planetmint.lib import Block from planetmint.backend.tarantool import query