Merge branch 'refactor-backend' of github.com:planetmint/planetmint into refactor-backend

This commit is contained in:
Lorenz Herzberger 2022-12-06 10:45:55 +01:00
commit 5902c15c99
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
19 changed files with 342 additions and 215 deletions

View File

@ -4,7 +4,7 @@
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
from dataclasses import dataclass from dataclasses import dataclass
# NOTE: only here temporarily # NOTE: only here temporarily
from planetmint.backend.models import Asset, MetaData, Input from planetmint.backend.models import Asset, MetaData, Input
from planetmint.backend.models import Output from planetmint.backend.models import Output
@ -16,37 +16,41 @@ class Block:
app_hash: str = None app_hash: str = None
height: int = None height: int = None
@dataclass @dataclass
class Script: class Script:
id: str = None id: str = None
script = None script = None
@dataclass @dataclass
class UTXO: class UTXO:
id: str = None id: str = None
output_index: int = None output_index: int = None
utxo: dict = None utxo: dict = None
@dataclass @dataclass
class Transaction: class Transaction:
id: str = None id: str = None
assets: list[Asset] = None assets: list[Asset] = None
metadata: MetaData = None metadata: MetaData = None
version: str = None # TODO: make enum version: str = None # TODO: make enum
operation: str = None # TODO: make enum operation: str = None # TODO: make enum
inputs: list[Input] = None inputs: list[Input] = None
outputs: list[Output] = None outputs: list[Output] = None
script: str = None script: str = None
@dataclass @dataclass
class Validator: class Validator:
id: str = None id: str = None
height: int = None height: int = None
validators = None validators = None
@dataclass @dataclass
class ABCIChain: class ABCIChain:
height: str = None height: str = None
is_synced: bool = None is_synced: bool = None
chain_id: str = None chain_id: str = None

View File

@ -9,3 +9,5 @@ from .input import Input
from .metadata import MetaData from .metadata import MetaData
from .script import Script from .script import Script
from .output import Output from .output import Output
from .keys import Keys
from .dbtransaction import DbTransaction

View File

@ -7,12 +7,24 @@ from __future__ import annotations
import json import json
from dataclasses import dataclass from dataclasses import dataclass
@dataclass @dataclass
class Asset: class Asset:
id: str = "" id: str = ""
tx_id: str = "" tx_id: str = ""
data: str = "" data: dict = ""
@staticmethod @staticmethod
def from_tuple(asset_tuple: tuple) -> Asset: def from_tuple(asset_tuple: tuple) -> Asset:
return Asset(asset_tuple[2], asset_tuple[1], json.loads(asset_tuple[0])) return Asset(asset_tuple[2], asset_tuple[1], json.loads(asset_tuple[0])["data"])
def to_dict(self) -> dict:
return {
"id": self.id,
"tx_id": self.tx_id,
"data": self.data
}
@staticmethod
def list_to_dict(asset_list: list[Asset]) -> list[dict]:
return [asset.to_dict() for asset in asset_list]

View File

@ -0,0 +1,57 @@
# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from __future__ import annotations
from dataclasses import dataclass
from planetmint.backend.models import Asset, MetaData, Input, Output, Script
from planetmint.backend.models.keys import Keys
@dataclass
class DbTransaction:
id: str = ""
operation: str = ""
version: str = ""
raw_transaction: dict = dict
assets: list[Asset] = None
metadata: MetaData = None
inputs: list[Input] = None
outputs: list[Output] = None
keys: Keys = None
script: Script = None
@staticmethod
def from_dict(transaction: dict) -> DbTransaction:
return DbTransaction(
id=transaction["id"],
operation=transaction["operation"],
version=transaction["version"],
inputs=transaction["inputs"],
raw_transaction=transaction["transaction"],
)
@staticmethod
def from_tuple(transaction: tuple) -> DbTransaction:
return DbTransaction(
id=transaction[0],
operation=transaction[1],
version=transaction[2],
raw_transaction=transaction[3],
)
def to_dict(self) -> dict:
return {
"id": self.id,
"operation": self.operation,
"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,
}

View File

@ -5,7 +5,8 @@
from dataclasses import dataclass from dataclasses import dataclass
@dataclass @dataclass
class Fulfills: class Fulfills:
transaction_id: str = "" transaction_id: str = ""
output_index: int = 0 output_index: int = 0

View File

