Adapt models

Signed-off-by: cybnon <stefan.weber93@googlemail.com>
This commit is contained in:
cybnon 2022-12-07 11:44:01 +01:00
parent 1832bf4814
commit cd74ea578d
10 changed files with 156 additions and 317 deletions

View File

@ -10,21 +10,21 @@ from dataclasses import dataclass
@dataclass @dataclass
class Asset: class Asset:
id: str = "" data: str = ""
tx_id: str = ""
data: dict = ""
@staticmethod @staticmethod
def from_tuple(asset_tuple: tuple) -> Asset: def from_dict(asset_tuple: dict) -> Asset:
return Asset(asset_tuple[2], asset_tuple[1], json.loads(asset_tuple[0])["data"]) return Asset(asset_tuple["data"])
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
"id": self.id,
"tx_id": self.tx_id,
"data": self.data "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 @staticmethod
def list_to_dict(asset_list: list[Asset]) -> list[dict]: 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 []]

View File

@ -15,12 +15,9 @@ class DbTransaction:
id: str = "" id: str = ""
operation: str = "" operation: str = ""
version: str = "" version: str = ""
raw_transaction: dict = dict
assets: list[Asset] = None
metadata: MetaData = None metadata: MetaData = None
assets: list[Asset] = None
inputs: list[Input] = None inputs: list[Input] = None
outputs: list[Output] = None
keys: Keys = None
script: Script = None script: Script = None
@staticmethod @staticmethod
@ -29,8 +26,10 @@ class DbTransaction:
id=transaction["id"], id=transaction["id"],
operation=transaction["operation"], operation=transaction["operation"],
version=transaction["version"], version=transaction["version"],
inputs=transaction["inputs"], inputs=Input.from_list_dict(transaction["inputs"]),
raw_transaction=transaction["transaction"], assets=Asset.from_list_dict(transaction["assets"]),
metadata=MetaData.from_dict(transaction["metadata"]),
script=Script.from_dict(transaction["script"]),
) )
@staticmethod @staticmethod
@ -39,7 +38,10 @@ class DbTransaction:
id=transaction[0], id=transaction[0],
operation=transaction[1], operation=transaction[1],
version=transaction[2], 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: def to_dict(self) -> dict:
@ -49,9 +51,6 @@ class DbTransaction:
"version": self.version, "version": self.version,
"inputs": Input.list_to_dict(self.inputs), "inputs": Input.list_to_dict(self.inputs),
"assets": Asset.list_to_dict(self.assets), "assets": Asset.list_to_dict(self.assets),
"metadata": self.metadata.to_dict(), "metadata": self.metadata.to_dict() if self.metadata is not None else None,
"outputs": Output.list_to_dict(self.outputs), "script": self.script.to_dict() if self.script is not None else None,
"keys": self.keys.to_dict(),
"script": self.script.to_dict(),
"transaction": self.raw_transaction,
} }

View File

@ -49,8 +49,12 @@ class Input:
return {"fulfills": fulfills, "fulfillment": self.fulfillment, "owners_before": self.owners_before} 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 @staticmethod
def list_to_dict(input_list: list[Input]) -> list[dict]: 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 []]

View File

@ -11,15 +11,15 @@ from typing import Optional
@dataclass @dataclass
class MetaData: class MetaData:
id: str = ""
metadata: Optional[str] = None metadata: Optional[str] = None
@staticmethod @staticmethod
def from_tuple(meta_data_tuple: tuple) -> MetaData: def from_dict(meta_data_tuple: dict) -> MetaData:
return MetaData(meta_data_tuple[0], json.loads(meta_data_tuple[1])) if meta_data_tuple is None:
return MetaData()
return MetaData(meta_data_tuple["meta_data"])
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
"id": self.id,
"metadata": self.metadata "metadata": self.metadata
} }

View File

