Transaction Compose/Decompose for tarantool usages, move to backend.tarantool folder

This commit is contained in:
andrei 2022-04-01 12:00:41 +03:00
parent 7442ebe5be
commit 43fe80818b
5 changed files with 159 additions and 164 deletions

View File

@ -1,45 +0,0 @@
import tarantool
import os
from planetmint.backend.tarantool.utils import run
def init_tarantool():
if os.path.exists(os.path.join(os.getcwd(), 'tarantool', 'init.lua')) is not True:
path = os.getcwd()
run(["mkdir", "tarantool_snap"])
run(["ln", "-s", path + "/init.lua", "init.lua"], path + "/tarantool_snap")
run(["tarantool", "init.lua"], path + "/tarantool")
else:
raise Exception("There is a instance of tarantool already created in %s" + os.getcwd() + "/tarantool_snap")
def drop_tarantool():
if os.path.exists(os.path.join(os.getcwd(), 'tarantool', 'init.lua')) is not True:
path = os.getcwd()
run(["ln", "-s", path + "/drop_db.lua", "drop_db.lua"], path + "/tarantool_snap")
run(["tarantool", "drop_db.lua"])
else:
raise Exception("There is no tarantool spaces to drop")
class TarantoolDB:
def __init__(self, host: str, port: int, username: str, password: str):
self.db_connect = tarantool.connect(host=host, port=port, user=username, password=password)
self._spaces = {
"abci_chains": self.db_connect.space("abci_chains"),
"assets": self.db_connect.space("assets"),
"blocks": {"blocks": self.db_connect.space("blocks"), "blocks_tx": self.db_connect.space("blocks_tx")},
"elections": self.db_connect.space("elections"),
"meta_data": self.db_connect.space("meta_data"),
"pre_commits": self.db_connect.space("pre_commits"),
"validators": self.db_connect.space("validators"),
"transactions": {
"transactions": self.db_connect.space("transactions"),
"inputs": self.db_connect.space("inputs"),
"outputs": self.db_connect.space("outputs"),
"keys": self.db_connect.space("keys")
}
}
def get_space(self, spacename: str):
return self._spaces[spacename]

View File

@ -11,7 +11,7 @@ from operator import itemgetter
from planetmint.backend import query
from planetmint.backend.utils import module_dispatch_registrar
from planetmint.backend.tarantool.connection import TarantoolDB
from planetmint.common.transaction import TransactionPrepare
from planetmint.backend.tarantool.transaction.tools import TransactionCompose, TransactionDecompose
register_query = module_dispatch_registrar(query)
@ -38,15 +38,25 @@ def _group_transaction_by_ids(connection, txids: list):
_txinputs = sorted(_txinputs, key=itemgetter(6), reverse=False)
_txoutputs = sorted(_txoutputs, key=itemgetter(8), reverse=False)
result_map = {
"transaction": _txobject,
"inputs": _txinputs,
"outputs": _txoutputs,
"keys": _txkeys,
"assets": _txassets,
"metadata": _txmeta,
}
tx_compose = TransactionCompose()
_transaction = tx_compose.convert_to_dict(db_results=result_map)
_obj = {
"inputs": [
{
"owners_before": _in[2],
"fulfillment": _in[1],
"fulfills": {"transaction_id": _in[3], "output_index": int(_in[4])} if len(_in[3]) > 0 and len(
# TODO Now it is working because of data type cast to INTEGER for field "output_index"
_in[4]) > 0 else None,
"fulfillment": _in[1]
"owners_before": _in[2]
} for _in in _txinputs
],
"outputs": [],
@ -59,18 +69,18 @@ def _group_transaction_by_ids(connection, txids: list):
if _txoutputs[0][7] is None:
_obj["outputs"] = [
{
"public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]],
"amount": _out[1],
"condition": {"details": {"type": _out[3], "public_key": _out[4]}, "uri": _out[2]},
"amount": _out[1]
"public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]]
} for _out in _txoutputs
]
else:
_obj["outputs"] = [
{
"public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]],
"amount": _out[1],
"condition": {"uri": _out[2], "details": {"subconditions": _out[7]}, "type": _out[3],
"treshold": _out[6]}
"treshold": _out[6]},
"public_keys": [_key[3] for _key in _txkeys if _key[2] == _out[5]]
} for _out in _txoutputs
]
@ -78,10 +88,8 @@ def _group_transaction_by_ids(connection, txids: list):
_obj["asset"] = {
"id": _txobject[3]
}
elif len(_txassets) == 1:
_obj["asset"] = {
"data": _txassets[0][1]
}
elif len(_txassets) > 0:
_obj["asset"] = _txassets[0][1]
_obj["metadata"] = _txmeta[0][1] if len(_txmeta) == 1 else None
_transactions.append(_obj)
return _transactions
@ -97,7 +105,7 @@ def store_transactions(connection, signed_transactions: list):
assetsxspace = connection.space("assets")
for transaction in signed_transactions:
txprepare = TransactionPrepare(transaction)
txprepare = TransactionDecompose(transaction)
txtuples = txprepare.convert_to_tuple()
txspace.insert(txtuples["transactions"])
@ -154,9 +162,9 @@ def store_asset(connection, asset: dict, tx_id=None, is_data=False): # TODO con
space = connection.space("assets")
try:
if is_data and tx_id is not None:
space.insert((tx_id, asset["data"]))
space.insert((tx_id, asset))
else:
space.insert((str(asset["id"]), asset["data"]))
space.insert((str(asset["id"]), asset))
except: # TODO Add Raise For Duplicate
print("DUPLICATE ERROR")
@ -176,7 +184,7 @@ def get_asset(connection, asset_id: str):
space = connection.space("assets")
_data = space.select(asset_id, index="assetid_search")
_data = _data.data
return {"data": _data[0][1]} if len(_data) == 1 else []
return _data[0][1] if len(_data) == 1 else []
@register_query(TarantoolDB)