@ -9,25 +9,23 @@ from typing import Optional
from .fulfills import Fulfills from .fulfills import Fulfills
@dataclass @dataclass
class Input: class Input:
tx_id: str = "" tx_id: str = ""
fulfills: Optional[Fulfills] = None fulfills: Optional[Fulfills] = None
owners_before: list[str] = field(default_factory=list) owners_before: list[str] = field(default_factory=list)
fulfillment: str = "" fulfillment: str = ""
@staticmethod @staticmethod
def from_dict(input_dict: dict, tx_id: str = "") -> Input: def from_dict(input_dict: dict, tx_id: str = "") -> Input:
fulfills = None fulfills = None
if input_dict["fulfills"]: if input_dict["fulfills"]:
fulfills = Fulfills( fulfills = Fulfills(input_dict["fulfills"]["transaction_id"], input_dict["fulfills"]["output_index"])
input_dict["fulfills"]["transaction_id"],
input_dict["fulfills"]["output_index"]
)
return Input(tx_id, fulfills, input_dict["owners_before"], input_dict["fulfillment"]) return Input(tx_id, fulfills, input_dict["owners_before"], input_dict["fulfillment"])
@staticmethod @staticmethod
def from_tuple(input_tuple: tuple) -> Input: def from_tuple(input_tuple: tuple) -> Input:
tx_id = input_tuple[0] tx_id = input_tuple[0]
@ -39,17 +37,20 @@ class Input:
if fulfills_tx_id: if fulfills_tx_id:
# TODO: the output_index should be an unsigned int # TODO: the output_index should be an unsigned int
fulfills = Fulfills(fulfills_tx_id, int(input_tuple[4])) fulfills = Fulfills(fulfills_tx_id, int(input_tuple[4]))
return Input(tx_id, fulfills, owners_before, fulfillment) return Input(tx_id, fulfills, owners_before, fulfillment)
def to_input_dict(self) -> dict: def to_dict(self) -> dict:
fulfills = { fulfills = (
"transaction_id": self.fulfills.transaction_id, {"transaction_id": self.fulfills.transaction_id, "output_index": self.fulfills.output_index}
"output_index": self.fulfills.output_index if self.fulfills
} if self.fulfills else None else None
)
return {
"fulfills": fulfills, return {"fulfills": fulfills, "fulfillment": self.fulfillment, "owners_before": self.owners_before}
"fulfillment": self.fulfillment,
"owners_before": self.owners_before @staticmethod
} def list_to_dict(input_list: list[Input]) -> list[dict]:
return [input.to_dict() for input in input_list]

View File

@ -7,6 +7,7 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Optional from typing import Optional
@dataclass @dataclass
class Keys: class Keys:
tx_id: str = "" tx_id: str = ""
@ -20,7 +21,6 @@ class Keys:
public_keys=output["public_keys"], public_keys=output["public_keys"],
) )
@staticmethod @staticmethod
def from_tuple(output: tuple) -> Keys: def from_tuple(output: tuple) -> Keys:
return Keys( return Keys(

View File

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

View File

@ -37,8 +37,9 @@ class Condition:
@dataclass @dataclass
class Output: class Output:
id: str = ""
tx_id: str = "" tx_id: str = ""
amount: int = 0 amount: str = '0'
public_keys: List[str] = field(default_factory=list) public_keys: List[str] = field(default_factory=list)
condition: Condition = field(default_factory=Condition) condition: Condition = field(default_factory=Condition)
@ -54,6 +55,7 @@ class Output:
@staticmethod @staticmethod
def from_tuple(output: tuple) -> Output: def from_tuple(output: tuple) -> Output:
return Output( return Output(
id=output[5],
tx_id=output[0], tx_id=output[0],
amount=output[1], amount=output[1],
condition=Condition( condition=Condition(
@ -67,7 +69,7 @@ class Output:
), ),
) )
def to_output_dict(self) -> dict: def to_dict(self) -> dict:
return { return {
"id": self.tx_id, "id": self.tx_id,
"amount": self.amount, "amount": self.amount,
@ -83,6 +85,10 @@ class Output:
}, },
} }
@staticmethod
def list_to_dict(output_list: list[Output]) -> list[dict]:
return [output.to_dict() for output in output_list]
def output_with_public_key(output, tx_id) -> Output: def output_with_public_key(output, tx_id) -> Output:
return Output( return Output(
@ -114,13 +120,8 @@ def output_with_sub_conditions(output, tx_id) -> Output:
type=sub_condition["type"], type=sub_condition["type"],
public_key=sub_condition["public_key"], public_key=sub_condition["public_key"],
) )
for sub_condition in output["condition"]["details"][ for sub_condition in output["condition"]["details"]["subconditions"]
"subconditions"
]
], ],
), ),
), ),
) )

View File

@ -7,13 +7,18 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
@dataclass @dataclass
class Script: class Script:
id: str = "" id: str = ""
script: Optional[str] = None script: Optional[str] = None
@staticmethod @staticmethod
def from_tuple(script_tuple: tuple) -> Script: def from_tuple(script_tuple: tuple) -> Script:
return Script(script_tuple[0], script_tuple[1]) return Script(script_tuple[0], script_tuple[1])
def to_dict(self) -> dict:
return {
"id": self.id,
"script": self.script
}

View File

@ -1,38 +0,0 @@
# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Transaction:
id: str = ""
operation: str = ""
version: str = ""
@staticmethod
def from_dict(transaction: dict) -> Transaction:
return Transaction(
id=transaction["id"],
operation=transaction["operation"],
version=transaction["version"],
)
@staticmethod
def from_tuple(transaction: tuple) -> Transaction:
return Transaction(
id=transaction[0],
operation=transaction[1],
version=transaction[2],
)
def to_dict(self) -> dict:
return {
"id": self.id,
"operation": self.operation,
"version": self.version,
}

View File