@ -15,6 +15,12 @@ class SubCondition:
type: str type: str
public_key: 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 @dataclass
class ConditionDetails: class ConditionDetails:
@ -23,11 +29,17 @@ class ConditionDetails:
threshold: int = 0 threshold: int = 0
sub_conditions: list[SubCondition] = None sub_conditions: list[SubCondition] = None
def sub_conditions_to_list_dict(self): @staticmethod
if self.sub_conditions is None: def from_dict(data: dict) -> ConditionDetails:
return None sub_conditions = None
return [sub_condition.__dict__ for sub_condition in self.sub_conditions] 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 @dataclass
class Condition: class Condition:
@ -35,43 +47,60 @@ class Condition:
details: ConditionDetails = field(default_factory=ConditionDetails) 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 @staticmethod
def outputs_and_keys_dict(output: dict, tx_id: str = "") -> (Output, Keys): def from_dict(data: dict) -> Condition:
out_dict: Output return Condition(
if output["condition"]["details"].get("subconditions") is None: uri=data.get("uri"),
out_dict = output_with_public_key(output, tx_id) details=ConditionDetails.from_dict(data.get("details")),
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 to_dict(self) -> dict: def to_dict(self) -> dict:
return { 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, "amount": self.amount,
"public_keys": self.public_keys, "public_keys": self.public_keys,
"condition": { "condition": {
@ -85,14 +114,15 @@ class Output:
}, },
} }
@staticmethod @staticmethod
def list_to_dict(output_list: list[Output]) -> list[dict]: 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( return Output(
tx_id=tx_id, transaction_id=transaction_id,
public_keys=output["public_keys"], public_keys=output["public_keys"],
amount=output["amount"], amount=output["amount"],
condition=Condition( 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( return Output(
tx_id=tx_id, transaction_id=transaction_id,
public_keys=output["public_keys"], public_keys=output["public_keys"],
amount=output["amount"], amount=output["amount"],
condition=Condition( condition=Condition(

View File

@ -10,15 +10,15 @@ from typing import Optional
@dataclass @dataclass
class Script: class Script:
id: str = "" script: dict = None
script: Optional[str] = None
@staticmethod @staticmethod
def from_tuple(script_tuple: tuple) -> Script: def from_dict(script_dict: dict) -> Script:
return Script(script_tuple[0], script_tuple[1]) if script_dict is None:
return Script()
return Script(script_dict["script"])
def to_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
"id": self.id,
"script": self.script "script": self.script
} }

View File

@ -12,3 +12,6 @@ TARANT_TABLE_OUTPUT = "outputs"
TARANT_TABLE_SCRIPT = "scripts" TARANT_TABLE_SCRIPT = "scripts"
TARANT_TX_ID_SEARCH = "transaction_id" TARANT_TX_ID_SEARCH = "transaction_id"
TARANT_ID_SEARCH = "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"

View File

@ -8,10 +8,10 @@ import json
from uuid import uuid4 from uuid import uuid4
from hashlib import sha256 from hashlib import sha256
from operator import itemgetter 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 import query
from planetmint.backend.models.keys import Keys
from planetmint.backend.models.dbtransaction import DbTransaction from planetmint.backend.models.dbtransaction import DbTransaction
from planetmint.backend.tarantool.const import ( from planetmint.backend.tarantool.const import (
TARANT_TABLE_META_DATA, TARANT_TABLE_META_DATA,
@ -22,7 +22,7 @@ from planetmint.backend.tarantool.const import (
TARANT_TABLE_OUTPUT, TARANT_TABLE_OUTPUT,
TARANT_TABLE_SCRIPT, TARANT_TABLE_SCRIPT,
TARANT_TX_ID_SEARCH, 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.utils import module_dispatch_registrar
from planetmint.backend.models import Asset, Block, MetaData, Input, Script, Output 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) tx = get_transaction_space_by_id(connection, txid)
if tx is None: if tx is None:
continue 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) _transactions.append(tx)
return _transactions 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) @register_query(TarantoolDBConnection)
def get_outputs_by_tx_id(connection, tx_id: str) -> list[Output]: 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)) _outputs = connection.run(connection.space(TARANT_TABLE_OUTPUT).select(tx_id, index=TARANT_TX_ID_SEARCH))
_sorted_outputs = sorted(_outputs, key=itemgetter(8)) _sorted_outputs = sorted(_outputs, key=itemgetter(4))
return [Output.from_tuple(output) for output in _sorted_outputs] 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) @register_query(TarantoolDBConnection)
def get_transaction(connection, tx_id: str) -> DbTransaction: def get_transaction(connection, tx_id: str) -> DbTransaction:
return NotImplemented 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: def store_transaction_outputs(connection, output: Output, index: int) -> str:
# TODO: store public keys as well
output_id = uuid4().hex output_id = uuid4().hex
if output.condition.details.sub_conditions is None: connection.run(connection.space(TARANT_TABLE_OUTPUT).insert((
tmp_output = ( output_id,
output.tx_id, int(output.amount),
output.amount, output.public_keys,
output.condition.uri if output.condition else "", output.condition.to_dict(),
output.condition.details.type if output.condition.details else "", index,
output.condition.details.public_key if output.condition.details else "", output.transaction_id,
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)))
return output_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) @register_query(TarantoolDBConnection)
def store_transactions(connection, signed_transactions: list): def store_transactions(connection, signed_transactions: list):
for transaction in signed_transactions: for transaction in signed_transactions:
store_transaction(connection, transaction) store_transaction(connection, transaction)
[ [
store_transaction_inputs(connection, Input.from_dict(input, transaction["id"]), index) store_transaction_outputs(connection, Output.outputs_dict(output, 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
)
for index, output in enumerate(transaction[TARANT_TABLE_OUTPUT]) 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) @register_query(TarantoolDBConnection)
def store_transaction(connection, transaction): 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) 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) 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) @register_query(TarantoolDBConnection)
def get_asset(connection, asset_id: str) -> Asset: def get_asset(connection, asset_id: str) -> Asset:
_data = connection.run(connection.space(TARANT_TABLE_ASSETS).select(asset_id, index=TARANT_TX_ID_SEARCH)) _data = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset_id, index=TARANT_INDEX_TX_BY_ASSET_ID))
return Asset.from_tuple(_data[0]) return Asset.from_dict(_data[0])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_assets(connection, assets_ids: list) -> list[Asset]: def get_assets(connection, assets_ids: list) -> list[Asset]:
_returned_data = [] _returned_data = []
for _id in list(set(assets_ids)): 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: if len(res) is 0:
continue continue
_returned_data.append(res[0]) _returned_data.append(res[0])
sorted_assets = sorted(_returned_data, key=lambda k: k[1], reverse=False) sorted_assets = sorted(_returned_data, key=lambda k: k[1], reverse=False)
return [Asset.from_tuple(asset) for asset in sorted_assets] return [Asset.from_dict(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]
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str): def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str):
_inputs = connection.run( _inputs = connection.run(
connection.space(TARANT_TABLE_INPUT).select( connection.space(TARANT_TABLE_TRANSACTION).select(
[fullfil_transaction_id, str(fullfil_output_index)], index="spent_search" [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) 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) @register_query(TarantoolDBConnection)
def get_txids_filtered( 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 ): # TODO here is used 'OR' operator
actions = { actions = {
"CREATE": {"sets": ["CREATE", asset_ids], "index": "transaction_search"}, "CREATE": {"sets": ["CREATE", asset_ids], "index": "transaction_search"},
@ -555,7 +415,7 @@ def get_election(connection, election_id: str):
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_asset_tokens_for_public_key( 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 ): # FIXME Something can be wrong with this function ! (public_key) is not used # noqa: E501
# space = connection.space("keys") # space = connection.space("keys")
# _keys = space.select([public_key], index="keys_search") # _keys = space.select([public_key], index="keys_search")
@ -589,9 +449,3 @@ def get_latest_abci_chain(connection):
return None return None
_chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0] _chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0]
return {"chain_id": _chain[0], "height": _chain[1], "is_synced": _chain[2]} 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)

View File

@ -239,6 +239,9 @@ class Planetmint(object):
for txid in txids: for txid in txids:
yield self.get_transaction(txid) 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): def get_outputs_filtered(self, owner, spent=None):
"""Get a list of output links filtered on some criteria """Get a list of output links filtered on some criteria
@ -337,17 +340,15 @@ class Planetmint(object):
return blocks 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.""" """Validate a transaction against the current status of the database."""
transaction = tx
# CLEANUP: The conditional below checks for transaction in dict format. # CLEANUP: The conditional below checks for transaction in dict format.
# It would be better to only have a single format for the transaction # It would be better to only have a single format for the transaction
# throught the code base. # throught the code base.
if isinstance(transaction, dict): if isinstance(transaction, dict):
try: try:
transaction = Transaction.from_dict(tx, False) transaction = Transaction.from_dict(transaction, False)
except SchemaValidationError as e: except SchemaValidationError as e:
logger.warning("Invalid transaction schema: %s", e.__cause__.message) logger.warning("Invalid transaction schema: %s", e.__cause__.message)
return False return False
@ -371,6 +372,7 @@ class Planetmint(object):
for input_ in tx.inputs: for input_ in tx.inputs:
input_txid = input_.fulfills.txid input_txid = input_.fulfills.txid
input_tx = self.get_transaction(input_txid) input_tx = self.get_transaction(input_txid)
_output = self.get_outputs_by_tx_id(input_txid)
if input_tx is None: if input_tx is None:
for ctxn in current_transactions: for ctxn in current_transactions:
if ctxn.id == input_txid: if ctxn.id == input_txid:
@ -383,9 +385,13 @@ class Planetmint(object):
if spent: if spent:
raise DoubleSpend("input `{}` was already spent".format(input_txid)) 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_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 # Validate that all inputs are distinct
links = [i.fulfills.to_uri() for i in tx.inputs] links = [i.fulfills.to_uri() for i in tx.inputs]

View File

@ -208,63 +208,6 @@ def test_get_owned_ids(signed_create_tx, user_pk, db_conn):
assert founded[0]["transactions"].raw_transaction == tx_dict 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): def test_store_block(db_conn):
from planetmint.lib import Block from planetmint.lib import Block
from planetmint.backend.tarantool import query from planetmint.backend.tarantool import query