View File

@ -0,0 +1,134 @@
from secrets import token_hex
def _save_keys_order(dictionary):
if type(dictionary) is dict:
keys = list(dictionary.keys())
_map = {}
for key in keys:
_map[key] = _save_keys_order(dictionary=dictionary[key])
return _map
elif type(dictionary) is list:
dictionary = next(iter(dictionary), None)
if dictionary is not None and type(dictionary) is dict:
_map = {}
keys = list(dictionary.keys())
for key in keys:
_map[key] = _save_keys_order(dictionary=dictionary[key])
return _map
else:
return None
class TransactionDecompose:
def __init__(self, _transaction):
self._transaction = _transaction
self._tuple_transaction = {
"transactions": (),
"inputs": [],
"outputs": [],
"keys": [],
"metadata": (),
"asset": "",
"asset_data": (),
"is_data": False
}
self.if_key = lambda dct, key: False if not key in dct.keys() else dct[key]
def get_map(self, dictionary: dict = None):
return _save_keys_order(dictionary=dictionary) if dictionary is not None else _save_keys_order(
dictionary=self._transaction)
def __create_hash(self, n: int):
return token_hex(n)
def _metadata_check(self):
metadata = self._transaction.get("metadata")
self._tuple_transaction["metadata"] = (self._transaction["id"], metadata) if metadata is not None else ()
def __asset_check(self):
_asset = self._transaction.get("asset")
if _asset is None:
self._tuple_transaction["asset"] = ""
return
_id = self.if_key(dct=_asset, key="id")
if _id is not False:
self._tuple_transaction["asset"] = _id
return
self._tuple_transaction["is_data"] = True
self._tuple_transaction["asset_data"] = (self._transaction["id"], _asset)
self._tuple_transaction["asset"] = ""
def __prepare_inputs(self):
_inputs = []
input_index = 0
for _input in self._transaction["inputs"]:
_inputs.append((self._transaction["id"],
_input["fulfillment"],
_input["owners_before"],
_input["fulfills"]["transaction_id"] if _input["fulfills"] is not None else "",
str(_input["fulfills"]["output_index"]) if _input["fulfills"] is not None else "",
self.__create_hash(7),
input_index))
input_index = input_index + 1
return _inputs
def __prepare_outputs(self):
_outputs = []
_keys = []
output_index = 0
for _output in self._transaction["outputs"]:
output_id = self.__create_hash(7)
if _output["condition"]["details"].get("subconditions") is None:
_outputs.append((self._transaction["id"],
_output["amount"],
_output["condition"]["uri"],
_output["condition"]["details"]["type"],
_output["condition"]["details"]["public_key"],
output_id,
None,
None,
output_index
))
else:
_outputs.append((self._transaction["id"],
_output["amount"],
_output["condition"]["uri"],
_output["condition"]["details"]["type"],
None,
output_id,
_output["condition"]["details"]["threshold"],
_output["condition"]["details"]["subconditions"],
output_index
))
output_index = output_index + 1
for _key in _output["public_keys"]:
key_id = self.__create_hash(7)
_keys.append((key_id, self._transaction["id"], output_id, _key))
return _keys, _outputs
def __prepare_transaction(self):
return (self._transaction["id"],
self._transaction["operation"],
self._transaction["version"],
self._tuple_transaction["asset"],
self.get_map())
def convert_to_tuple(self):
self._metadata_check()
self.__asset_check()
self._tuple_transaction["transactions"] = self.__prepare_transaction()
self._tuple_transaction["inputs"] = self.__prepare_inputs()
keys, outputs = self.__prepare_outputs()
self._tuple_transaction["outputs"] = outputs
self._tuple_transaction["keys"] = keys
return self._tuple_transaction
class TransactionCompose:
def convert_to_dict(self, db_results):
transaction_map = db_results["transaction"][4]