@ -6,9 +6,13 @@
"""Query interfaces for backends.""" """Query interfaces for backends."""
from functools import singledispatch from functools import singledispatch
from planetmint.backend.models import Asset, MetaData, Output, Input, Script
from planetmint.backend.exceptions import OperationError from planetmint.backend.exceptions import OperationError
from planetmint.backend.interfaces import Asset, Block, MetaData, Input, Script, Output, Transaction
from planetmint.backend.models.keys import Keys from planetmint.backend.models.keys import Keys
from planetmint.backend.interfaces import Block
from planetmint.backend.models.dbtransaction import DbTransaction
@singledispatch @singledispatch
@ -59,6 +63,7 @@ def store_transactions(connection, signed_transactions):
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def store_transaction(connection, transaction): def store_transaction(connection, transaction):
"""Store a single transaction.""" """Store a single transaction."""
@ -67,14 +72,27 @@ def store_transaction(connection, transaction):
@singledispatch @singledispatch
def get_transaction(conn, transaction_id): def get_transaction_space_by_id(connection, transaction_id):
"""Get a transaction from the database.""" """Get the transaction space by transaction id."""
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def get_transactions(connection, transactions_ids) -> list[Transaction]: def get_transaction_single(connection, transaction_id) -> DbTransaction:
"""Get a single transaction by id."""
raise NotImplementedError
@singledispatch
def get_transaction(connection, transaction_id):
"""Get a transaction by id."""
raise NotImplementedError
@singledispatch
def get_transactions(connection, transactions_ids) -> list[DbTransaction]:
"""Get a transaction from the transactions table. """Get a transaction from the transactions table.
Args: Args:
@ -101,6 +119,20 @@ def get_asset(connection, asset_id) -> Asset:
raise NotImplementedError raise NotImplementedError
@singledispatch
def get_assets_by_tx_id(connection, tx_id: str) -> list[Asset]:
"""Get assets by transaction id.
Args:
tx_id (str): the id of the transaction.
Returns:
The result of the operation.
"""
raise NotImplementedError
@singledispatch @singledispatch
def get_spent(connection, transaction_id, condition_id): def get_spent(connection, transaction_id, condition_id):
"""Check if a `txid` was already used as an input. """Check if a `txid` was already used as an input.
@ -175,6 +207,7 @@ def get_block_with_transaction(connection, txid):
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def get_metadata_by_tx_id(connection, transaction_id: str) -> MetaData: def get_metadata_by_tx_id(connection, transaction_id: str) -> MetaData:
"""Get metadata from the metadata table containing `transaction_id`. """Get metadata from the metadata table containing `transaction_id`.
@ -188,6 +221,7 @@ def get_metadata_by_tx_id(connection, transaction_id: str) -> MetaData:
""" """
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def store_transaction_outputs_and_keys(connection, output: Output, index: int): def store_transaction_outputs_and_keys(connection, output: Output, index: int):
"""Store the transaction outputs. """Store the transaction outputs.
@ -209,6 +243,7 @@ def store_transaction_outputs(connection, output: Output, index: int):
""" """
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def store_transaction_keys(connection, keys: [Keys], output_id: str, index: int): def store_transaction_keys(connection, keys: [Keys], output_id: str, index: int):
"""Store the transaction keys. """Store the transaction keys.
@ -480,21 +515,25 @@ def get_latest_abci_chain(conn):
""" """
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def get_inputs_by_tx_id(connection, tx_id) -> list[Input]: def get_inputs_by_tx_id(connection, tx_id) -> list[Input]:
"""Retrieve inputs for a transaction by its id""" """Retrieve inputs for a transaction by its id"""
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def store_transaction_inputs(connection, inputs: list[Input]): def store_transaction_inputs(connection, inputs: list[Input]):
"""Store inputs for a transaction""" """Store inputs for a transaction"""
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def _group_transaction_by_ids(txids: list, connection): def _group_transaction_by_ids(txids: list, connection):
"""Returns the transactions object (JSON TYPE), from list of ids.""" """Returns the transactions object (JSON TYPE), from list of ids."""
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def get_script_by_tx_id(connection, tx_id: str) -> Script: def get_script_by_tx_id(connection, tx_id: str) -> Script:
"""Retrieve script for a transaction by its id""" """Retrieve script for a transaction by its id"""
@ -506,7 +545,8 @@ def get_outputs_by_tx_id(connection, tx_id: str) -> list[Output]:
"""Retrieve outputs for a transaction by its id""" """Retrieve outputs for a transaction by its id"""
raise NotImplementedError raise NotImplementedError
@singledispatch @singledispatch
def get_keys_by_tx_id(connection, tx_id: str) -> list[Keys]: def get_keys_by_tx_id(connection, tx_id: str) -> list[Keys]:
"""Retrieve keys for a transaction by its id""" """Retrieve keys for a transaction by its id"""
raise NotImplementedError raise NotImplementedError

View File

@ -11,10 +11,18 @@ from operator import itemgetter
from tarantool.error import DatabaseError from tarantool.error import DatabaseError
from planetmint.backend import query from planetmint.backend import query
from planetmint.backend.models.keys import Keys from planetmint.backend.models.keys import Keys
from planetmint.backend.models.transaction import Transaction from planetmint.backend.models.dbtransaction import DbTransaction
from planetmint.backend.tarantool.const import TARANT_TABLE_META_DATA, TARANT_TABLE_ASSETS, TARANT_TABLE_KEYS, \ from planetmint.backend.tarantool.const import (
TARANT_TABLE_TRANSACTION, TARANT_TABLE_INPUT, TARANT_TABLE_OUTPUT, TARANT_TABLE_SCRIPT, TARANT_TX_ID_SEARCH, \ TARANT_TABLE_META_DATA,
TARANT_ID_SEARCH TARANT_TABLE_ASSETS,
TARANT_TABLE_KEYS,
TARANT_TABLE_TRANSACTION,
TARANT_TABLE_INPUT,
TARANT_TABLE_OUTPUT,
TARANT_TABLE_SCRIPT,
TARANT_TX_ID_SEARCH,
TARANT_ID_SEARCH,
)
from planetmint.backend.utils import module_dispatch_registrar from planetmint.backend.utils import module_dispatch_registrar
from planetmint.backend.models import Asset, MetaData, Input, Script, Output from planetmint.backend.models import Asset, MetaData, Input, Script, Output
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.connection import TarantoolDBConnection
@ -23,35 +31,29 @@ register_query = module_dispatch_registrar(query)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def _group_transaction_by_ids(connection, txids: list): def _group_transaction_by_ids(connection, txids: list) -> list[DbTransaction]:
_transactions = [] _transactions = []
for txid in txids: for txid in txids:
_txobject = connection.run(connection.space(TARANT_TABLE_TRANSACTION).get(txid, index=TARANT_ID_SEARCH)) tx = get_transaction_space_by_id(connection, txid)
if tx is None:
if _txobject is None:
continue continue
_txinputs = get_inputs_by_tx_id(connection, txid) tx.inputs = get_inputs_by_tx_id(connection, txid)
_txoutputs = get_outputs_by_tx_id(connection, txid) _output = get_outputs_by_tx_id(connection, txid)
_txkeys = get_keys_by_tx_id(connection, txid) _keys = get_keys_by_tx_id(connection, txid)
_txassets = get_assets(connection, [txid]) tx.outputs = [_enricht_output_with_public_keys(_keys, output) for output in _output]
_txmeta = get_metadata_by_tx_id(connection, txid) tx.assets = get_assets_by_tx_id(connection, txid)
_txscript = get_script_by_tx_id(connection, txid) tx.metadata = get_metadata_by_tx_id(connection, txid)
tx.script = get_script_by_tx_id(connection, txid)
_transaction = get_transaction(connection, txid) _transactions.append(tx)
_transaction[TARANT_TABLE_TRANSACTION] = [tx.to_dict for tx in _transactions]
_transaction[TARANT_TABLE_INPUT] + [input.to_input_dict() for input in _txinputs]
_transaction[TARANT_TABLE_OUTPUT] = [output.to_output_dict() for output in _txoutputs]
_transaction[TARANT_TABLE_KEYS] = [key.to_dict() for key in _txkeys]
_transaction["assets"] = [asset.data for asset in _txassets]
_transaction["metadata"] = _txmeta.metadata
if _txscript.script:
_transaction[TARANT_TABLE_SCRIPT] = _txscript.script
_transactions.append(_transaction)
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) @register_query(TarantoolDBConnection)
def get_inputs_by_tx_id(connection, tx_id: str) -> list[Input]: 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)) _inputs = connection.run(connection.space(TARANT_TABLE_INPUT).select(tx_id, index=TARANT_ID_SEARCH))
@ -73,18 +75,26 @@ def get_keys_by_tx_id(connection, tx_id: str) -> list[Keys]:
return [Keys.from_tuple(key) for key in _sorted_keys] 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) @register_query(TarantoolDBConnection)
def store_transaction_inputs(connection, input: Input, index: int): def store_transaction_inputs(connection, input: Input, index: int):
connection.run(connection.space(TARANT_TABLE_INPUT).insert(( connection.run(
input.tx_id, connection.space(TARANT_TABLE_INPUT).insert(
input.fulfillment, (
input.owners_before, input.tx_id,
input.fulfills.transaction_id if input.fulfills else "", input.fulfillment,
# TODO: the output_index should be an unsigned int input.owners_before,
str(input.fulfills.output_index) if input.fulfills else "", input.fulfills.transaction_id if input.fulfills else "",
uuid4().hex, # TODO: the output_index should be an unsigned int
index str(input.fulfills.output_index) if input.fulfills else "",
))) uuid4().hex,
index,
)
)
)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@ -120,34 +130,32 @@ def store_transaction_outputs(connection, output: Output, index: int) -> str:
index, index,
) )
connection.run(connection.space(TARANT_TABLE_OUTPUT).insert(( connection.run(connection.space(TARANT_TABLE_OUTPUT).insert((tmp_output)))
tmp_output
)))
return output_id return output_id
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def store_transaction_keys(connection, keys: Keys, output_id: str, index: int): def store_transaction_keys(connection, keys: Keys, output_id: str, index: int):
for key in keys.public_keys: for key in keys.public_keys:
connection.run(connection.space(TARANT_TABLE_KEYS).insert(( connection.run(connection.space(TARANT_TABLE_KEYS).insert((uuid4().hex, keys.tx_id, output_id, key, index)))
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_inputs(connection, Input.from_dict(input, transaction["id"]), index) for [
index, input in enumerate(transaction[TARANT_TABLE_INPUT])] 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) [
for index, output in store_transaction_outputs_and_keys(
enumerate(transaction[TARANT_TABLE_OUTPUT])] connection, Output.outputs_and_keys_dict(output, transaction["id"]), index
)
for index, output in enumerate(transaction[TARANT_TABLE_OUTPUT])
]
store_metadatas(connection, [MetaData(transaction["id"], transaction["metadata"])]) store_metadatas(connection, [MetaData(transaction["id"], transaction["metadata"])])
@ -160,39 +168,39 @@ def store_transactions(connection, signed_transactions: list):
if TARANT_TABLE_SCRIPT in transaction: if TARANT_TABLE_SCRIPT in transaction:
connection.run( connection.run(
connection.space(TARANT_TABLE_SCRIPT).insert((transaction["id"], transaction[TARANT_TABLE_SCRIPT])), connection.space(TARANT_TABLE_SCRIPT).insert((transaction["id"], transaction[TARANT_TABLE_SCRIPT])),
only_data=False) only_data=False,
)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def store_transaction(connection, transaction): def store_transaction(connection, transaction):
tx = Transaction(id=transaction["id"], operation=transaction["operation"], version=transaction["version"]) tx = (transaction["id"], transaction["operation"], transaction["version"], [transaction])
connection.run(connection.space(TARANT_TABLE_TRANSACTION).insert( connection.run(connection.space(TARANT_TABLE_TRANSACTION).insert(tx), only_data=False)
tx.id,
tx.operation,
tx.version,
),
only_data=False)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_transaction(connection, transaction_id: str) -> Transaction: def get_transaction_space_by_id(connection, transaction_id):
return Transaction.from_tuple( txs = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(transaction_id, index=TARANT_ID_SEARCH))
connection.run(connection.space(TARANT_TABLE_TRANSACTION).get(transaction_id, index=TARANT_ID_SEARCH))) if len(txs) == 0:
return None
return DbTransaction.from_tuple(txs[0])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_transactions(connection, transactions_ids: list) -> list[Transaction]: def get_transaction_single(connection, transaction_id) -> DbTransaction:
_transactions = _group_transaction_by_ids(txids=transactions_ids, connection=connection) return _group_transaction_by_ids(txids=[transaction_id], connection=connection)[0]
return [Transaction.from_tuple(_transaction) for _transaction in _transactions]
@register_query(TarantoolDBConnection)
def get_transactions(connection, transactions_ids: list) -> list[DbTransaction]:
return _group_transaction_by_ids(txids=transactions_ids, connection=connection)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def store_metadatas(connection, metadata: list[MetaData]): def store_metadatas(connection, metadata: list[MetaData]):
for meta in metadata: for meta in metadata:
connection.run( connection.run(
connection.space(TARANT_TABLE_META_DATA).insert( connection.space(TARANT_TABLE_META_DATA).insert((meta.id, json.dumps(meta.metadata))) # noqa: E713
(meta.id, json.dumps(meta.metadata))
) # noqa: E713
) )
@ -242,20 +250,36 @@ 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_ASSETS).select(_id, index=TARANT_TX_ID_SEARCH))
if len(res) is 0:
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_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]
@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([fullfil_transaction_id, str(fullfil_output_index)], connection.space(TARANT_TABLE_INPUT).select(
index="spent_search") [fullfil_transaction_id, str(fullfil_output_index)], index="spent_search"
)
) )
_transactions = _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)
return _transactions
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@ -287,7 +311,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"},
@ -313,7 +337,9 @@ def get_txids_filtered(
_transactions.extend(_tmp_transactions) _transactions.extend(_tmp_transactions)
else: else:
_tx_ids = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset_ids, index=TARANT_ID_SEARCH)) _tx_ids = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset_ids, index=TARANT_ID_SEARCH))
_assets_ids = connection.run(connection.space(TARANT_TABLE_ASSETS).select(asset_ids, index="only_asset_search")) _assets_ids = connection.run(
connection.space(TARANT_TABLE_ASSETS).select(asset_ids, index="only_asset_search")
)
return tuple(set([sublist[1] for sublist in _assets_ids] + [sublist[0] for sublist in _tx_ids])) return tuple(set([sublist[1] for sublist in _assets_ids] + [sublist[0] for sublist in _tx_ids]))
if last_tx: if last_tx:
@ -347,8 +373,7 @@ def get_owned_ids(connection, owner: str):
if _keys is None or len(_keys) == 0: if _keys is None or len(_keys) == 0:
return [] return []
_transactionids = list(set([key[1] for key in _keys])) _transactionids = list(set([key[1] for key in _keys]))
_transactions = _group_transaction_by_ids(txids=_transactionids, connection=connection) return _group_transaction_by_ids(txids=_transactionids, connection=connection)
return _transactions
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@ -392,20 +417,27 @@ def delete_transactions(connection, txn_ids: list):
for _id in txn_ids: for _id in txn_ids:
connection.run(connection.space(TARANT_TABLE_TRANSACTION).delete(_id), only_data=False) connection.run(connection.space(TARANT_TABLE_TRANSACTION).delete(_id), only_data=False)
for _id in txn_ids: for _id in txn_ids:
_inputs = connection.run(connection.space(TARANT_TABLE_INPUT).select(_id, index=TARANT_ID_SEARCH), _inputs = connection.run(
only_data=False) connection.space(TARANT_TABLE_INPUT).select(_id, index=TARANT_ID_SEARCH), only_data=False
_outputs = connection.run(connection.space(TARANT_TABLE_OUTPUT).select(_id, index=TARANT_ID_SEARCH), )
only_data=False) _outputs = connection.run(
_keys = connection.run(connection.space(TARANT_TABLE_KEYS).select(_id, index=TARANT_TX_ID_SEARCH), connection.space(TARANT_TABLE_OUTPUT).select(_id, index=TARANT_ID_SEARCH), only_data=False
only_data=False) )
_keys = connection.run(
connection.space(TARANT_TABLE_KEYS).select(_id, index=TARANT_TX_ID_SEARCH), only_data=False
)
for _kID in _keys: for _kID in _keys:
connection.run(connection.space(TARANT_TABLE_KEYS).delete(_kID[0], index=TARANT_ID_SEARCH), only_data=False) connection.run(
connection.space(TARANT_TABLE_KEYS).delete(_kID[0], index=TARANT_ID_SEARCH), only_data=False
)
for _inpID in _inputs: for _inpID in _inputs:
connection.run(connection.space(TARANT_TABLE_INPUT).delete(_inpID[5], index="delete_search"), connection.run(
only_data=False) connection.space(TARANT_TABLE_INPUT).delete(_inpID[5], index="delete_search"), only_data=False
)
for _outpID in _outputs: for _outpID in _outputs:
connection.run(connection.space(TARANT_TABLE_OUTPUT).delete(_outpID[5], index="unique_search"), connection.run(
only_data=False) connection.space(TARANT_TABLE_OUTPUT).delete(_outpID[5], index="unique_search"), only_data=False
)
for _id in txn_ids: for _id in txn_ids:
connection.run(connection.space(TARANT_TABLE_META_DATA).delete(_id, index=TARANT_ID_SEARCH), only_data=False) connection.run(connection.space(TARANT_TABLE_META_DATA).delete(_id, index=TARANT_ID_SEARCH), only_data=False)
@ -541,15 +573,14 @@ 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")
_transactions = connection.run(connection.space(TARANT_TABLE_ASSETS).select([asset_id], index="assetid_search")) _transactions = connection.run(connection.space(TARANT_TABLE_ASSETS).select([asset_id], index="assetid_search"))
# _transactions = _transactions # _transactions = _transactions
# _keys = _keys.data # _keys = _keys.data
_grouped_transactions = _group_transaction_by_ids(connection=connection, txids=[_tx[1] for _tx in _transactions]) return _group_transaction_by_ids(connection=connection, txids=[_tx[1] for _tx in _transactions])
return _grouped_transactions
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)