View File

@ -26,7 +26,6 @@ try:
except ImportError:
from sha3 import sha3_256
from secrets import token_hex
from planetmint.common.crypto import PrivateKey, hash_data
from planetmint.common.exceptions import (KeypairMismatchException,
InputDoesNotExist, DoubleSpend,
@ -1182,6 +1181,7 @@ class Transaction(object):
tx_body_serialized = Transaction._to_str(tx_body)
valid_tx_id = Transaction._to_hash(tx_body_serialized)
print("VALIDTX " + tx_body_serialized)
if proposed_tx_id != valid_tx_id:
err_msg = ("The transaction's id '{}' isn't equal to "
"the hash of its body, i.e. it's not valid.")
@ -1328,106 +1328,3 @@ class Transaction(object):
raise InvalidSignature('Transaction signature is invalid.')
return True
class TransactionPrepare:
def __init__(self, _transaction):
self._transaction = _transaction
self._tuple_transaction = {
"transactions": (),
"inputs": [],
"outputs": [],
"keys": [],
"metadata": (),
"asset": "",
"asset_data": (),
"is_data": False
}
self.if_key = lambda dct, key: False if not key in dct.keys() else dct[key]
def __create_hash(self, n: int):
return token_hex(n)
def _metadata_check(self):
metadata = self._transaction.get("metadata")
self._tuple_transaction["metadata"] = (self._transaction["id"], metadata) if metadata is not None else ()
def __asset_check(self):
_asset = self._transaction.get("asset")
if _asset is None:
self._tuple_transaction["asset"] = ""
return
_id = self.if_key(dct=_asset, key="id")
# data = self.if_key(dct=_asset, key="data")
if _id is not False:
self._tuple_transaction["asset"] = _id
else:
self._tuple_transaction["is_data"] = True
_key = list(_asset.keys())[0]
self._tuple_transaction["asset_data"] = (self._transaction["id"], _asset[_key])
self._tuple_transaction["asset"] = ""
def __prepare_inputs(self):
_inputs = []
input_index = 0
for _input in self._transaction["inputs"]:
_inputs.append((self._transaction["id"],
_input["fulfillment"],
_input["owners_before"],
_input["fulfills"]["transaction_id"] if _input["fulfills"] is not None else "",
str(_input["fulfills"]["output_index"]) if _input["fulfills"] is not None else "",
self.__create_hash(7),
input_index))
input_index = input_index + 1
return _inputs
def __prepare_outputs(self):
_outputs = []
_keys = []
output_index = 0
for _output in self._transaction["outputs"]:
output_id = self.__create_hash(7)
if _output["condition"]["details"].get("subconditions") is None:
_outputs.append((self._transaction["id"],
_output["amount"],
_output["condition"]["uri"],
_output["condition"]["details"]["type"],
_output["condition"]["details"]["public_key"],
output_id,
None,
None,
output_index
))
else:
_outputs.append((self._transaction["id"],
_output["amount"],
_output["condition"]["uri"],
_output["condition"]["details"]["type"],
None,
output_id,
_output["condition"]["details"]["threshold"],
_output["condition"]["details"]["subconditions"],
output_index
))
output_index = output_index + 1
for _key in _output["public_keys"]:
key_id = self.__create_hash(7)
_keys.append((key_id, self._transaction["id"], output_id, _key))
return _keys, _outputs
def __prepare_transaction(self):
return (self._transaction["id"],
self._transaction["operation"],
self._transaction["version"],
self._tuple_transaction["asset"])
def convert_to_tuple(self):
self._metadata_check()
self.__asset_check()
self._tuple_transaction["transactions"] = self.__prepare_transaction()
self._tuple_transaction["inputs"] = self.__prepare_inputs()
keys, outputs = self.__prepare_outputs()
self._tuple_transaction["outputs"] = outputs
self._tuple_transaction["keys"] = keys
return self._tuple_transaction

View File

@ -380,6 +380,7 @@ def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol):
[([bob.public_key], 1)],
asset_id=tx.id)\
.sign([alice.private_key])
print("PROPOSEDTX " + str(same_input_double_spend.to_dict()))
b.store_bulk_transactions([tx])