View File

@ -1,7 +1,14 @@
from transactions.common.memoize import HDict from transactions.common.memoize import HDict
from planetmint.backend.tarantool.const import TARANT_TABLE_META_DATA, TARANT_TABLE_ASSETS, TARANT_TABLE_KEYS, \ from planetmint.backend.tarantool.const import (
TARANT_TABLE_TRANSACTION, TARANT_TABLE_INPUT, TARANT_TABLE_OUTPUT, TARANT_TABLE_SCRIPT TARANT_TABLE_META_DATA,
TARANT_TABLE_ASSETS,
TARANT_TABLE_KEYS,
TARANT_TABLE_TRANSACTION,
TARANT_TABLE_INPUT,
TARANT_TABLE_OUTPUT,
TARANT_TABLE_SCRIPT,
)
def get_items(_list): def get_items(_list):

View File

@ -18,7 +18,7 @@ class FastQuery:
"""Get outputs for a public key""" """Get outputs for a public key"""
txs = list(query.get_owned_ids(self.connection, public_key)) txs = list(query.get_owned_ids(self.connection, public_key))
return [ return [
TransactionLink(tx["id"], index) TransactionLink(tx["transactions"].id, index)
for tx in txs for tx in txs
for index, output in enumerate(tx["outputs"]) for index, output in enumerate(tx["outputs"])
if condition_details_has_owner(output["condition"]["details"], public_key) if condition_details_has_owner(output["condition"]["details"], public_key)

View File

@ -8,11 +8,8 @@ MongoDB.
""" """
import logging import logging
from collections import namedtuple
from uuid import uuid4
from planetmint.backend.connection import Connection from planetmint.backend.connection import Connection
from hashlib import sha3_256
import json import json
import rapidjson import rapidjson
import requests import requests
@ -40,6 +37,7 @@ from transactions.common.transaction import VALIDATOR_ELECTION, CHAIN_MIGRATION_
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC
from transactions.types.elections.election import Election from transactions.types.elections.election import Election
from transactions.types.elections.validator_utils import election_id_to_public_key from transactions.types.elections.validator_utils import election_id_to_public_key
from planetmint.config import Config from planetmint.config import Config
from planetmint import backend, config_utils, fastquery from planetmint import backend, config_utils, fastquery
from planetmint.tendermint_utils import ( from planetmint.tendermint_utils import (
@ -226,12 +224,11 @@ class Planetmint(object):
return backend.query.delete_unspent_outputs(self.connection, *unspent_outputs) return backend.query.delete_unspent_outputs(self.connection, *unspent_outputs)
def is_committed(self, transaction_id): def is_committed(self, transaction_id):
transaction = backend.query.get_transaction(self.connection, transaction_id) transaction = backend.query.get_transaction_space_by_id(self.connection, transaction_id)
return bool(transaction) return bool(transaction)
def get_transaction(self, transaction_id): def get_transaction(self, transaction_id):
transaction = backend.query.get_transaction(self.connection, transaction_id) return backend.query.get_transaction_single(self.connection, transaction_id)
return Transaction.from_dict(transaction, False)
def get_transactions(self, txn_ids): def get_transactions(self, txn_ids):
return backend.query.get_transactions(self.connection, txn_ids) return backend.query.get_transactions(self.connection, txn_ids)
@ -265,7 +262,6 @@ class Planetmint(object):
def get_spent(self, txid, output, current_transactions=[]): def get_spent(self, txid, output, current_transactions=[]):
transactions = backend.query.get_spent(self.connection, txid, output) transactions = backend.query.get_spent(self.connection, txid, output)
transactions = list(transactions) if transactions else []
if len(transactions) > 1: if len(transactions) > 1:
raise core_exceptions.CriticalDoubleSpend( raise core_exceptions.CriticalDoubleSpend(
"`{}` was spent more than once. There is a problem" " with the chain".format(txid) "`{}` was spent more than once. There is a problem" " with the chain".format(txid)
@ -281,10 +277,10 @@ class Planetmint(object):
if len(transactions) + len(current_spent_transactions) > 1: if len(transactions) + len(current_spent_transactions) > 1:
raise DoubleSpend('tx "{}" spends inputs twice'.format(txid)) raise DoubleSpend('tx "{}" spends inputs twice'.format(txid))
elif transactions: elif transactions:
transaction = backend.query.get_transaction(self.connection, transactions[0]["id"]) tx_id = transactions[0]["transactions"].id
assets = backend.query.get_assets(self.connection, [transaction["id"]]) tx = backend.query.get_transaction_single(self.connection, tx_id)
transaction["assets"] = [asset.data for asset in assets] tx.assets = backend.query.get_assets_by_tx_id(self.connection, tx_id)
transaction = Transaction.from_dict(transaction, False) transaction = tx.to_dict()
elif current_spent_transactions: elif current_spent_transactions:
transaction = current_spent_transactions[0] transaction = current_spent_transactions[0]
@ -697,7 +693,7 @@ class Planetmint(object):
return recipients return recipients
def show_election_status(self, transaction): def show_election_status(self, transaction):
data = transaction.assets[0]["data"] data = transaction.assets[0]
if "public_key" in data.keys(): if "public_key" in data.keys():
data["public_key"] = public_key_to_base64(data["public_key"]["value"]) data["public_key"] = public_key_to_base64(data["public_key"]["value"])
response = "" response = ""
@ -746,23 +742,23 @@ class Planetmint(object):
# validators and their voting power in the network # validators and their voting power in the network
return current_topology == voters return current_topology == voters
def count_votes(self, election_pk, transactions, getter=getattr): def count_votes(self, election_pk, transactions):
votes = 0 votes = 0
for txn in transactions: for txn in transactions:
if getter(txn, "operation") == Vote.OPERATION: if txn.operation == Vote.OPERATION:
for output in getter(txn, "outputs"): for output in txn.outputs:
# NOTE: We enforce that a valid vote to election id will have only # NOTE: We enforce that a valid vote to election id will have only
# election_pk in the output public keys, including any other public key # election_pk in the output public keys, including any other public key
# along with election_pk will lead to vote being not considered valid. # along with election_pk will lead to vote being not considered valid.
if len(getter(output, "public_keys")) == 1 and [election_pk] == getter(output, "public_keys"): if len(output.public_keys) == 1 and [election_pk] == output.public_keys:
votes = votes + int(getter(output, "amount")) votes = votes + output.amount
return votes return votes
def get_commited_votes(self, transaction, election_pk=None): # TODO: move somewhere else def get_commited_votes(self, transaction, election_pk=None): # TODO: move somewhere else
if election_pk is None: if election_pk is None:
election_pk = election_id_to_public_key(transaction.id) election_pk = election_id_to_public_key(transaction.id)
txns = list(backend.query.get_asset_tokens_for_public_key(self.connection, transaction.id, election_pk)) txns = backend.query.get_asset_tokens_for_public_key(self.connection, transaction.id, election_pk)
return self.count_votes(election_pk, txns, dict.get) return self.count_votes(election_pk, txns)
def _get_initiated_elections(self, height, txns): # TODO: move somewhere else def _get_initiated_elections(self, height, txns): # TODO: move somewhere else
elections = [] elections = []
@ -855,7 +851,7 @@ class Planetmint(object):
votes_committed = self.get_commited_votes(transaction, election_pk) votes_committed = self.get_commited_votes(transaction, election_pk)
votes_current = self.count_votes(election_pk, current_votes) votes_current = self.count_votes(election_pk, current_votes)
total_votes = sum(output.amount for output in transaction.outputs) total_votes = sum(int(output.amount) for output in transaction.outputs)
if (votes_committed < (2 / 3) * total_votes) and (votes_committed + votes_current >= (2 / 3) * total_votes): if (votes_committed < (2 / 3) * total_votes) and (votes_committed + votes_current >= (2 / 3) * total_votes):
return True return True
@ -913,7 +909,7 @@ class Planetmint(object):
if election.operation == CHAIN_MIGRATION_ELECTION: if election.operation == CHAIN_MIGRATION_ELECTION:
self.migrate_abci_chain() self.migrate_abci_chain()
if election.operation == VALIDATOR_ELECTION: if election.operation == VALIDATOR_ELECTION:
validator_updates = [election.assets[0]["data"]] validator_updates = [election.assets[0].data]
curr_validator_set = self.get_validators(new_height) curr_validator_set = self.get_validators(new_height)
updated_validator_set = new_validator_set(curr_validator_set, validator_updates) updated_validator_set = new_validator_set(curr_validator_set, validator_updates)
@ -921,7 +917,7 @@ class Planetmint(object):
# TODO change to `new_height + 2` when upgrading to Tendermint 0.24.0. # TODO change to `new_height + 2` when upgrading to Tendermint 0.24.0.
self.store_validator_set(new_height + 1, updated_validator_set) self.store_validator_set(new_height + 1, updated_validator_set)
return encode_validator(election.assets[0]["data"]) return encode_validator(election.assets[0].data)
Block = namedtuple("Block", ("app_hash", "height", "transactions")) Block = namedtuple("Block", ("app_hash", "height", "transactions"))

View File

@ -49,7 +49,7 @@ def test_write_assets(db_conn):
Asset("2", "2", "2"), Asset("2", "2", "2"),
Asset("3", "3", "3"), Asset("3", "3", "3"),
# Duplicated id. Should not be written to the database # Duplicated id. Should not be written to the database
Asset("1", "1", "1") Asset("1", "1", "1"),
] ]
# write the assets # write the assets
@ -65,7 +65,7 @@ def test_write_assets(db_conn):
def test_get_assets(db_conn): def test_get_assets(db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool import query
assets = [ assets = [
Asset("1", "1"), Asset("1", "1"),
Asset("2", "2"), Asset("2", "2"),
@ -186,7 +186,7 @@ def test_get_metadata(db_conn):
metadata = [ metadata = [
MetaData("dd86682db39e4b424df0eec1413cfad65488fd48712097c5d865ca8e8e059b64", None), MetaData("dd86682db39e4b424df0eec1413cfad65488fd48712097c5d865ca8e8e059b64", None),
MetaData("55a2303e3bcd653e4b5bd7118d39c0e2d48ee2f18e22fbcf64e906439bdeb45d", {"key": "value"}) MetaData("55a2303e3bcd653e4b5bd7118d39c0e2d48ee2f18e22fbcf64e906439bdeb45d", {"key": "value"}),
] ]
# conn.db.metadata.insert_many(deepcopy(metadata), ordered=False) # conn.db.metadata.insert_many(deepcopy(metadata), ordered=False)
@ -204,8 +204,8 @@ def test_get_owned_ids(signed_create_tx, user_pk, db_conn):
query.store_transactions(connection=db_conn, signed_transactions=[signed_create_tx.to_dict()]) query.store_transactions(connection=db_conn, signed_transactions=[signed_create_tx.to_dict()])
txns = list(query.get_owned_ids(connection=db_conn, owner=user_pk)) txns = list(query.get_owned_ids(connection=db_conn, owner=user_pk))
tx_dict = signed_create_tx.to_dict() tx_dict = signed_create_tx.to_dict()
founded = [tx for tx in txns if tx["id"] == tx_dict["id"]] founded = [tx for tx in txns if tx["transactions"].id == tx_dict["id"]]
assert founded[0] == tx_dict assert founded[0]["transactions"].raw_transaction == tx_dict
def test_get_spending_transactions(user_pk, user_sk, db_conn): def test_get_spending_transactions(user_pk, user_sk, db_conn):
@ -225,7 +225,8 @@ def test_get_spending_transactions(user_pk, user_sk, db_conn):
txns = list(query.get_spending_transactions(connection=db_conn, inputs=links)) txns = list(query.get_spending_transactions(connection=db_conn, inputs=links))
# tx3 not a member because input 1 not asked for # tx3 not a member because input 1 not asked for
assert txns == [tx2.to_dict(), tx4.to_dict()] 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): def test_get_spending_transactions_multiple_inputs(db_conn):
@ -261,7 +262,7 @@ def test_get_spending_transactions_multiple_inputs(db_conn):
txns = list(query.get_spending_transactions(connection=db_conn, inputs=[li])) txns = list(query.get_spending_transactions(connection=db_conn, inputs=[li]))
assert len(txns) == num assert len(txns) == num
if len(txns): if len(txns):
assert [tx["id"] for tx in txns] == match assert [tx["transactions"].id for tx in txns] == match
def test_store_block(db_conn): def test_store_block(db_conn):

View File

@ -110,10 +110,10 @@ class TestBigchainApi(object):
before = tx.to_dict() before = tx.to_dict()
after = tx_from_db.to_dict() after = tx_from_db.to_dict()
assert before["assets"][0]["data"] == after["assets"][0]["data"] assert before["assets"][0] == after["transaction"]["assets"][0]
before.pop("asset", None) before.pop("asset", None)
after.pop("asset", None) after["transaction"].pop("asset", None)
assert before == after assert before == after["transaction"]
class TestTransactionValidation(object): class TestTransactionValidation(object):

View File

@ -67,8 +67,8 @@ def test_asset_is_separated_from_transaciton(b):
tx_dict = copy.deepcopy(tx.to_dict()) tx_dict = copy.deepcopy(tx.to_dict())
b.store_bulk_transactions([tx]) b.store_bulk_transactions([tx])
assert "asset" not in backend.query.get_transaction(b.connection, tx.id) assert "asset" not in backend.query.get_transaction_single(b.connection, tx.id)
assert backend.query.get_asset(b.connection, tx.id)["data"] == assets[0] assert backend.query.get_asset(b.connection, tx.id).data == assets[0]
assert b.get_transaction(tx.id).to_dict() == tx_dict assert b.get_transaction(tx.id).to_dict() == tx_dict
@ -479,7 +479,7 @@ def test_get_spent_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
inputs = tx1.to_inputs() inputs = tx1.to_inputs()
tx2 = Transfer.generate([inputs[1]], [([user2_pk], 2)], [tx1.id]).sign([user_sk]) tx2 = Transfer.generate([inputs[1]], [([user2_pk], 2)], [tx1.id]).sign([user_sk])
assert b.validate_transaction(tx2) assert b.validate_transaction(tx2)
b.store_bulk_transactions([tx2]) b.store_bulk_transactions([tx2])
tx3 = Transfer.generate([inputs[1]], [([bob.public_key], 2)], [tx1.id]).sign([user_sk]) tx3 = Transfer.generate([inputs[1]], [([bob.public_key], 2)], [tx1.id]).sign([user_sk])