332 integrate tarantool driver abstraction (#339)

* renamed bigchain_pool -> validator_obj
* renamed the flask connection pool (class name)
* prepared AsyncIO separation
* renamed abci/core.py and class names, merged utils files
* removed obsolete file
* tidy up of ABCI application logic interface
* updated to newest driver tarantool 0.12.1
* Added new start options: --abci-only and --web-api-only to enable seperate execution of the services
* Added exception handling to the ABCI app
* removed space() object usage and thereby halved the amount of DB lookups
* removed async_io handling in the connection object but left some basics of the potential initialization
* tidied up the import structure/order
* tidied up imports
* set version number and changelog

Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
Jürgen Eckel 2023-03-01 17:42:18 +01:00 committed by GitHub
parent 2c0b0f03c9
commit 83dfbed8b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 794 additions and 616 deletions

View File

@ -25,6 +25,12 @@ For reference, the possible headings are:
* **Known Issues** * **Known Issues**
* **Notes** * **Notes**
## [2.3.0] - 2023-01-03
* **Fixed** double usage of the tarantool driver in one instance that lead to crashes
* **Changed** refactored a lot of classes and the structure
* **Changed** upgraded to tarantool driver 0.12.1
## [2.2.4] - 2023-15-02 ## [2.2.4] - 2023-15-02
* **Fixed** subcondition instantiation now works recursively * **Fixed** subcondition instantiation now works recursively
* **Changed** migrated dependency management to poetry * **Changed** migrated dependency management to poetry

View File

@ -23,7 +23,7 @@ services:
- "8081:8081" - "8081:8081"
volumes: volumes:
- ./planetmint/backend/tarantool/init.lua:/opt/tarantool/init.lua - ./planetmint/backend/tarantool/init.lua:/opt/tarantool/init.lua
command: tarantool /opt/tarantool/init.lua entrypoint: tarantool /opt/tarantool/init.lua
restart: always restart: always
planetmint: planetmint:
depends_on: depends_on:

View File

@ -9,9 +9,10 @@ with Tendermint.
import logging import logging
import sys import sys
from tendermint.abci import types_pb2
from abci.application import BaseApplication from abci.application import BaseApplication
from abci.application import OkCode from abci.application import OkCode
from tendermint.abci import types_pb2
from tendermint.abci.types_pb2 import ( from tendermint.abci.types_pb2 import (
ResponseInfo, ResponseInfo,
ResponseInitChain, ResponseInitChain,
@ -23,47 +24,50 @@ from tendermint.abci.types_pb2 import (
) )
from planetmint.application.validator import Validator from planetmint.application.validator import Validator
from planetmint.model.models import Models from planetmint.abci.utils import decode_validator, decode_transaction, calculate_hash
from planetmint.abci.tendermint_utils import decode_transaction, calculate_hash, decode_validator
from planetmint.abci.block import Block from planetmint.abci.block import Block
from planetmint.ipc.events import EventTypes, Event from planetmint.ipc.events import EventTypes, Event
from planetmint.backend.exceptions import DBConcurrencyError
CodeTypeError = 1 CodeTypeError = 1
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class App(BaseApplication): class ApplicationLogic(BaseApplication):
"""Bridge between Planetmint and Tendermint. """Bridge between Planetmint and Tendermint.
The role of this class is to expose the Planetmint The role of this class is to expose the Planetmint
transaction logic to Tendermint Core. transaction logic to Tendermint Core.
""" """
def __init__(self, planetmint_node=None, events_queue=None, models: Models = None, validator: Validator = None): def __init__(
self,
validator: Validator = None,
events_queue=None,
):
# super().__init__(abci) # super().__init__(abci)
logger.debug("Checking values of types") logger.debug("Checking values of types")
logger.debug(dir(types_pb2)) logger.debug(dir(types_pb2))
self.events_queue = events_queue self.events_queue = events_queue
self.validator = Validator() self.validator = validator if validator else Validator()
self.models = models or Models()
self.block_txn_ids = [] self.block_txn_ids = []
self.block_txn_hash = "" self.block_txn_hash = ""
self.block_transactions = [] self.block_transactions = []
self.validators = None self.validators = None
self.new_height = None self.new_height = None
self.chain = self.models.get_latest_abci_chain() self.chain = self.validator.models.get_latest_abci_chain()
def log_abci_migration_error(self, chain_id, validators): def log_abci_migration_error(self, chain_id, validators):
logger.error( logger.error(
"An ABCI chain migration is in process. " "An ABCI chain migration is in process. "
"Download theself.planetmint_node.get_latest_abci_chain new ABCI client and configure it with " "Download the self.planetmint_node.get_latest_abci_chain new ABCI client and configure it with "
f"chain_id={chain_id} and validators={validators}." f"chain_id={chain_id} and validators={validators}."
) )
def abort_if_abci_chain_is_not_synced(self): def abort_if_abci_chain_is_not_synced(self):
if self.chain is None or self.chain["is_synced"]: if self.chain is None or self.chain["is_synced"]:
return return
validators = self.models.get_validators() validators = self.validator.models.get_validators()
self.log_abci_migration_error(self.chain["chain_id"], validators) self.log_abci_migration_error(self.chain["chain_id"], validators)
sys.exit(1) sys.exit(1)
@ -71,33 +75,44 @@ class App(BaseApplication):
"""Initialize chain upon genesis or a migration""" """Initialize chain upon genesis or a migration"""
app_hash = "" app_hash = ""
height = 0 height = 0
known_chain = self.models.get_latest_abci_chain() try:
known_chain = self.validator.models.get_latest_abci_chain()
if known_chain is not None: if known_chain is not None:
chain_id = known_chain["chain_id"] chain_id = known_chain["chain_id"]
if known_chain["is_synced"]: if known_chain["is_synced"]:
msg = f"Got invalid InitChain ABCI request ({genesis}) - " f"the chain {chain_id} is already synced." msg = (
f"Got invalid InitChain ABCI request ({genesis}) - " f"the chain {chain_id} is already synced."
)
logger.error(msg) logger.error(msg)
sys.exit(1) sys.exit(1)
if chain_id != genesis.chain_id: if chain_id != genesis.chain_id:
validators = self.models.get_validators() validators = self.validator.models.get_validators()
self.log_abci_migration_error(chain_id, validators) self.log_abci_migration_error(chain_id, validators)
sys.exit(1) sys.exit(1)
# set migration values for app hash and height # set migration values for app hash and height
block = self.models.get_latest_block() block = self.validator.models.get_latest_block()
app_hash = "" if block is None else block["app_hash"] app_hash = "" if block is None else block["app_hash"]
height = 0 if block is None else block["height"] + 1 height = 0 if block is None else block["height"] + 1
known_validators = self.models.get_validators() known_validators = self.validator.models.get_validators()
validator_set = [decode_validator(v) for v in genesis.validators] validator_set = [decode_validator(v) for v in genesis.validators]
if known_validators and known_validators != validator_set: if known_validators and known_validators != validator_set:
self.log_abci_migration_error(known_chain["chain_id"], known_validators) self.log_abci_migration_error(known_chain["chain_id"], known_validators)
sys.exit(1) sys.exit(1)
block = Block(app_hash=app_hash, height=height, transactions=[]) block = Block(app_hash=app_hash, height=height, transactions=[])
self.models.store_block(block._asdict()) self.validator.models.store_block(block._asdict())
self.models.store_validator_set(height + 1, validator_set) self.validator.models.store_validator_set(height + 1, validator_set)
abci_chain_height = 0 if known_chain is None else known_chain["height"] abci_chain_height = 0 if known_chain is None else known_chain["height"]
self.models.store_abci_chain(abci_chain_height, genesis.chain_id, True) self.validator.models.store_abci_chain(abci_chain_height, genesis.chain_id, True)
self.chain = {"height": abci_chain_height, "is_synced": True, "chain_id": genesis.chain_id} self.chain = {
"height": abci_chain_height,
"is_synced": True,
"chain_id": genesis.chain_id,
}
except DBConcurrencyError:
sys.exit(1)
except ValueError:
sys.exit(1)
return ResponseInitChain() return ResponseInitChain()
def info(self, request): def info(self, request):
@ -114,7 +129,13 @@ class App(BaseApplication):
# logger.info(f"Tendermint version: {request.version}") # logger.info(f"Tendermint version: {request.version}")
r = ResponseInfo() r = ResponseInfo()
block = self.models.get_latest_block() block = None
try:
block = self.validator.models.get_latest_block()
except DBConcurrencyError:
block = None
except ValueError:
block = None
if block: if block:
chain_shift = 0 if self.chain is None else self.chain["height"] chain_shift = 0 if self.chain is None else self.chain["height"]
r.last_block_height = block["height"] - chain_shift r.last_block_height = block["height"] - chain_shift
@ -136,12 +157,17 @@ class App(BaseApplication):
logger.debug("check_tx: %s", raw_transaction) logger.debug("check_tx: %s", raw_transaction)
transaction = decode_transaction(raw_transaction) transaction = decode_transaction(raw_transaction)
try:
if self.validator.is_valid_transaction(transaction): if self.validator.is_valid_transaction(transaction):
logger.debug("check_tx: VALID") logger.debug("check_tx: VALID")
return ResponseCheckTx(code=OkCode) return ResponseCheckTx(code=OkCode)
else: else:
logger.debug("check_tx: INVALID") logger.debug("check_tx: INVALID")
return ResponseCheckTx(code=CodeTypeError) return ResponseCheckTx(code=CodeTypeError)
except DBConcurrencyError:
sys.exit(1)
except ValueError:
sys.exit(1)
def begin_block(self, req_begin_block): def begin_block(self, req_begin_block):
"""Initialize list of transaction. """Initialize list of transaction.
@ -169,7 +195,15 @@ class App(BaseApplication):
self.abort_if_abci_chain_is_not_synced() self.abort_if_abci_chain_is_not_synced()
logger.debug("deliver_tx: %s", raw_transaction) logger.debug("deliver_tx: %s", raw_transaction)
transaction = self.validator.is_valid_transaction(decode_transaction(raw_transaction), self.block_transactions) transaction = None
try:
transaction = self.validator.is_valid_transaction(
decode_transaction(raw_transaction), self.block_transactions
)
except DBConcurrencyError:
sys.exit(1)
except ValueError:
sys.exit(1)
if not transaction: if not transaction:
logger.debug("deliver_tx: INVALID") logger.debug("deliver_tx: INVALID")
@ -198,10 +232,11 @@ class App(BaseApplication):
# `end_block` or `commit` # `end_block` or `commit`
logger.debug(f"Updating pre-commit state: {self.new_height}") logger.debug(f"Updating pre-commit state: {self.new_height}")
pre_commit_state = dict(height=self.new_height, transactions=self.block_txn_ids) pre_commit_state = dict(height=self.new_height, transactions=self.block_txn_ids)
self.models.store_pre_commit_state(pre_commit_state) try:
self.validator.models.store_pre_commit_state(pre_commit_state)
block_txn_hash = calculate_hash(self.block_txn_ids) block_txn_hash = calculate_hash(self.block_txn_ids)
block = self.models.get_latest_block() block = self.validator.models.get_latest_block()
logger.debug("BLOCK: ", block) logger.debug("BLOCK: ", block)
@ -211,6 +246,10 @@ class App(BaseApplication):
self.block_txn_hash = block["app_hash"] self.block_txn_hash = block["app_hash"]
validator_update = self.validator.process_block(self.new_height, self.block_transactions) validator_update = self.validator.process_block(self.new_height, self.block_transactions)
except DBConcurrencyError:
sys.exit(1)
except ValueError:
sys.exit(1)
return ResponseEndBlock(validator_updates=validator_update) return ResponseEndBlock(validator_updates=validator_update)
@ -220,15 +259,23 @@ class App(BaseApplication):
self.abort_if_abci_chain_is_not_synced() self.abort_if_abci_chain_is_not_synced()
data = self.block_txn_hash.encode("utf-8") data = self.block_txn_hash.encode("utf-8")
try:
# register a new block only when new transactions are received # register a new block only when new transactions are received
if self.block_txn_ids: if self.block_txn_ids:
self.models.store_bulk_transactions(self.block_transactions) self.validator.models.store_bulk_transactions(self.block_transactions)
block = Block(app_hash=self.block_txn_hash, height=self.new_height, transactions=self.block_txn_ids) block = Block(
app_hash=self.block_txn_hash,
height=self.new_height,
transactions=self.block_txn_ids,
)
# NOTE: storing the block should be the last operation during commit # NOTE: storing the block should be the last operation during commit
# this effects crash recovery. Refer BEP#8 for details # this effects crash recovery. Refer BEP#8 for details
self.models.store_block(block._asdict()) self.validator.models.store_block(block._asdict())
except DBConcurrencyError:
sys.exit(1)
except ValueError:
sys.exit(1)
logger.debug( logger.debug(
"Commit-ing new block with hash: apphash=%s ," "height=%s, txn ids=%s", "Commit-ing new block with hash: apphash=%s ," "height=%s, txn ids=%s",
@ -240,7 +287,11 @@ class App(BaseApplication):
if self.events_queue: if self.events_queue:
event = Event( event = Event(
EventTypes.BLOCK_VALID, EventTypes.BLOCK_VALID,
{"height": self.new_height, "hash": self.block_txn_hash, "transactions": self.block_transactions}, {
"height": self.new_height,
"hash": self.block_txn_hash,
"transactions": self.block_transactions,
},
) )
self.events_queue.put(event) self.events_queue.put(event)

View File

@ -6,9 +6,9 @@
import multiprocessing import multiprocessing
from collections import defaultdict from collections import defaultdict
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.application.validator import Validator from planetmint.application.validator import Validator
from planetmint.abci.tendermint_utils import decode_transaction from planetmint.abci.utils import decode_transaction
from abci.application import OkCode from abci.application import OkCode
from tendermint.abci.types_pb2 import ( from tendermint.abci.types_pb2 import (
ResponseCheckTx, ResponseCheckTx,
@ -16,7 +16,7 @@ from tendermint.abci.types_pb2 import (
) )
class ParallelValidationApp(App): class ParallelValidationApp(ApplicationLogic):
def __init__(self, planetmint=None, events_queue=None): def __init__(self, planetmint=None, events_queue=None):
super().__init__(planetmint, events_queue) super().__init__(planetmint, events_queue)
self.parallel_validator = ParallelValidator() self.parallel_validator = ParallelValidator()

View File

@ -7,8 +7,7 @@ from transactions.common.transaction_mode_types import (
BROADCAST_TX_SYNC, BROADCAST_TX_SYNC,
) )
from planetmint.utils import Singleton from planetmint.abci.utils import encode_transaction
from planetmint.abci.tendermint_utils import encode_transaction
from planetmint.application.validator import logger from planetmint.application.validator import logger
from planetmint.config_utils import autoconfigure from planetmint.config_utils import autoconfigure
from planetmint.config import Config from planetmint.config import Config

View File

@ -1,151 +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
import base64
import hashlib
import json
import codecs
from binascii import hexlify
from tendermint.abci import types_pb2
from tendermint.crypto import keys_pb2
from hashlib import sha3_256
from transactions.common.exceptions import InvalidPublicKey
def encode_validator(v):
ed25519_public_key = v["public_key"]["value"]
pub_key = keys_pb2.PublicKey(ed25519=bytes.fromhex(ed25519_public_key))
return types_pb2.ValidatorUpdate(pub_key=pub_key, power=v["power"])
def decode_validator(v):
return {
"public_key": {
"type": "ed25519-base64",
"value": codecs.encode(v.pub_key.ed25519, "base64").decode().rstrip("\n"),
},
"voting_power": v.power,
}
def new_validator_set(validators, updates):
validators_dict = {}
for v in validators:
validators_dict[v["public_key"]["value"]] = v
updates_dict = {}
for u in updates:
decoder = get_public_key_decoder(u["public_key"])
public_key64 = base64.b64encode(decoder(u["public_key"]["value"])).decode("utf-8")
updates_dict[public_key64] = {
"public_key": {"type": "ed25519-base64", "value": public_key64},
"voting_power": u["power"],
}
new_validators_dict = {**validators_dict, **updates_dict}
return list(new_validators_dict.values())
def get_public_key_decoder(pk):
encoding = pk["type"]
decoder = base64.b64decode
if encoding == "ed25519-base16":
decoder = base64.b16decode
elif encoding == "ed25519-base32":
decoder = base64.b32decode
elif encoding == "ed25519-base64":
decoder = base64.b64decode
else:
raise InvalidPublicKey("Invalid `type` specified for public key `value`")
return decoder
def encode_transaction(value):
"""Encode a transaction (dict) to Base64."""
return base64.b64encode(json.dumps(value).encode("utf8")).decode("utf8")
def decode_transaction(raw):
"""Decode a transaction from bytes to a dict."""
return json.loads(raw.decode("utf8"))
def decode_transaction_base64(value):
"""Decode a transaction from Base64."""
return json.loads(base64.b64decode(value.encode("utf8")).decode("utf8"))
def calculate_hash(key_list):
if not key_list:
return ""
full_hash = sha3_256()
for key in key_list:
full_hash.update(key.encode("utf8"))
return full_hash.hexdigest()
def merkleroot(hashes):
"""Computes the merkle root for a given list.
Args:
hashes (:obj:`list` of :obj:`bytes`): The leaves of the tree.
Returns:
str: Merkle root in hexadecimal form.
"""
# XXX TEMPORARY -- MUST REVIEW and possibly CHANGE
# The idea here is that the UTXO SET would be empty and this function
# would be invoked to compute the merkle root, and since there is nothing,
# i.e. an empty list, then the hash of the empty string is returned.
# This seems too easy but maybe that is good enough? TO REVIEW!
if not hashes:
return sha3_256(b"").hexdigest()
# XXX END TEMPORARY -- MUST REVIEW ...
if len(hashes) == 1:
return hexlify(hashes[0]).decode()
if len(hashes) % 2 == 1:
hashes.append(hashes[-1])
parent_hashes = [sha3_256(hashes[i] + hashes[i + 1]).digest() for i in range(0, len(hashes) - 1, 2)]
return merkleroot(parent_hashes)
# ripemd160 is only available below python 3.9.13
@DeprecationWarning
def public_key64_to_address(base64_public_key):
"""Note this only compatible with Tendermint 0.19.x"""
ed25519_public_key = public_key_from_base64(base64_public_key)
encoded_public_key = amino_encoded_public_key(ed25519_public_key)
return hashlib.new("ripemd160", encoded_public_key).hexdigest().upper()
def public_key_from_base64(base64_public_key):
return key_from_base64(base64_public_key)
def key_from_base64(base64_key):
return base64.b64decode(base64_key).hex().upper()
def public_key_to_base64(ed25519_public_key):
return key_to_base64(ed25519_public_key)
def key_to_base64(ed25519_key):
ed25519_key = bytes.fromhex(ed25519_key)
return base64.b64encode(ed25519_key).decode("utf-8")
def amino_encoded_public_key(ed25519_public_key):
return bytes.fromhex("1624DE6220{}".format(ed25519_public_key))

View File

@ -1,9 +1,16 @@
import base64
import codecs
import hashlib
import json import json
from binascii import hexlify
from hashlib import sha3_256
from packaging import version from packaging import version
from tendermint.abci import types_pb2
from tendermint.crypto import keys_pb2
from transactions.common.crypto import key_pair_from_ed25519_key from transactions.common.crypto import key_pair_from_ed25519_key
from transactions.common.exceptions import InvalidPublicKey
from planetmint.abci.tendermint_utils import key_from_base64
from planetmint.version import __tm_supported_versions__ from planetmint.version import __tm_supported_versions__
@ -33,3 +40,138 @@ def tendermint_version_is_compatible(running_tm_ver):
if version.parse(ver) == version.parse(tm_ver[0]): if version.parse(ver) == version.parse(tm_ver[0]):
return True return True
return False return False
def encode_validator(v):
ed25519_public_key = v["public_key"]["value"]
pub_key = keys_pb2.PublicKey(ed25519=bytes.fromhex(ed25519_public_key))
return types_pb2.ValidatorUpdate(pub_key=pub_key, power=v["power"])
def decode_validator(v):
return {
"public_key": {
"type": "ed25519-base64",
"value": codecs.encode(v.pub_key.ed25519, "base64").decode().rstrip("\n"),
},
"voting_power": v.power,
}
def new_validator_set(validators, updates):
validators_dict = {}
for v in validators:
validators_dict[v["public_key"]["value"]] = v
updates_dict = {}
for u in updates:
decoder = get_public_key_decoder(u["public_key"])
public_key64 = base64.b64encode(decoder(u["public_key"]["value"])).decode("utf-8")
updates_dict[public_key64] = {
"public_key": {"type": "ed25519-base64", "value": public_key64},
"voting_power": u["power"],
}
new_validators_dict = {**validators_dict, **updates_dict}
return list(new_validators_dict.values())
def get_public_key_decoder(pk):
encoding = pk["type"]
decoder = base64.b64decode
if encoding == "ed25519-base16":
decoder = base64.b16decode
elif encoding == "ed25519-base32":
decoder = base64.b32decode
elif encoding == "ed25519-base64":
decoder = base64.b64decode
else:
raise InvalidPublicKey("Invalid `type` specified for public key `value`")
return decoder
def encode_transaction(value):
"""Encode a transaction (dict) to Base64."""
return base64.b64encode(json.dumps(value).encode("utf8")).decode("utf8")
def decode_transaction(raw):
"""Decode a transaction from bytes to a dict."""
return json.loads(raw.decode("utf8"))
def decode_transaction_base64(value):
"""Decode a transaction from Base64."""
return json.loads(base64.b64decode(value.encode("utf8")).decode("utf8"))
def calculate_hash(key_list):
if not key_list:
return ""
full_hash = sha3_256()
for key in key_list:
full_hash.update(key.encode("utf8"))
return full_hash.hexdigest()
def merkleroot(hashes):
"""Computes the merkle root for a given list.
Args:
hashes (:obj:`list` of :obj:`bytes`): The leaves of the tree.
Returns:
str: Merkle root in hexadecimal form.
"""
# XXX TEMPORARY -- MUST REVIEW and possibly CHANGE
# The idea here is that the UTXO SET would be empty and this function
# would be invoked to compute the merkle root, and since there is nothing,
# i.e. an empty list, then the hash of the empty string is returned.
# This seems too easy but maybe that is good enough? TO REVIEW!
if not hashes:
return sha3_256(b"").hexdigest()
# XXX END TEMPORARY -- MUST REVIEW ...
if len(hashes) == 1:
return hexlify(hashes[0]).decode()
if len(hashes) % 2 == 1:
hashes.append(hashes[-1])
parent_hashes = [sha3_256(hashes[i] + hashes[i + 1]).digest() for i in range(0, len(hashes) - 1, 2)]
return merkleroot(parent_hashes)
@DeprecationWarning
def public_key64_to_address(base64_public_key):
"""Note this only compatible with Tendermint 0.19.x"""
ed25519_public_key = public_key_from_base64(base64_public_key)
encoded_public_key = amino_encoded_public_key(ed25519_public_key)
return hashlib.new("ripemd160", encoded_public_key).hexdigest().upper()
def public_key_from_base64(base64_public_key):
return key_from_base64(base64_public_key)
def key_from_base64(base64_key):
return base64.b64decode(base64_key).hex().upper()
def public_key_to_base64(ed25519_public_key):
return key_to_base64(ed25519_public_key)
def key_to_base64(ed25519_key):
ed25519_key = bytes.fromhex(ed25519_key)
return base64.b64encode(ed25519_key).decode("utf-8")
def amino_encoded_public_key(ed25519_public_key):
return bytes.fromhex("1624DE6220{}".format(ed25519_public_key))

View File

@ -22,16 +22,10 @@ from transactions.common.transaction import VALIDATOR_ELECTION, CHAIN_MIGRATION_
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.abci.tendermint_utils import ( from planetmint.abci.utils import encode_validator, new_validator_set, key_from_base64, public_key_to_base64
merkleroot,
key_from_base64,
public_key_to_base64,
encode_validator,
new_validator_set,
)
from planetmint.application.basevalidationrules import BaseValidationRules from planetmint.application.basevalidationrules import BaseValidationRules
from planetmint.backend.models.output import Output from planetmint.backend.models.output import Output
from planetmint.model.models import Models from planetmint.model.dataaccessor import DataAccessor
from planetmint.config import Config from planetmint.config import Config
from planetmint.config_utils import load_validation_plugin from planetmint.config_utils import load_validation_plugin
@ -39,12 +33,13 @@ logger = logging.getLogger(__name__)
class Validator: class Validator:
def __init__(self): def __init__(self, async_io: bool = False):
self.models = Models() self.async_io = async_io
self.validation = Validator._get_validationmethod() self.models = DataAccessor(async_io=async_io)
self.validation = Validator._get_validation_method()
@staticmethod @staticmethod
def _get_validationmethod(): def _get_validation_method():
validationPlugin = Config().get().get("validation_plugin") validationPlugin = Config().get().get("validation_plugin")
if validationPlugin: if validationPlugin:

View File

@ -11,7 +11,7 @@ from transactions.common.exceptions import ConfigurationError
from planetmint.config import Config from planetmint.config import Config
BACKENDS = { BACKENDS = {
"tarantool_db": "planetmint.backend.tarantool.connection.TarantoolDBConnection", "tarantool_db": "planetmint.backend.tarantool.sync_io.connection.TarantoolDBConnection",
"localmongodb": "planetmint.backend.localmongodb.connection.LocalMongoDBConnection", "localmongodb": "planetmint.backend.localmongodb.connection.LocalMongoDBConnection",
} }
@ -64,6 +64,7 @@ class DBConnection(metaclass=DBSingleton):
backend: str = None, backend: str = None,
connection_timeout: int = None, connection_timeout: int = None,
max_tries: int = None, max_tries: int = None,
async_io: bool = False,
**kwargs **kwargs
): ):
"""Create a new :class:`~.Connection` instance. """Create a new :class:`~.Connection` instance.

View File

@ -22,5 +22,9 @@ class OperationDataInsertionError(BackendError):
"""Exception raised when a Database operation fails.""" """Exception raised when a Database operation fails."""
class DBConcurrencyError(BackendError):
"""Exception raised when a Database operation fails."""
class DuplicateKeyError(OperationError): class DuplicateKeyError(OperationError):
"""Exception raised when an insert fails because the key is not unique""" """Exception raised when an insert fails because the key is not unique"""

View File

@ -1,5 +1,2 @@
# Register the single dispatched modules on import. # Register the single dispatched modules on import.
from planetmint.backend.tarantool import query, connection, schema # noqa from planetmint.backend.tarantool.sync_io import connection, query, schema
# MongoDBConnection should always be accessed via
# ``planetmint.backend.connect()``.

View File

@ -1,5 +1,10 @@
box.cfg{listen = 3303} box.cfg{listen = 3303}
box.once("bootstrap", function()
box.schema.user.grant('guest','read,write,execute,create,drop','universe')
end)
function init() function init()
-- ABCI chains -- ABCI chains
abci_chains = box.schema.create_space('abci_chains', { if_not_exists = true }) abci_chains = box.schema.create_space('abci_chains', { if_not_exists = true })

View File

@ -6,6 +6,7 @@
import logging import logging
import tarantool import tarantool
from planetmint.config import Config from planetmint.config import Config
from transactions.common.exceptions import ConfigurationError from transactions.common.exceptions import ConfigurationError
from planetmint.utils import Lazy from planetmint.utils import Lazy
@ -55,11 +56,15 @@ class TarantoolDBConnection(DBConnection):
with open(path, "r") as f: with open(path, "r") as f:
execute = f.readlines() execute = f.readlines()
f.close() f.close()
return "".join(execute).encode() return "".join(execute).encode(encoding="utf-8")
def connect(self): def connect(self):
if not self.__conn: if not self.__conn:
self.__conn = tarantool.connect(host=self.host, port=self.port) self.__conn = tarantool.Connection(
host=self.host, port=self.port, encoding="utf-8", connect_now=True, reconnect_delay=0.1
)
elif self.__conn.connected == False:
self.__conn.connect()
return self.__conn return self.__conn
def close(self): def close(self):
@ -74,65 +79,8 @@ class TarantoolDBConnection(DBConnection):
def get_space(self, space_name: str): def get_space(self, space_name: str):
return self.connect().space(space_name) return self.connect().space(space_name)
def space(self, space_name: str):
return self.query().space(space_name)
def exec(self, query, only_data=True):
try:
conn = self.connect()
conn.execute(query) if only_data else conn.execute(query)
except tarantool.error.OperationalError as op_error:
raise op_error
except tarantool.error.NetworkError as net_error:
raise net_error
def run(self, query, only_data=True):
try:
conn = self.connect()
return query.run(conn).data if only_data else query.run(conn)
except tarantool.error.OperationalError as op_error:
raise op_error
except tarantool.error.NetworkError as net_error:
raise net_error
def drop_database(self): def drop_database(self):
self.connect().call("drop") self.connect().call("drop")
def init_database(self): def init_database(self):
self.connect().call("init") self.connect().call("init")
def run_command(self, command: str, config: dict):
from subprocess import run
try:
self.close()
except ConnectionError:
pass
print(f" commands: {command}")
host_port = "%s:%s" % (self.host, self.port)
execute_cmd = self._file_content_to_bytes(path=command)
output = run(
["tarantoolctl", "connect", host_port],
input=execute_cmd,
capture_output=True,
).stderr
output = output.decode()
return output
def run_command_with_output(self, command: str):
from subprocess import run
try:
self.close()
except ConnectionError:
pass
host_port = "%s:%s" % (
Config().get()["database"]["host"],
Config().get()["database"]["port"],
)
output = run(["tarantoolctl", "connect", host_port], input=command, capture_output=True)
if output.returncode != 0:
raise Exception(f"Error while trying to execute cmd {command} on host:port {host_port}: {output.stderr}")
return output.stdout

View File

@ -4,7 +4,6 @@
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
"""Query implementation for Tarantool""" """Query implementation for Tarantool"""
import json
import logging import logging
from uuid import uuid4 from uuid import uuid4
from operator import itemgetter from operator import itemgetter
@ -15,9 +14,8 @@ from planetmint.backend import query
from planetmint.backend.models.dbtransaction import DbTransaction from planetmint.backend.models.dbtransaction import DbTransaction
from planetmint.backend.exceptions import OperationDataInsertionError from planetmint.backend.exceptions import OperationDataInsertionError
from planetmint.exceptions import CriticalDoubleSpend from planetmint.exceptions import CriticalDoubleSpend
from planetmint.backend.exceptions import DBConcurrencyError
from planetmint.backend.tarantool.const import ( from planetmint.backend.tarantool.const import (
TARANT_TABLE_META_DATA,
TARANT_TABLE_ASSETS,
TARANT_TABLE_TRANSACTION, TARANT_TABLE_TRANSACTION,
TARANT_TABLE_OUTPUT, TARANT_TABLE_OUTPUT,
TARANT_TABLE_SCRIPT, TARANT_TABLE_SCRIPT,
@ -35,13 +33,43 @@ from planetmint.backend.tarantool.const import (
) )
from planetmint.backend.utils import module_dispatch_registrar from planetmint.backend.utils import module_dispatch_registrar
from planetmint.backend.models import Asset, Block, Output from planetmint.backend.models import Asset, Block, Output
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
from transactions.common.transaction import Transaction from transactions.common.transaction import Transaction
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
register_query = module_dispatch_registrar(query) register_query = module_dispatch_registrar(query)
from tarantool.error import OperationalError, NetworkError, SchemaError
from functools import wraps
def catch_db_exception(function_to_decorate):
@wraps(function_to_decorate)
def wrapper(*args, **kw):
try:
output = function_to_decorate(*args, **kw)
except OperationalError as op_error:
raise op_error
except SchemaError as schema_error:
raise schema_error
except NetworkError as net_error:
raise net_error
except ValueError:
logger.info(f"ValueError in Query/DB instruction: {e}: raising DBConcurrencyError")
raise DBConcurrencyError
except AttributeError:
logger.info(f"Attribute in Query/DB instruction: {e}: raising DBConcurrencyError")
raise DBConcurrencyError
except Exception as e:
logger.info(f"Could not insert transactions: {e}")
if e.args[0] == 3 and e.args[1].startswith("Duplicate key exists in"):
raise CriticalDoubleSpend()
else:
raise OperationDataInsertionError()
return output
return wrapper
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
def get_complete_transactions_by_ids(connection, txids: list) -> list[DbTransaction]: def get_complete_transactions_by_ids(connection, txids: list) -> list[DbTransaction]:
@ -59,8 +87,9 @@ def get_complete_transactions_by_ids(connection, txids: list) -> list[DbTransact
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
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_TX_ID_SEARCH)) _outputs = connection.connect().select(TARANT_TABLE_OUTPUT, tx_id, index=TARANT_TX_ID_SEARCH).data
_sorted_outputs = sorted(_outputs, key=itemgetter(4)) _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]
@ -75,28 +104,34 @@ def get_transaction(connection, tx_id: str) -> Union[DbTransaction, None]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_transactions_by_asset(connection, asset: str, limit: int = 1000) -> list[DbTransaction]: def get_transactions_by_asset(connection, asset: str, limit: int = 1000) -> list[DbTransaction]:
txs = connection.run( txs = (
connection.space(TARANT_TABLE_TRANSACTION).select(asset, limit=limit, index="transactions_by_asset_cid") connection.connect()
.select(TARANT_TABLE_TRANSACTION, asset, limit=limit, index="transactions_by_asset_cid")
.data
) )
tx_ids = [tx[0] for tx in txs] tx_ids = [tx[0] for tx in txs]
return get_complete_transactions_by_ids(connection, tx_ids) return get_complete_transactions_by_ids(connection, tx_ids)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_transactions_by_metadata(connection, metadata: str, limit: int = 1000) -> list[DbTransaction]: def get_transactions_by_metadata(connection, metadata: str, limit: int = 1000) -> list[DbTransaction]:
txs = connection.run( txs = (
connection.space(TARANT_TABLE_TRANSACTION).select(metadata, limit=limit, index="transactions_by_metadata_cid") connection.connect()
.select(TARANT_TABLE_TRANSACTION, metadata, limit=limit, index="transactions_by_metadata_cid")
.data
) )
tx_ids = [tx[0] for tx in txs] tx_ids = [tx[0] for tx in txs]
return get_complete_transactions_by_ids(connection, tx_ids) return get_complete_transactions_by_ids(connection, tx_ids)
@catch_db_exception
def store_transaction_outputs(connection, output: Output, index: int) -> str: def store_transaction_outputs(connection, output: Output, index: int) -> str:
output_id = uuid4().hex output_id = uuid4().hex
try: connection.connect().insert(
connection.run( TARANT_TABLE_OUTPUT,
connection.space(TARANT_TABLE_OUTPUT).insert(
( (
output_id, output_id,
int(output.amount), int(output.amount),
@ -104,13 +139,9 @@ def store_transaction_outputs(connection, output: Output, index: int) -> str:
output.condition.to_dict(), output.condition.to_dict(),
index, index,
output.transaction_id, output.transaction_id,
) ),
) ).data
)
return output_id return output_id
except Exception as e:
logger.info(f"Could not insert Output: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@ -124,6 +155,7 @@ def store_transactions(connection, signed_transactions: list, table=TARANT_TABLE
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_transaction(connection, transaction, table=TARANT_TABLE_TRANSACTION): def store_transaction(connection, transaction, table=TARANT_TABLE_TRANSACTION):
scripts = None scripts = None
if TARANT_TABLE_SCRIPT in transaction: if TARANT_TABLE_SCRIPT in transaction:
@ -142,19 +174,13 @@ def store_transaction(connection, transaction, table=TARANT_TABLE_TRANSACTION):
transaction["inputs"], transaction["inputs"],
scripts, scripts,
) )
try: connection.connect().insert(table, tx)
connection.run(connection.space(table).insert(tx), only_data=False)
except Exception as e:
logger.info(f"Could not insert transactions: {e}")
if e.args[0] == 3 and e.args[1].startswith("Duplicate key exists in"):
raise CriticalDoubleSpend()
else:
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_transaction_by_id(connection, transaction_id, table=TARANT_TABLE_TRANSACTION): def get_transaction_by_id(connection, transaction_id, table=TARANT_TABLE_TRANSACTION):
txs = connection.run(connection.space(table).select(transaction_id, index=TARANT_ID_SEARCH), only_data=False) txs = connection.connect().select(table, transaction_id, index=TARANT_ID_SEARCH)
if len(txs) == 0: if len(txs) == 0:
return None return None
return DbTransaction.from_tuple(txs[0]) return DbTransaction.from_tuple(txs[0])
@ -172,18 +198,18 @@ def get_transactions(connection, transactions_ids: list) -> list[DbTransaction]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_asset(connection, asset_id: str) -> Asset: def get_asset(connection, asset_id: str) -> Asset:
_data = connection.run( connection.connect().select(TARANT_TABLE_TRANSACTION, asset_id, index=TARANT_INDEX_TX_BY_ASSET_ID).data
connection.space(TARANT_TABLE_TRANSACTION).select(asset_id, index=TARANT_INDEX_TX_BY_ASSET_ID)
)
return Asset.from_dict(_data[0]) return Asset.from_dict(_data[0])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
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_TRANSACTION).select(_id, index=TARANT_INDEX_TX_BY_ASSET_ID)) res = connection.connect().select(TARANT_TABLE_TRANSACTION, _id, index=TARANT_INDEX_TX_BY_ASSET_ID).data
if len(res) == 0: if len(res) == 0:
continue continue
_returned_data.append(res[0]) _returned_data.append(res[0])
@ -193,18 +219,24 @@ def get_assets(connection, assets_ids: list) -> list[Asset]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str) -> list[DbTransaction]: def get_spent(connection, fullfil_transaction_id: str, fullfil_output_index: str) -> list[DbTransaction]:
_inputs = connection.run( _inputs = (
connection.space(TARANT_TABLE_TRANSACTION).select( connection.connect()
[fullfil_transaction_id, fullfil_output_index], index=TARANT_INDEX_SPENDING_BY_ID_AND_OUTPUT_INDEX .select(
TARANT_TABLE_TRANSACTION,
[fullfil_transaction_id, fullfil_output_index],
index=TARANT_INDEX_SPENDING_BY_ID_AND_OUTPUT_INDEX,
) )
.data
) )
return get_complete_transactions_by_ids(txids=[inp[0] for inp in _inputs], connection=connection) return get_complete_transactions_by_ids(txids=[inp[0] for inp in _inputs], connection=connection)
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_latest_block(connection) -> Union[dict, None]: def get_latest_block(connection) -> Union[dict, None]:
blocks = connection.run(connection.space(TARANT_TABLE_BLOCKS).select()) blocks = connection.connect().select(TARANT_TABLE_BLOCKS).data
if not blocks: if not blocks:
return None return None
@ -214,37 +246,32 @@ def get_latest_block(connection) -> Union[dict, None]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_block(connection, block: dict): def store_block(connection, block: dict):
block_unique_id = uuid4().hex block_unique_id = uuid4().hex
try: connection.connect().insert(
connection.run( TARANT_TABLE_BLOCKS, (block_unique_id, block["app_hash"], block["height"], block[TARANT_TABLE_TRANSACTION])
connection.space(TARANT_TABLE_BLOCKS).insert(
(block_unique_id, block["app_hash"], block["height"], block[TARANT_TABLE_TRANSACTION])
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert block: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_txids_filtered(connection, asset_ids: list[str], operation: str = "", last_tx: bool = False) -> list[str]: def get_txids_filtered(connection, asset_ids: list[str], operation: str = "", last_tx: bool = False) -> list[str]:
transactions = [] transactions = []
if operation == "CREATE": if operation == "CREATE":
transactions = connection.run( transactions = (
connection.space(TARANT_TABLE_TRANSACTION).select( connection.connect()
[asset_ids[0], operation], index="transactions_by_id_and_operation" .select(TARANT_TABLE_TRANSACTION, [asset_ids[0], operation], index="transactions_by_id_and_operation")
) .data
) )
elif operation == "TRANSFER": elif operation == "TRANSFER":
transactions = connection.run( transactions = (
connection.space(TARANT_TABLE_TRANSACTION).select(asset_ids, index=TARANT_INDEX_TX_BY_ASSET_ID) connection.connect().select(TARANT_TABLE_TRANSACTION, asset_ids, index=TARANT_INDEX_TX_BY_ASSET_ID).data
) )
else: else:
txs = connection.run(connection.space(TARANT_TABLE_TRANSACTION).select(asset_ids, index=TARANT_ID_SEARCH)) txs = connection.connect().select(TARANT_TABLE_TRANSACTION, asset_ids, index=TARANT_ID_SEARCH).data
asset_txs = connection.run( asset_txs = (
connection.space(TARANT_TABLE_TRANSACTION).select(asset_ids, index=TARANT_INDEX_TX_BY_ASSET_ID) connection.connect().select(TARANT_TABLE_TRANSACTION, asset_ids, index=TARANT_INDEX_TX_BY_ASSET_ID).data
) )
transactions = txs + asset_txs transactions = txs + asset_txs
@ -258,8 +285,9 @@ def get_txids_filtered(connection, asset_ids: list[str], operation: str = "", la
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_owned_ids(connection, owner: str) -> list[DbTransaction]: def get_owned_ids(connection, owner: str) -> list[DbTransaction]:
outputs = connection.run(connection.space(TARANT_TABLE_OUTPUT).select(owner, index="public_keys")) outputs = connection.connect().select(TARANT_TABLE_OUTPUT, owner, index="public_keys").data
if len(outputs) == 0: if len(outputs) == 0:
return [] return []
txids = [output[5] for output in outputs] txids = [output[5] for output in outputs]
@ -283,8 +311,9 @@ def get_spending_transactions(connection, inputs):
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_block(connection, block_id=None) -> Union[dict, None]: def get_block(connection, block_id=None) -> Union[dict, None]:
_block = connection.run(connection.space(TARANT_TABLE_BLOCKS).select(block_id, index="height", limit=1)) _block = connection.connect().select(TARANT_TABLE_BLOCKS, block_id, index="height", limit=1).data
if len(_block) == 0: if len(_block) == 0:
return return
_block = Block.from_tuple(_block[0]) _block = Block.from_tuple(_block[0])
@ -292,8 +321,9 @@ def get_block(connection, block_id=None) -> Union[dict, None]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_block_with_transaction(connection, txid: str) -> Union[dict, None]: def get_block_with_transaction(connection, txid: str) -> Union[dict, None]:
_block = connection.run(connection.space(TARANT_TABLE_BLOCKS).select(txid, index="block_by_transaction_id")) _block = connection.connect().select(TARANT_TABLE_BLOCKS, txid, index="block_by_transaction_id").data
if len(_block) == 0: if len(_block) == 0:
return return
_block = Block.from_tuple(_block[0]) _block = Block.from_tuple(_block[0])
@ -301,30 +331,28 @@ def get_block_with_transaction(connection, txid: str) -> Union[dict, None]:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def delete_transactions(connection, txn_ids: list): def delete_transactions(connection, txn_ids: list):
try:
for _id in txn_ids: for _id in txn_ids:
_outputs = get_outputs_by_tx_id(connection, _id) _outputs = get_outputs_by_tx_id(connection, _id)
for x in range(len(_outputs)): for x in range(len(_outputs)):
connection.connect().call("delete_output", (_outputs[x].id)) connection.connect().call("delete_output", (_outputs[x].id))
for _id in txn_ids: for _id in txn_ids:
connection.run(connection.space(TARANT_TABLE_TRANSACTION).delete(_id), only_data=False) connection.connect().delete(TARANT_TABLE_TRANSACTION, _id)
connection.run(connection.space(TARANT_TABLE_GOVERNANCE).delete(_id), only_data=False) connection.connect().delete(TARANT_TABLE_GOVERNANCE, _id)
except Exception as e:
logger.info(f"Could not insert unspent output: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_unspent_outputs(connection, *unspent_outputs: list): def store_unspent_outputs(connection, *unspent_outputs: list):
result = [] result = []
if unspent_outputs: if unspent_outputs:
for utxo in unspent_outputs: for utxo in unspent_outputs:
try: try:
output = connection.run( output = (
connection.space(TARANT_TABLE_UTXOS).insert( connection.connect()
(uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo) .insert(TARANT_TABLE_UTXOS, (uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
) .data
) )
result.append(output) result.append(output)
except Exception as e: except Exception as e:
@ -334,50 +362,51 @@ def store_unspent_outputs(connection, *unspent_outputs: list):
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def delete_unspent_outputs(connection, *unspent_outputs: list): def delete_unspent_outputs(connection, *unspent_outputs: list):
result = [] result = []
if unspent_outputs: if unspent_outputs:
for utxo in unspent_outputs: for utxo in unspent_outputs:
output = connection.run( output = (
connection.space(TARANT_TABLE_UTXOS).delete( connection.connect()
(utxo["transaction_id"], utxo["output_index"]), index="utxo_by_transaction_id_and_output_index" .delete(
TARANT_TABLE_UTXOS,
(utxo["transaction_id"], utxo["output_index"]),
index="utxo_by_transaction_id_and_output_index",
) )
.data
) )
result.append(output) result.append(output)
return result return result
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_unspent_outputs(connection, query=None): # for now we don't have implementation for 'query'. def get_unspent_outputs(connection, query=None): # for now we don't have implementation for 'query'.
_utxos = connection.run(connection.space(TARANT_TABLE_UTXOS).select([])) _utxos = connection.connect().select(TARANT_TABLE_UTXOS, []).data
return [utx[3] for utx in _utxos] return [utx[3] for utx in _utxos]
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_pre_commit_state(connection, state: dict): def store_pre_commit_state(connection, state: dict):
_precommit = connection.run(connection.space(TARANT_TABLE_PRE_COMMITS).select([], limit=1)) _precommit = connection.connect().select(TARANT_TABLE_PRE_COMMITS, [], limit=1).data
_precommitTuple = ( _precommitTuple = (
(uuid4().hex, state["height"], state[TARANT_TABLE_TRANSACTION]) (uuid4().hex, state["height"], state[TARANT_TABLE_TRANSACTION])
if _precommit is None or len(_precommit) == 0 if _precommit is None or len(_precommit) == 0
else _precommit[0] else _precommit[0]
) )
try: connection.connect().upsert(
connection.run( TARANT_TABLE_PRE_COMMITS,
connection.space(TARANT_TABLE_PRE_COMMITS).upsert(
_precommitTuple, _precommitTuple,
op_list=[("=", 1, state["height"]), ("=", 2, state[TARANT_TABLE_TRANSACTION])], op_list=[("=", 1, state["height"]), ("=", 2, state[TARANT_TABLE_TRANSACTION])],
limit=1,
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert pre commit state: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_pre_commit_state(connection) -> dict: def get_pre_commit_state(connection) -> dict:
_commit = connection.run(connection.space(TARANT_TABLE_PRE_COMMITS).select([], index=TARANT_ID_SEARCH)) _commit = connection.connect().select(TARANT_TABLE_PRE_COMMITS, [], index=TARANT_ID_SEARCH).data
if _commit is None or len(_commit) == 0: if _commit is None or len(_commit) == 0:
return None return None
_commit = sorted(_commit, key=itemgetter(1), reverse=False)[0] _commit = sorted(_commit, key=itemgetter(1), reverse=False)[0]
@ -385,71 +414,56 @@ def get_pre_commit_state(connection) -> dict:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_validator_set(conn, validators_update: dict): def store_validator_set(conn, validators_update: dict):
_validator = conn.run( _validator = (
conn.space(TARANT_TABLE_VALIDATOR_SETS).select(validators_update["height"], index="height", limit=1) conn.connect().select(TARANT_TABLE_VALIDATOR_SETS, validators_update["height"], index="height", limit=1).data
) )
unique_id = uuid4().hex if _validator is None or len(_validator) == 0 else _validator[0][0] unique_id = uuid4().hex if _validator is None or len(_validator) == 0 else _validator[0][0]
try: conn.connect().upsert(
conn.run( TARANT_TABLE_VALIDATOR_SETS,
conn.space(TARANT_TABLE_VALIDATOR_SETS).upsert(
(unique_id, validators_update["height"], validators_update["validators"]), (unique_id, validators_update["height"], validators_update["validators"]),
op_list=[("=", 1, validators_update["height"]), ("=", 2, validators_update["validators"])], op_list=[("=", 1, validators_update["height"]), ("=", 2, validators_update["validators"])],
limit=1,
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert validator set: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def delete_validator_set(connection, height: int): def delete_validator_set(connection, height: int):
_validators = connection.run(connection.space(TARANT_TABLE_VALIDATOR_SETS).select(height, index="height")) _validators = connection.connect().select(TARANT_TABLE_VALIDATOR_SETS, height, index="height").data
for _valid in _validators: for _valid in _validators:
connection.run(connection.space(TARANT_TABLE_VALIDATOR_SETS).delete(_valid[0]), only_data=False) connection.connect().delete(TARANT_TABLE_VALIDATOR_SETS, _valid[0])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_election(connection, election_id: str, height: int, is_concluded: bool): def store_election(connection, election_id: str, height: int, is_concluded: bool):
try: connection.connect().upsert(
connection.run( TARANT_TABLE_ELECTIONS, (election_id, height, is_concluded), op_list=[("=", 1, height), ("=", 2, is_concluded)]
connection.space(TARANT_TABLE_ELECTIONS).upsert(
(election_id, height, is_concluded), op_list=[("=", 1, height), ("=", 2, is_concluded)], limit=1
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert election: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_elections(connection, elections: list): def store_elections(connection, elections: list):
try:
for election in elections: for election in elections:
_election = connection.run( # noqa: F841 _election = connection.connect().insert(
connection.space(TARANT_TABLE_ELECTIONS).insert( TARANT_TABLE_ELECTIONS, (election["election_id"], election["height"], election["is_concluded"])
(election["election_id"], election["height"], election["is_concluded"])
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert elections: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def delete_elections(connection, height: int): def delete_elections(connection, height: int):
_elections = connection.run(connection.space(TARANT_TABLE_ELECTIONS).select(height, index="height")) _elections = connection.connect().select(TARANT_TABLE_ELECTIONS, height, index="height").data
for _elec in _elections: for _elec in _elections:
connection.run(connection.space(TARANT_TABLE_ELECTIONS).delete(_elec[0]), only_data=False) connection.connect().delete(TARANT_TABLE_ELECTIONS, _elec[0])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_validator_set(connection, height: int = None): def get_validator_set(connection, height: int = None):
_validators = connection.run(connection.space(TARANT_TABLE_VALIDATOR_SETS).select()) _validators = connection.connect().select(TARANT_TABLE_VALIDATOR_SETS).data
if height is not None and _validators is not None: if height is not None and _validators is not None:
_validators = [ _validators = [
{"height": validator[1], "validators": validator[2]} for validator in _validators if validator[1] <= height {"height": validator[1], "validators": validator[2]} for validator in _validators if validator[1] <= height
@ -462,8 +476,9 @@ def get_validator_set(connection, height: int = None):
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_election(connection, election_id: str) -> dict: def get_election(connection, election_id: str) -> dict:
_elections = connection.run(connection.space(TARANT_TABLE_ELECTIONS).select(election_id, index=TARANT_ID_SEARCH)) _elections = connection.connect().select(TARANT_TABLE_ELECTIONS, election_id, index=TARANT_ID_SEARCH).data
if _elections is None or len(_elections) == 0: if _elections is None or len(_elections) == 0:
return None return None
_election = sorted(_elections, key=itemgetter(0), reverse=True)[0] _election = sorted(_elections, key=itemgetter(0), reverse=True)[0]
@ -471,39 +486,38 @@ def get_election(connection, election_id: str) -> dict:
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_asset_tokens_for_public_key(connection, asset_id: str, public_key: str) -> list[DbTransaction]: def get_asset_tokens_for_public_key(connection, asset_id: str, public_key: str) -> list[DbTransaction]:
id_transactions = connection.run(connection.space(TARANT_TABLE_GOVERNANCE).select([asset_id])) id_transactions = connection.connect().select(TARANT_TABLE_GOVERNANCE, [asset_id]).data
asset_id_transactions = connection.run( asset_id_transactions = (
connection.space(TARANT_TABLE_GOVERNANCE).select([asset_id], index="governance_by_asset_id") connection.connect().select(TARANT_TABLE_GOVERNANCE, [asset_id], index="governance_by_asset_id").data
) )
transactions = id_transactions + asset_id_transactions transactions = id_transactions + asset_id_transactions
return get_complete_transactions_by_ids(connection, [_tx[0] for _tx in transactions]) return get_complete_transactions_by_ids(connection, [_tx[0] for _tx in transactions])
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def store_abci_chain(connection, height: int, chain_id: str, is_synced: bool = True): def store_abci_chain(connection, height: int, chain_id: str, is_synced: bool = True):
try: connection.connect().upsert(
connection.run( TARANT_TABLE_ABCI_CHAINS,
connection.space(TARANT_TABLE_ABCI_CHAINS).upsert(
(chain_id, height, is_synced), (chain_id, height, is_synced),
op_list=[("=", 0, chain_id), ("=", 1, height), ("=", 2, is_synced)], op_list=[("=", 0, chain_id), ("=", 1, height), ("=", 2, is_synced)],
),
only_data=False,
) )
except Exception as e:
logger.info(f"Could not insert abci-chain: {e}")
raise OperationDataInsertionError()
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def delete_abci_chain(connection, height: int): def delete_abci_chain(connection, height: int):
chains = connection.run(connection.space(TARANT_TABLE_ABCI_CHAINS).select(height, index="height"), only_data=False) chains = connection.connect().select(TARANT_TABLE_ABCI_CHAINS, height, index="height")
connection.run(connection.space(TARANT_TABLE_ABCI_CHAINS).delete(chains[0][0], index="id"), only_data=False) connection.connect().delete(TARANT_TABLE_ABCI_CHAINS, chains[0][0], index="id")
@register_query(TarantoolDBConnection) @register_query(TarantoolDBConnection)
@catch_db_exception
def get_latest_abci_chain(connection) -> Union[dict, None]: def get_latest_abci_chain(connection) -> Union[dict, None]:
_all_chains = connection.run(connection.space(TARANT_TABLE_ABCI_CHAINS).select()) _all_chains = connection.connect().select(TARANT_TABLE_ABCI_CHAINS).data
if _all_chains is None or len(_all_chains) == 0: if _all_chains is None or len(_all_chains) == 0:
return None return None
_chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0] _chain = sorted(_all_chains, key=itemgetter(1), reverse=True)[0]

View File

@ -3,7 +3,7 @@ import logging
from planetmint.config import Config from planetmint.config import Config
from planetmint.backend.utils import module_dispatch_registrar from planetmint.backend.utils import module_dispatch_registrar
from planetmint import backend from planetmint import backend
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
register_schema = module_dispatch_registrar(backend.schema) register_schema = module_dispatch_registrar(backend.schema)
@ -32,19 +32,6 @@ def create_database(connection, dbname):
logger.info("Create database `%s`.", dbname) logger.info("Create database `%s`.", dbname)
def run_command_with_output(command):
from subprocess import run
host_port = "%s:%s" % (
Config().get()["database"]["host"],
Config().get()["database"]["port"],
)
output = run(["tarantoolctl", "connect", host_port], input=command, capture_output=True)
if output.returncode != 0:
raise Exception(f"Error while trying to execute cmd {command} on host:port {host_port}: {output.stderr}")
return output.stdout
@register_schema(TarantoolDBConnection) @register_schema(TarantoolDBConnection)
def create_tables(connection, dbname): def create_tables(connection, dbname):
connection.connect().call("init") connection.connect().call("init")

View File

@ -24,19 +24,16 @@ from transactions.types.elections.validator_election import ValidatorElection
from transactions.common.transaction import Transaction from transactions.common.transaction import Transaction
from planetmint.abci.rpc import ABCI_RPC
from planetmint.abci.utils import load_node_key
from planetmint.application.validator import Validator from planetmint.application.validator import Validator
from planetmint.backend import schema from planetmint.backend import schema
from planetmint.commands import utils from planetmint.commands import utils
from planetmint.commands.utils import configure_planetmint, input_on_stderr from planetmint.commands.utils import configure_planetmint, input_on_stderr
from planetmint.config_utils import setup_logging from planetmint.config_utils import setup_logging
from planetmint.abci.tendermint_utils import public_key_from_base64
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
from planetmint.abci.utils import load_node_key, public_key_from_base64
from planetmint.commands.election_types import elections from planetmint.commands.election_types import elections
from planetmint.version import __tm_supported_versions__ from planetmint.version import __tm_supported_versions__
from planetmint.config import Config from planetmint.config import Config
from planetmint.model.models import Models
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -113,7 +110,7 @@ def run_configure(args):
def run_election(args): def run_election(args):
"""Initiate and manage elections""" """Initiate and manage elections"""
b = Planetmint() b = Validator()
# Call the function specified by args.action, as defined above # Call the function specified by args.action, as defined above
globals()[f"run_election_{args.action}"](args, b) globals()[f"run_election_{args.action}"](args, b)
@ -293,6 +290,7 @@ def run_start(args):
validator = Validator() validator = Validator()
validator.rollback() validator.rollback()
del validator
logger.info("Starting Planetmint main process.") logger.info("Starting Planetmint main process.")
from planetmint.start import start from planetmint.start import start
@ -386,6 +384,21 @@ def create_parser():
help="💀 EXPERIMENTAL: parallelize validation for better throughput 💀", help="💀 EXPERIMENTAL: parallelize validation for better throughput 💀",
) )
start_parser.add_argument(
"--web-api-only",
dest="web_api_only",
default=False,
action="store_true",
help="💀 EXPERIMENTAL: seperate web API from ABCI server 💀",
)
start_parser.add_argument(
"--abci-only",
dest="abci_only",
default=False,
action="store_true",
help="💀 EXPERIMENTAL: seperate web API from ABCI server 💀",
)
return parser return parser

View File

@ -8,11 +8,10 @@ from transactions.common.exceptions import InputDoesNotExist
from planetmint import config_utils, backend from planetmint import config_utils, backend
from planetmint.const import GOVERNANCE_TRANSACTION_TYPES from planetmint.const import GOVERNANCE_TRANSACTION_TYPES
from planetmint.model.fastquery import FastQuery
from planetmint.abci.utils import key_from_base64
from planetmint.backend.connection import Connection from planetmint.backend.connection import Connection
from planetmint.backend.tarantool.const import TARANT_TABLE_TRANSACTION, TARANT_TABLE_GOVERNANCE from planetmint.backend.tarantool.const import TARANT_TABLE_TRANSACTION, TARANT_TABLE_GOVERNANCE
from planetmint.model.fastquery import FastQuery
from planetmint.abci.tendermint_utils import key_from_base64
from planetmint.backend.models.block import Block from planetmint.backend.models.block import Block
from planetmint.backend.models.output import Output from planetmint.backend.models.output import Output
from planetmint.backend.models.asset import Asset from planetmint.backend.models.asset import Asset
@ -20,10 +19,10 @@ from planetmint.backend.models.metadata import MetaData
from planetmint.backend.models.dbtransaction import DbTransaction from planetmint.backend.models.dbtransaction import DbTransaction
class Models: class DataAccessor:
def __init__(self, database_connection=None): def __init__(self, database_connection=None, async_io: bool = False):
config_utils.autoconfigure() config_utils.autoconfigure()
self.connection = database_connection if database_connection is not None else Connection() self.connection = database_connection if database_connection is not None else Connection(async_io=async_io)
def store_bulk_transactions(self, transactions): def store_bulk_transactions(self, transactions):
txns = [] txns = []

View File

@ -8,7 +8,7 @@ import setproctitle
from planetmint.config import Config from planetmint.config import Config
from planetmint.application.validator import Validator from planetmint.application.validator import Validator
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.abci.parallel_validation import ParallelValidationApp from planetmint.abci.parallel_validation import ParallelValidationApp
from planetmint.web import server, websocket_server from planetmint.web import server, websocket_server
from planetmint.ipc.events import EventTypes from planetmint.ipc.events import EventTypes
@ -35,18 +35,20 @@ BANNER = """
""" """
def start(args): def start_web_api(args):
# Exchange object for event stream api
logger.info("Starting Planetmint")
exchange = Exchange()
# start the web api
app_server = server.create_server( app_server = server.create_server(
settings=Config().get()["server"], log_config=Config().get()["log"], planetmint_factory=Validator settings=Config().get()["server"], log_config=Config().get()["log"], planetmint_factory=Validator
) )
if args.web_api_only:
app_server.run()
else:
p_webapi = Process(name="planetmint_webapi", target=app_server.run, daemon=True) p_webapi = Process(name="planetmint_webapi", target=app_server.run, daemon=True)
p_webapi.start() p_webapi.start()
def start_abci_server(args):
logger.info(BANNER.format(__version__, Config().get()["server"]["bind"])) logger.info(BANNER.format(__version__, Config().get()["server"]["bind"]))
exchange = Exchange()
# start websocket server # start websocket server
p_websocket_server = Process( p_websocket_server = Process(
@ -67,21 +69,29 @@ def start(args):
setproctitle.setproctitle("planetmint") setproctitle.setproctitle("planetmint")
# Start the ABCIServer abci_server_app = None
publisher_queue = exchange.get_publisher_queue()
if args.experimental_parallel_validation: if args.experimental_parallel_validation:
app = ABCIServer( abci_server_app = ParallelValidationApp(events_queue=publisher_queue)
app=ParallelValidationApp(
events_queue=exchange.get_publisher_queue(),
)
)
else: else:
app = ABCIServer( abci_server_app = ApplicationLogic(events_queue=publisher_queue)
app=App(
events_queue=exchange.get_publisher_queue(), app = ABCIServer(abci_server_app)
)
)
app.run() app.run()
def start(args):
logger.info("Starting Planetmint")
if args.web_api_only:
start_web_api(args)
elif args.abci_only:
start_abci_server(args)
else:
start_web_api(args)
start_abci_server(args)
if __name__ == "__main__": if __name__ == "__main__":
start() start()

View File

@ -3,8 +3,8 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
__version__ = "2.2.4" __version__ = "2.3.0"
__short_version__ = "2.2" __short_version__ = "2.3"
# Supported Tendermint versions # Supported Tendermint versions
__tm_supported_versions__ = ["0.34.15"] __tm_supported_versions__ = ["0.34.15"]

View File

@ -81,7 +81,7 @@ def create_app(*, debug=False, threads=1, planetmint_factory=None):
app.debug = debug app.debug = debug
app.config["bigchain_pool"] = utils.pool(planetmint_factory, size=threads) app.config["validator_class_name"] = utils.pool(planetmint_factory, size=threads)
add_routes(app) add_routes(app)

View File

@ -26,9 +26,9 @@ class AssetListApi(Resource):
if not args["limit"]: if not args["limit"]:
del args["limit"] del args["limit"]
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
assets = validator.models.get_assets_by_cid(cid, **args) assets = validator.models.get_assets_by_cid(cid, **args)
try: try:

View File

@ -20,9 +20,9 @@ class LatestBlock(Resource):
A JSON string containing the data about the block. A JSON string containing the data about the block.
""" """
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
block = validator.models.get_latest_block() block = validator.models.get_latest_block()
if not block: if not block:
@ -42,9 +42,9 @@ class BlockApi(Resource):
A JSON string containing the data about the block. A JSON string containing the data about the block.
""" """
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
block = validator.models.get_block(block_id=block_id) block = validator.models.get_block(block_id=block_id)
if not block: if not block:
@ -68,9 +68,9 @@ class BlockListApi(Resource):
args = parser.parse_args(strict=True) args = parser.parse_args(strict=True)
tx_id = args["transaction_id"] tx_id = args["transaction_id"]
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
block = validator.models.get_block_containing_tx(tx_id) block = validator.models.get_block_containing_tx(tx_id)
if not block: if not block:

View File

@ -34,9 +34,9 @@ class MetadataApi(Resource):
if not args["limit"]: if not args["limit"]:
del args["limit"] del args["limit"]
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
metadata = validator.models.get_metadata_by_cid(cid, **args) metadata = validator.models.get_metadata_by_cid(cid, **args)
try: try:

View File

@ -22,8 +22,8 @@ class OutputListApi(Resource):
parser.add_argument("spent", type=parameters.valid_bool) parser.add_argument("spent", type=parameters.valid_bool)
args = parser.parse_args(strict=True) args = parser.parse_args(strict=True)
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
try: try:
outputs = validator.models.get_outputs_filtered(args["public_key"], args["spent"]) outputs = validator.models.get_outputs_filtered(args["public_key"], args["spent"])
except Exception as e: except Exception as e:

View File

@ -11,17 +11,15 @@ import logging
from flask import current_app, request, jsonify from flask import current_app, request, jsonify
from flask_restful import Resource, reqparse from flask_restful import Resource, reqparse
from transactions.common.transaction import Transaction
from transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC from transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC
from transactions.common.exceptions import ( from transactions.common.exceptions import (
SchemaValidationError, SchemaValidationError,
ValidationError, ValidationError,
) )
from planetmint.abci.rpc import ABCI_RPC, MODE_COMMIT, MODE_LIST
from planetmint.abci.rpc import ABCI_RPC
from planetmint.web.views.base import make_error
from planetmint.web.views import parameters from planetmint.web.views import parameters
from transactions.common.transaction import Transaction from planetmint.web.views.base import make_error
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -36,9 +34,9 @@ class TransactionApi(Resource):
Return: Return:
A JSON string containing the data about the transaction. A JSON string containing the data about the transaction.
""" """
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
tx = validator.models.get_transaction(tx_id) tx = validator.models.get_transaction(tx_id)
if not tx: if not tx:
@ -54,7 +52,7 @@ class TransactionListApi(Resource):
parser.add_argument("asset_ids", type=parameters.valid_txid_list, required=True) parser.add_argument("asset_ids", type=parameters.valid_txid_list, required=True)
parser.add_argument("last_tx", type=parameters.valid_bool, required=False) parser.add_argument("last_tx", type=parameters.valid_bool, required=False)
args = parser.parse_args() args = parser.parse_args()
with current_app.config["bigchain_pool"]() as validator: with current_app.config["validator_class_name"]() as validator:
txs = validator.models.get_transactions_filtered(**args) txs = validator.models.get_transactions_filtered(**args)
return [tx.to_dict() for tx in txs] return [tx.to_dict() for tx in txs]
@ -70,7 +68,7 @@ class TransactionListApi(Resource):
args = parser.parse_args() args = parser.parse_args()
mode = str(args["mode"]) mode = str(args["mode"])
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
# `force` will try to format the body of the POST request even if the # `force` will try to format the body of the POST request even if the
# `content-type` header is not set to `application/json` # `content-type` header is not set to `application/json`
@ -89,9 +87,9 @@ class TransactionListApi(Resource):
except Exception as e: except Exception as e:
return make_error(500, "Invalid transaction ({}): {} - {}".format(type(e).__name__, e, tx), level="error") return make_error(500, "Invalid transaction ({}): {} - {}".format(type(e).__name__, e, tx), level="error")
with pool() as planet: with validator_class() as validator:
try: try:
planet.validate_transaction(tx_obj) validator.validate_transaction(tx_obj)
except ValidationError as e: except ValidationError as e:
return make_error(400, "Invalid transaction ({}): {}".format(type(e).__name__, e)) return make_error(400, "Invalid transaction ({}): {}".format(type(e).__name__, e))
except Exception as e: except Exception as e:

View File

@ -15,9 +15,9 @@ class ValidatorsApi(Resource):
A JSON string containing the validator set of the current node. A JSON string containing the validator set of the current node.
""" """
pool = current_app.config["bigchain_pool"] validator_class = current_app.config["validator_class_name"]
with pool() as validator: with validator_class() as validator:
validators = validator.models.get_validators() validators = validator.models.get_validators()
return validators return validators

169
poetry.lock generated
View File

@ -209,6 +209,55 @@ files = [
{file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"},
] ]
[[package]]
name = "asynctnt"
version = "2.0.1"
description = "A fast Tarantool Database connector for Python/asyncio."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "asynctnt-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c4b9e3c5cb557e4417b8845aca997441320788a0ea7d94d40692ca669239571"},
{file = "asynctnt-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:448959f8a1273bf7659c464c6159fccd426cbefabced9cfdc71e4cb48d518125"},
{file = "asynctnt-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8851db8f629568e5249a1676a0b9e849a65185533a24308822b18d93601184a"},
{file = "asynctnt-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:47cd2d10c54a17fda502144bd9c77af83589ca25a1cfdfa0e59c1042aeb4df89"},
{file = "asynctnt-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2ed53209c29fe488bc42e362ccd45f1353da59338dea7bf77811ba41369f81ad"},
{file = "asynctnt-2.0.1-cp310-cp310-win32.whl", hash = "sha256:0bc9c09cf5e63a5ae82d4e335410f7375e9b9f5c89c44ba94202441811b2d0e3"},
{file = "asynctnt-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:eaca7eae2e0161bb16f775f4997e7b71aadf3b18deec8ddbe7a3d7e129cb3b2d"},
{file = "asynctnt-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ef238ee5abf21c465d881ff94e8b5709637ce070ea84bc2071b5396320bd1eb9"},
{file = "asynctnt-2.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15a2ab6be4b5cb12826a39ef57c027068d8c8ac8aee804913d288241236fc03d"},
{file = "asynctnt-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18a7a9a6630336e97fa64401235e0647795c9b1633ce991638f1fa1e5fa8c97c"},
{file = "asynctnt-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:18086835b4db2aa6987097348156016854a43bfc6ed57de09f033393323afa7f"},
{file = "asynctnt-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:1bc9ec81543ea73e65c5849faa13763e054c6894b51b2fe0d05a9de075d11e48"},
{file = "asynctnt-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:45bb81873916267d15d2d71600297a579bcf1698e4f75d5123b17de20e291c7b"},
{file = "asynctnt-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:400220c233561a494656bdf485319918cf0faf454d10b3be87ed652f93f63da2"},
{file = "asynctnt-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b1d9e1a389bc39ec304c00544a7ce865174079a559477eba68a19532a4313b7"},
{file = "asynctnt-2.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c0dc3d0a7b9b2f12280abab0fe976984e1495407a1c7502e09d886ffeb124d4"},
{file = "asynctnt-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1b9e6dd3ec4a4396c2309393d828a2c5e7e9ae9b05e207219b3edd3ea691519b"},
{file = "asynctnt-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:61ac8b944b2a149947f07f8d5c9db10efd177deee1206c729039b8bb5e31fcb9"},
{file = "asynctnt-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1411d14fc31bdd7b9eb7b6e059a03fa373b8b6f6c7bd78d69a5488575182e65"},
{file = "asynctnt-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:dd04c6bf05ddd78be50c8aee595aeeb4a7a49ab7ae84685567d2ba0cfcd0a606"},
{file = "asynctnt-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:bf6b94630b134c400daa1c7c1e2821afe7850e454c34795bdf11d2ff619e32fa"},
{file = "asynctnt-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5956086983cda76a5a3534d1bbf076476f36de3deb77d74f25f5b067ca92752"},
{file = "asynctnt-2.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:631c6e68e9efd1a1992c154b53bb3d0aa3ae361c47535ee72538efd92d5d9209"},
{file = "asynctnt-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:602f5e860773c6640cd4260f588dc4659962d2d810e0c7d02e872c6440bbc6c8"},
{file = "asynctnt-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d286d82d0f5c11ded09d9a067caf23c37a19906351ca8b9991d1e61fb6b31bf9"},
{file = "asynctnt-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:884897f42dc0f0e5eef9fd1a8a90e1ab811d1aaae3cdda2d6021125c7c5ddce9"},
{file = "asynctnt-2.0.1-cp38-cp38-win32.whl", hash = "sha256:98ee3194e834380e5cd79cc0b3fc07510c1023b81e1b341fe442fa94614a82f7"},
{file = "asynctnt-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:b95fc8b7c0be5086a65b4b629114bf214f66a0cf20599e7464ddbbb06720ecd6"},
{file = "asynctnt-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2da8dae75300cab35fedd8dbded6954bb82c05a596af9383d5a18b9d59d5fe47"},
{file = "asynctnt-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afba9b5f4a1cbf3ba16ca15a0e190697d3f7e70c93e43a4fe12f39f03274ac8e"},
{file = "asynctnt-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:871b3ac7bc20bb3b383b4244e46dbad3de684a8562bdcd6765733d47c9da51c7"},
{file = "asynctnt-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a834c3334430dd2fa3c93ce45b2c35eef1312c9931860c25b379ea69da9b96ed"},
{file = "asynctnt-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bf3d7e6ac447bf3504db837c5ec1ee9d8c275a0f9cb2319f9b5405bd228120b"},
{file = "asynctnt-2.0.1-cp39-cp39-win32.whl", hash = "sha256:6d77b0988b7c9cc15883442f85c23bb284986532a0ed9b9bf9ff710872133bfc"},
{file = "asynctnt-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:a743511b91f4217a05e158277148c8d9523a152343a54c58b5a950ae52661c58"},
{file = "asynctnt-2.0.1.tar.gz", hash = "sha256:b11efe38122e2b9a658aadc4a4ad1182cbf7100b5d7040206a71f9f107206ebc"},
]
[package.dependencies]
PyYAML = ">=5.0"
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "22.2.0" version = "22.2.0"
@ -1590,6 +1639,44 @@ files = [
[package.dependencies] [package.dependencies]
setuptools = "*" setuptools = "*"
[[package]]
name = "numpy"
version = "1.24.2"
description = "Fundamental package for array computing in Python"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "numpy-1.24.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eef70b4fc1e872ebddc38cddacc87c19a3709c0e3e5d20bf3954c147b1dd941d"},
{file = "numpy-1.24.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d2859428712785e8a8b7d2b3ef0a1d1565892367b32f915c4a4df44d0e64f5"},
{file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6524630f71631be2dabe0c541e7675db82651eb998496bbe16bc4f77f0772253"},
{file = "numpy-1.24.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a51725a815a6188c662fb66fb32077709a9ca38053f0274640293a14fdd22978"},
{file = "numpy-1.24.2-cp310-cp310-win32.whl", hash = "sha256:2620e8592136e073bd12ee4536149380695fbe9ebeae845b81237f986479ffc9"},
{file = "numpy-1.24.2-cp310-cp310-win_amd64.whl", hash = "sha256:97cf27e51fa078078c649a51d7ade3c92d9e709ba2bfb97493007103c741f1d0"},
{file = "numpy-1.24.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7de8fdde0003f4294655aa5d5f0a89c26b9f22c0a58790c38fae1ed392d44a5a"},
{file = "numpy-1.24.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4173bde9fa2a005c2c6e2ea8ac1618e2ed2c1c6ec8a7657237854d42094123a0"},
{file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4cecaed30dc14123020f77b03601559fff3e6cd0c048f8b5289f4eeabb0eb281"},
{file = "numpy-1.24.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a23f8440561a633204a67fb44617ce2a299beecf3295f0d13c495518908e910"},
{file = "numpy-1.24.2-cp311-cp311-win32.whl", hash = "sha256:e428c4fbfa085f947b536706a2fc349245d7baa8334f0c5723c56a10595f9b95"},
{file = "numpy-1.24.2-cp311-cp311-win_amd64.whl", hash = "sha256:557d42778a6869c2162deb40ad82612645e21d79e11c1dc62c6e82a2220ffb04"},
{file = "numpy-1.24.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d0a2db9d20117bf523dde15858398e7c0858aadca7c0f088ac0d6edd360e9ad2"},
{file = "numpy-1.24.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c72a6b2f4af1adfe193f7beb91ddf708ff867a3f977ef2ec53c0ffb8283ab9f5"},
{file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29e6bd0ec49a44d7690ecb623a8eac5ab8a923bce0bea6293953992edf3a76a"},
{file = "numpy-1.24.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2eabd64ddb96a1239791da78fa5f4e1693ae2dadc82a76bc76a14cbb2b966e96"},
{file = "numpy-1.24.2-cp38-cp38-win32.whl", hash = "sha256:e3ab5d32784e843fc0dd3ab6dcafc67ef806e6b6828dc6af2f689be0eb4d781d"},
{file = "numpy-1.24.2-cp38-cp38-win_amd64.whl", hash = "sha256:76807b4063f0002c8532cfeac47a3068a69561e9c8715efdad3c642eb27c0756"},
{file = "numpy-1.24.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4199e7cfc307a778f72d293372736223e39ec9ac096ff0a2e64853b866a8e18a"},
{file = "numpy-1.24.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:adbdce121896fd3a17a77ab0b0b5eedf05a9834a18699db6829a64e1dfccca7f"},
{file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:889b2cc88b837d86eda1b17008ebeb679d82875022200c6e8e4ce6cf549b7acb"},
{file = "numpy-1.24.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f64bb98ac59b3ea3bf74b02f13836eb2e24e48e0ab0145bbda646295769bd780"},
{file = "numpy-1.24.2-cp39-cp39-win32.whl", hash = "sha256:63e45511ee4d9d976637d11e6c9864eae50e12dc9598f531c035265991910468"},
{file = "numpy-1.24.2-cp39-cp39-win_amd64.whl", hash = "sha256:a77d3e1163a7770164404607b7ba3967fb49b24782a6ef85d9b5f54126cc39e5"},
{file = "numpy-1.24.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92011118955724465fb6853def593cf397b4a1367495e0b59a7e69d40c4eb71d"},
{file = "numpy-1.24.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9006288bcf4895917d02583cf3411f98631275bc67cce355a7f39f8c14338fa"},
{file = "numpy-1.24.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:150947adbdfeceec4e5926d956a06865c1c690f2fd902efede4ca6fe2e657c3f"},
{file = "numpy-1.24.2.tar.gz", hash = "sha256:003a9f530e880cb2cd177cba1af7220b9aa42def9c4afc2a2fc3ee6be7eb2b22"},
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "23.0" version = "23.0"
@ -1602,6 +1689,55 @@ files = [
{file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"},
] ]
[[package]]
name = "pandas"
version = "1.5.3"
description = "Powerful data structures for data analysis, time series, and statistics"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
{file = "pandas-1.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3749077d86e3a2f0ed51367f30bf5b82e131cc0f14260c4d3e499186fccc4406"},
{file = "pandas-1.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:972d8a45395f2a2d26733eb8d0f629b2f90bebe8e8eddbb8829b180c09639572"},
{file = "pandas-1.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:50869a35cbb0f2e0cd5ec04b191e7b12ed688874bd05dd777c19b28cbea90996"},
{file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ac844a0fe00bfaeb2c9b51ab1424e5c8744f89860b138434a363b1f620f354"},
{file = "pandas-1.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0a56cef15fd1586726dace5616db75ebcfec9179a3a55e78f72c5639fa2a23"},
{file = "pandas-1.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:478ff646ca42b20376e4ed3fa2e8d7341e8a63105586efe54fa2508ee087f328"},
{file = "pandas-1.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6973549c01ca91ec96199e940495219c887ea815b2083722821f1d7abfa2b4dc"},
{file = "pandas-1.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c39a8da13cede5adcd3be1182883aea1c925476f4e84b2807a46e2775306305d"},
{file = "pandas-1.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f76d097d12c82a535fda9dfe5e8dd4127952b45fea9b0276cb30cca5ea313fbc"},
{file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e474390e60ed609cec869b0da796ad94f420bb057d86784191eefc62b65819ae"},
{file = "pandas-1.5.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f2b952406a1588ad4cad5b3f55f520e82e902388a6d5a4a91baa8d38d23c7f6"},
{file = "pandas-1.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:bc4c368f42b551bf72fac35c5128963a171b40dce866fb066540eeaf46faa003"},
{file = "pandas-1.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14e45300521902689a81f3f41386dc86f19b8ba8dd5ac5a3c7010ef8d2932813"},
{file = "pandas-1.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9842b6f4b8479e41968eced654487258ed81df7d1c9b7b870ceea24ed9459b31"},
{file = "pandas-1.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26d9c71772c7afb9d5046e6e9cf42d83dd147b5cf5bcb9d97252077118543792"},
{file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fbcb19d6fceb9e946b3e23258757c7b225ba450990d9ed63ccceeb8cae609f7"},
{file = "pandas-1.5.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:565fa34a5434d38e9d250af3c12ff931abaf88050551d9fbcdfafca50d62babf"},
{file = "pandas-1.5.3-cp38-cp38-win32.whl", hash = "sha256:87bd9c03da1ac870a6d2c8902a0e1fd4267ca00f13bc494c9e5a9020920e1d51"},
{file = "pandas-1.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:41179ce559943d83a9b4bbacb736b04c928b095b5f25dd2b7389eda08f46f373"},
{file = "pandas-1.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c74a62747864ed568f5a82a49a23a8d7fe171d0c69038b38cedf0976831296fa"},
{file = "pandas-1.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c4c00e0b0597c8e4f59e8d461f797e5d70b4d025880516a8261b2817c47759ee"},
{file = "pandas-1.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a50d9a4336a9621cab7b8eb3fb11adb82de58f9b91d84c2cd526576b881a0c5a"},
{file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd05f7783b3274aa206a1af06f0ceed3f9b412cf665b7247eacd83be41cf7bf0"},
{file = "pandas-1.5.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f69c4029613de47816b1bb30ff5ac778686688751a5e9c99ad8c7031f6508e5"},
{file = "pandas-1.5.3-cp39-cp39-win32.whl", hash = "sha256:7cec0bee9f294e5de5bbfc14d0573f65526071029d036b753ee6507d2a21480a"},
{file = "pandas-1.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfd681c5dc216037e0b0a2c821f5ed99ba9f03ebcf119c7dac0e9a7b960b9ec9"},
{file = "pandas-1.5.3.tar.gz", hash = "sha256:74a3fd7e5a7ec052f183273dc7b0acd3a863edf7520f5d3a1765c04ffdb3b0b1"},
]
[package.dependencies]
numpy = [
{version = ">=1.20.3", markers = "python_version < \"3.10\""},
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
]
python-dateutil = ">=2.8.1"
pytz = ">=2020.1"
[package.extras]
test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"]
[[package]] [[package]]
name = "parso" name = "parso"
version = "0.8.3" version = "0.8.3"
@ -2175,7 +2311,7 @@ cffi = ">=1.4.1"
six = "*" six = "*"
[package.extras] [package.extras]
docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] docs = ["sphinx (>=1.6.5)", "sphinx_rtd_theme"]
tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"]
[[package]] [[package]]
@ -2382,6 +2518,21 @@ files = [
{file = "python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b"}, {file = "python-baseconv-1.2.2.tar.gz", hash = "sha256:0539f8bd0464013b05ad62e0a1673f0ac9086c76b43ebf9f833053527cd9931b"},
] ]
[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
[package.dependencies]
six = ">=1.5"
[[package]] [[package]]
name = "python-decouple" name = "python-decouple"
version = "3.7" version = "3.7"
@ -2952,18 +3103,20 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
[[package]] [[package]]
name = "tarantool" name = "tarantool"
version = "0.7.1" version = "0.12.1"
description = "Python client library for Tarantool 1.6 Database" description = "Python client library for Tarantool"
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = ">=3.6"
files = [ files = [
{file = "tarantool-0.7.1-py3-none-any.whl", hash = "sha256:e557e5faf5337e6040eb324b43a21701986e0c37fae87d4c80011632faa20ff6"}, {file = "tarantool-0.12.1-py3-none-any.whl", hash = "sha256:711b47671aba6e6faedc71f57bc07a10f6d7ac728b696183e99a31f20082f187"},
{file = "tarantool-0.7.1.tar.gz", hash = "sha256:a4bf212e86c5f43dcb6baf89487f0db478a45e2c5a1b16926fbbc0e9aa6eae22"}, {file = "tarantool-0.12.1.tar.gz", hash = "sha256:80370cb5de0e35572f9515f09d8fc616367162d858ec8aacd3b537820b695c0e"},
] ]
[package.dependencies] [package.dependencies]
msgpack = ">=0.4.0" msgpack = "*"
pandas = "*"
pytz = "*"
[[package]] [[package]]
name = "tomli" name = "tomli"
@ -3308,4 +3461,4 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "ce9a30d4e4ed6d8bdd7c6cfe8f1908ffb787fc111e77fc1f709dbe8c4dba7fbb" content-hash = "8b9c0f4ebc41bca0af100c124f3522e2e1052271ebde525b0b06b44496a2d105"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "planetmint" name = "planetmint"
version = "2.2.4" version = "2.3.0"
description = "Planetmint: The Blockchain Database" description = "Planetmint: The Blockchain Database"
authors = ["Planetmint contributors"] authors = ["Planetmint contributors"]
license = "AGPLv3" license = "AGPLv3"
@ -26,7 +26,6 @@ python = "^3.9"
chardet = "3.0.4" chardet = "3.0.4"
base58 = "2.1.1" base58 = "2.1.1"
aiohttp = "3.8.1" aiohttp = "3.8.1"
abci = "0.8.3"
flask-cors = "3.0.10" flask-cors = "3.0.10"
flask-restful = "0.3.9" flask-restful = "0.3.9"
flask = "2.1.2" flask = "2.1.2"
@ -35,7 +34,7 @@ jsonschema = "4.16.0"
logstats = "0.3.0" logstats = "0.3.0"
packaging = ">=22.0" packaging = ">=22.0"
pymongo = "3.11.4" pymongo = "3.11.4"
tarantool = "0.7.1" tarantool = ">=0.12.1"
python-rapidjson = ">=1.0" python-rapidjson = ">=1.0"
pyyaml = "6.0.0" pyyaml = "6.0.0"
requests = "2.25.1" requests = "2.25.1"
@ -47,6 +46,8 @@ planetmint-ipld = ">=0.0.3"
pyasn1 = ">=0.4.8" pyasn1 = ">=0.4.8"
python-decouple = "^3.7" python-decouple = "^3.7"
planetmint-transactions = ">=0.7.0" planetmint-transactions = ">=0.7.0"
asynctnt = "^2.0.1"
abci = "^0.8.3"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
aafigure = "0.6" aafigure = "0.6"

View File

@ -10,7 +10,7 @@ pytestmark = pytest.mark.bdb
def test_get_txids_filtered(signed_create_tx, signed_transfer_tx, db_conn): def test_get_txids_filtered(signed_create_tx, signed_transfer_tx, db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
# create and insert two blocks, one for the create and one for the # create and insert two blocks, one for the create and one for the
# transfer transaction # transfer transaction
@ -36,7 +36,7 @@ def test_get_txids_filtered(signed_create_tx, signed_transfer_tx, db_conn):
def test_get_owned_ids(signed_create_tx, user_pk, db_conn): def test_get_owned_ids(signed_create_tx, user_pk, db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
# insert a transaction # insert a transaction
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()])
@ -49,18 +49,18 @@ def test_get_owned_ids(signed_create_tx, user_pk, db_conn):
def test_store_block(db_conn): def test_store_block(db_conn):
from planetmint.abci.block import Block from planetmint.abci.block import Block
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
block = Block(app_hash="random_utxo", height=3, transactions=[]) block = Block(app_hash="random_utxo", height=3, transactions=[])
query.store_block(connection=db_conn, block=block._asdict()) query.store_block(connection=db_conn, block=block._asdict())
# block = query.get_block(connection=db_conn) # block = query.get_block(connection=db_conn)
blocks = db_conn.run(db_conn.space("blocks").select([])) blocks = db_conn.connect().select("blocks", []).data
assert len(blocks) == 1 assert len(blocks) == 1
def test_get_block(db_conn): def test_get_block(db_conn):
from planetmint.abci.block import Block from planetmint.abci.block import Block
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
block = Block(app_hash="random_utxo", height=3, transactions=[]) block = Block(app_hash="random_utxo", height=3, transactions=[])
@ -71,7 +71,7 @@ def test_get_block(db_conn):
def test_store_pre_commit_state(db_conn): def test_store_pre_commit_state(db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
state = dict(height=3, transactions=[]) state = dict(height=3, transactions=[])
@ -84,11 +84,11 @@ def test_store_pre_commit_state(db_conn):
def test_get_pre_commit_state(db_conn): def test_get_pre_commit_state(db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
all_pre = db_conn.run(db_conn.space("pre_commits").select([])) all_pre = db_conn.connect().select("pre_commits", []).data
for pre in all_pre: for pre in all_pre:
db_conn.run(db_conn.space("pre_commits").delete(pre[0]), only_data=False) db_conn.connect().delete("pre_commits", pre[0])
# TODO First IN, First OUT # TODO First IN, First OUT
state = dict(height=3, transactions=[]) state = dict(height=3, transactions=[])
# db_context.conn.db.pre_commit.insert_one # db_context.conn.db.pre_commit.insert_one
@ -98,10 +98,14 @@ def test_get_pre_commit_state(db_conn):
def test_validator_update(db_conn): def test_validator_update(db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
def gen_validator_update(height): def gen_validator_update(height):
return {"validators": [], "height": height, "election_id": f"election_id_at_height_{height}"} return {
"validators": [],
"height": height,
"election_id": f"election_id_at_height_{height}",
}
# return {'data': 'somedata', 'height': height, 'election_id': f'election_id_at_height_{height}'} # return {'data': 'somedata', 'height': height, 'election_id': f'election_id_at_height_{height}'}
for i in range(1, 100, 10): for i in range(1, 100, 10):
@ -160,7 +164,7 @@ def test_validator_update(db_conn):
], ],
) )
def test_store_abci_chain(description, stores, expected, db_conn): def test_store_abci_chain(description, stores, expected, db_conn):
from planetmint.backend.tarantool import query from planetmint.backend.tarantool.sync_io import query
for store in stores: for store in stores:
query.store_abci_chain(db_conn, **store) query.store_abci_chain(db_conn, **store)

View File

@ -8,7 +8,7 @@ import pytest
def test_get_connection_raises_a_configuration_error(monkeypatch): def test_get_connection_raises_a_configuration_error(monkeypatch):
from planetmint.backend.exceptions import ConnectionError from planetmint.backend.exceptions import ConnectionError
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
with pytest.raises(ConnectionError): with pytest.raises(ConnectionError):
TarantoolDBConnection("localhost", "1337", "mydb", "password") TarantoolDBConnection("localhost", "1337", "mydb", "password")
@ -17,7 +17,6 @@ def test_get_connection_raises_a_configuration_error(monkeypatch):
@pytest.mark.skip(reason="we currently do not suppport mongodb.") @pytest.mark.skip(reason="we currently do not suppport mongodb.")
def test_get_connection_raises_a_configuration_error_mongodb(monkeypatch): def test_get_connection_raises_a_configuration_error_mongodb(monkeypatch):
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from transactions.common.exceptions import ConfigurationError
with pytest.raises(ConnectionError): with pytest.raises(ConnectionError):
conn = LocalMongoDBConnection("localhost", "1337", "mydb", "password") conn = LocalMongoDBConnection("localhost", "1337", "mydb", "password")

View File

@ -109,7 +109,7 @@ def test_bigchain_show_config(capsys):
def test__run_init(mocker): def test__run_init(mocker):
init_db_mock = mocker.patch("planetmint.backend.tarantool.connection.TarantoolDBConnection.init_database") init_db_mock = mocker.patch("planetmint.backend.tarantool.sync_io.connection.TarantoolDBConnection.init_database")
conn = Connection() conn = Connection()
conn.init_database() conn.init_database()

View File

@ -22,15 +22,14 @@ from logging import getLogger
from logging.config import dictConfig from logging.config import dictConfig
from planetmint.backend.connection import Connection from planetmint.backend.connection import Connection
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
from transactions.common import crypto from transactions.common import crypto
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from planetmint.abci.tendermint_utils import key_from_base64 from planetmint.abci.utils import key_from_base64
from planetmint.backend import schema, query from planetmint.backend import schema, query
from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
from planetmint.abci.block import Block from planetmint.abci.block import Block
from planetmint.abci.rpc import MODE_LIST from planetmint.abci.rpc import MODE_LIST
from planetmint.model.models import Models
from tests.utils import gen_vote from tests.utils import gen_vote
from planetmint.config import Config from planetmint.config import Config
from transactions.types.elections.validator_election import ValidatorElection # noqa from transactions.types.elections.validator_election import ValidatorElection # noqa
@ -142,6 +141,8 @@ def _bdb(_setup_database):
from planetmint.config import Config from planetmint.config import Config
conn = Connection() conn = Connection()
conn.close()
conn.connect()
yield yield
dbname = Config().get()["database"]["name"] dbname = Config().get()["database"]["name"]
flush_db(conn, dbname) flush_db(conn, dbname)
@ -241,7 +242,6 @@ def merlin():
@pytest.fixture @pytest.fixture
# def a():
def abci_fixture(): def abci_fixture():
from tendermint.abci import types_pb2 from tendermint.abci import types_pb2
@ -250,9 +250,9 @@ def abci_fixture():
@pytest.fixture @pytest.fixture
def test_models(): def test_models():
from planetmint.model.models import Models from planetmint.model.dataaccessor import DataAccessor
return Models() return DataAccessor()
@pytest.fixture @pytest.fixture
@ -273,7 +273,10 @@ def test_abci_rpc():
def b(): def b():
from planetmint.application import Validator from planetmint.application import Validator
return Validator() validator = Validator()
validator.models.connection.close()
validator.models.connection.connect()
return validator
@pytest.fixture @pytest.fixture
@ -385,7 +388,10 @@ def db_name(db_config):
@pytest.fixture @pytest.fixture
def db_conn(): def db_conn():
return Connection() conn = Connection()
conn.close()
conn.connect()
return conn
@pytest.fixture @pytest.fixture
@ -453,10 +459,10 @@ def abci_server():
from abci.server import ABCIServer from abci.server import ABCIServer
# from tendermint.abci import types_pb2 as types_v0_34_11 # from tendermint.abci import types_pb2 as types_v0_34_11
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.utils import Process from planetmint.utils import Process
app = ABCIServer(app=App()) app = ABCIServer(app=ApplicationLogic())
abci_proxy = Process(name="ABCI", target=app.run) abci_proxy = Process(name="ABCI", target=app.run)
yield abci_proxy.start() yield abci_proxy.start()
abci_proxy.terminate() abci_proxy.terminate()

View File

@ -3,7 +3,6 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
import random import random
import warnings
from unittest.mock import patch from unittest.mock import patch
import pytest import pytest
from base58 import b58decode from base58 import b58decode
@ -14,7 +13,6 @@ from transactions.common.transaction import TransactionLink
from transactions.common.transaction import Transaction from transactions.common.transaction import Transaction
from transactions.types.assets.create import Create from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer from transactions.types.assets.transfer import Transfer
from planetmint.model.fastquery import FastQuery
from planetmint.exceptions import CriticalDoubleSpend from planetmint.exceptions import CriticalDoubleSpend
pytestmark = pytest.mark.bdb pytestmark = pytest.mark.bdb
@ -48,10 +46,8 @@ class TestBigchainApi(object):
b.models.store_bulk_transactions([transfer_tx2]) b.models.store_bulk_transactions([transfer_tx2])
def test_double_inclusion(self, b, alice): def test_double_inclusion(self, b, alice):
from tarantool.error import DatabaseError
from planetmint.backend.exceptions import OperationError from planetmint.backend.exceptions import OperationError
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
tx = Create.generate([alice.public_key], [([alice.public_key], 1)]) tx = Create.generate([alice.public_key], [([alice.public_key], 1)])
tx = tx.sign([alice.private_key]) tx = tx.sign([alice.private_key])

View File

@ -15,12 +15,11 @@ from transactions import ValidatorElection, ChainMigrationElection
from transactions.common.crypto import generate_key_pair from transactions.common.crypto import generate_key_pair
from transactions.types.assets.create import Create from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer from transactions.types.assets.transfer import Transfer
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.backend import query from planetmint.backend import query
from planetmint.abci.core import OkCode, CodeTypeError from planetmint.abci.application_logic import OkCode, CodeTypeError
from planetmint.abci.block import Block from planetmint.abci.block import Block
from planetmint.abci.tendermint_utils import new_validator_set from planetmint.abci.utils import new_validator_set, public_key_to_base64
from planetmint.abci.tendermint_utils import public_key_to_base64
from planetmint.version import __tm_supported_versions__ from planetmint.version import __tm_supported_versions__
from tests.utils import generate_election, generate_validators from tests.utils import generate_election, generate_validators
@ -49,7 +48,7 @@ def generate_init_chain_request(chain_id, vals=None):
def test_init_chain_successfully_registers_chain(b): def test_init_chain_successfully_registers_chain(b):
request = generate_init_chain_request("chain-XYZ") request = generate_init_chain_request("chain-XYZ")
res = App(b).init_chain(request) res = ApplicationLogic(validator=b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
chain = query.get_latest_abci_chain(b.models.connection) chain = query.get_latest_abci_chain(b.models.connection)
assert chain == {"height": 0, "chain_id": "chain-XYZ", "is_synced": True} assert chain == {"height": 0, "chain_id": "chain-XYZ", "is_synced": True}
@ -63,7 +62,7 @@ def test_init_chain_successfully_registers_chain(b):
def test_init_chain_ignores_invalid_init_chain_requests(b): def test_init_chain_ignores_invalid_init_chain_requests(b):
validators = [generate_validator()] validators = [generate_validator()]
request = generate_init_chain_request("chain-XYZ", validators) request = generate_init_chain_request("chain-XYZ", validators)
res = App(b).init_chain(request) res = ApplicationLogic(validator=b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.models.connection) validator_set = query.get_validator_set(b.models.connection)
@ -77,7 +76,7 @@ def test_init_chain_ignores_invalid_init_chain_requests(b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).init_chain(r) ApplicationLogic(validator=b).init_chain(r)
# assert nothing changed - neither validator set, nor chain ID # assert nothing changed - neither validator set, nor chain ID
new_validator_set = query.get_validator_set(b.models.connection) new_validator_set = query.get_validator_set(b.models.connection)
assert new_validator_set == validator_set assert new_validator_set == validator_set
@ -93,7 +92,7 @@ def test_init_chain_ignores_invalid_init_chain_requests(b):
def test_init_chain_recognizes_new_chain_after_migration(b): def test_init_chain_recognizes_new_chain_after_migration(b):
validators = [generate_validator()] validators = [generate_validator()]
request = generate_init_chain_request("chain-XYZ", validators) request = generate_init_chain_request("chain-XYZ", validators)
res = App(b).init_chain(request) res = ApplicationLogic(validator=b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
validator_set = query.get_validator_set(b.models.connection)["validators"] validator_set = query.get_validator_set(b.models.connection)["validators"]
@ -111,7 +110,7 @@ def test_init_chain_recognizes_new_chain_after_migration(b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).init_chain(r) ApplicationLogic(validator=b).init_chain(r)
assert query.get_latest_abci_chain(b.models.connection) == { assert query.get_latest_abci_chain(b.models.connection) == {
"chain_id": "chain-XYZ-migrated-at-height-1", "chain_id": "chain-XYZ-migrated-at-height-1",
"is_synced": False, "is_synced": False,
@ -123,7 +122,7 @@ def test_init_chain_recognizes_new_chain_after_migration(b):
# a request with the matching chain ID and matching validator set # a request with the matching chain ID and matching validator set
# completes the migration # completes the migration
request = generate_init_chain_request("chain-XYZ-migrated-at-height-1", validators) request = generate_init_chain_request("chain-XYZ-migrated-at-height-1", validators)
res = App(b).init_chain(request) res = ApplicationLogic(validator=b).init_chain(request)
assert res == types.ResponseInitChain() assert res == types.ResponseInitChain()
assert query.get_latest_abci_chain(b.models.connection) == { assert query.get_latest_abci_chain(b.models.connection) == {
"chain_id": "chain-XYZ-migrated-at-height-1", "chain_id": "chain-XYZ-migrated-at-height-1",
@ -144,7 +143,7 @@ def test_init_chain_recognizes_new_chain_after_migration(b):
] ]
for r in invalid_requests: for r in invalid_requests:
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).init_chain(r) ApplicationLogic(validator=b).init_chain(r)
assert query.get_latest_abci_chain(b.models.connection) == { assert query.get_latest_abci_chain(b.models.connection) == {
"chain_id": "chain-XYZ-migrated-at-height-1", "chain_id": "chain-XYZ-migrated-at-height-1",
"is_synced": True, "is_synced": True,
@ -161,7 +160,7 @@ def test_init_chain_recognizes_new_chain_after_migration(b):
def test_info(b): def test_info(b):
r = types.RequestInfo(version=__tm_supported_versions__[0]) r = types.RequestInfo(version=__tm_supported_versions__[0])
app = App(b) app = ApplicationLogic(validator=b)
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
@ -174,7 +173,7 @@ def test_info(b):
# simulate a migration and assert the height is shifted # simulate a migration and assert the height is shifted
b.models.store_abci_chain(2, "chain-XYZ") b.models.store_abci_chain(2, "chain-XYZ")
app = App(b) app = ApplicationLogic(validator=b)
b.models.store_block(Block(app_hash="2", height=2, transactions=[])._asdict()) b.models.store_block(Block(app_hash="2", height=2, transactions=[])._asdict())
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
@ -187,7 +186,7 @@ def test_info(b):
# it's always the latest migration that is taken into account # it's always the latest migration that is taken into account
b.models.store_abci_chain(4, "chain-XYZ-new") b.models.store_abci_chain(4, "chain-XYZ-new")
app = App(b) app = ApplicationLogic(validator=b)
b.models.store_block(Block(app_hash="4", height=4, transactions=[])._asdict()) b.models.store_block(Block(app_hash="4", height=4, transactions=[])._asdict())
res = app.info(r) res = app.info(r)
assert res.last_block_height == 0 assert res.last_block_height == 0
@ -200,7 +199,7 @@ def test_check_tx__signed_create_is_ok(b):
tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key]) tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key])
app = App(b) app = ApplicationLogic(validator=b)
result = app.check_tx(encode_tx_to_bytes(tx)) result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == OkCode assert result.code == OkCode
@ -211,7 +210,7 @@ def test_check_tx__unsigned_create_is_error(b):
tx = Create.generate([alice.public_key], [([bob.public_key], 1)]) tx = Create.generate([alice.public_key], [([bob.public_key], 1)])
app = App(b) app = ApplicationLogic(validator=b)
result = app.check_tx(encode_tx_to_bytes(tx)) result = app.check_tx(encode_tx_to_bytes(tx))
assert result.code == CodeTypeError assert result.code == CodeTypeError
@ -223,7 +222,7 @@ def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_reque
tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key]) tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key])
app = App(b, events) app = ApplicationLogic(validator=b, events_queue=events)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
@ -253,7 +252,7 @@ def test_deliver_tx__double_spend_fails(b, init_chain_request):
tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key]) tx = Create.generate([alice.public_key], [([bob.public_key], 1)]).sign([alice.private_key])
app = App(b) app = ApplicationLogic(validator=b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -270,7 +269,7 @@ def test_deliver_tx__double_spend_fails(b, init_chain_request):
def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request): def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request):
app = App(b) app = ApplicationLogic(validator=b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -303,7 +302,7 @@ def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request):
def test_end_block_return_validator_updates(b, init_chain_request): def test_end_block_return_validator_updates(b, init_chain_request):
app = App(b) app = ApplicationLogic(validator=b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -335,7 +334,7 @@ def test_end_block_return_validator_updates(b, init_chain_request):
def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request): def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.backend import query from planetmint.backend import query
tx = Create.generate( tx = Create.generate(
@ -344,7 +343,7 @@ def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
assets=[{"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}], assets=[{"data": "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"}],
).sign([alice.private_key]) ).sign([alice.private_key])
app = App(b) app = ApplicationLogic(validator=b)
app.init_chain(init_chain_request) app.init_chain(init_chain_request)
begin_block = types.RequestBeginBlock() begin_block = types.RequestBeginBlock()
@ -365,7 +364,7 @@ def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request):
# simulate a chain migration and assert the height is shifted # simulate a chain migration and assert the height is shifted
b.models.store_abci_chain(100, "new-chain") b.models.store_abci_chain(100, "new-chain")
app = App(b) app = ApplicationLogic(validator=b)
app.begin_block(begin_block) app.begin_block(begin_block)
app.deliver_tx(encode_tx_to_bytes(tx)) app.deliver_tx(encode_tx_to_bytes(tx))
app.end_block(types.RequestEndBlock(height=1)) app.end_block(types.RequestEndBlock(height=1))
@ -470,39 +469,39 @@ def test_info_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).info(types.RequestInfo()) ApplicationLogic(validator=b).info(types.RequestInfo())
def test_check_tx_aborts_if_chain_is_not_synced(b): def test_check_tx_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).check_tx("some bytes") ApplicationLogic(validator=b).check_tx("some bytes")
def test_begin_aborts_if_chain_is_not_synced(b): def test_begin_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).info(types.RequestBeginBlock()) ApplicationLogic(validator=b).info(types.RequestBeginBlock())
def test_deliver_tx_aborts_if_chain_is_not_synced(b): def test_deliver_tx_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).deliver_tx("some bytes") ApplicationLogic(validator=b).deliver_tx("some bytes")
def test_end_block_aborts_if_chain_is_not_synced(b): def test_end_block_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).info(types.RequestEndBlock()) ApplicationLogic(validator=b).info(types.RequestEndBlock())
def test_commit_aborts_if_chain_is_not_synced(b): def test_commit_aborts_if_chain_is_not_synced(b):
b.models.store_abci_chain(0, "chain-XYZ", False) b.models.store_abci_chain(0, "chain-XYZ", False)
with pytest.raises(SystemExit): with pytest.raises(SystemExit):
App(b).commit() ApplicationLogic(validator=b).commit()

View File

@ -21,11 +21,11 @@ from io import BytesIO
@pytest.mark.bdb @pytest.mark.bdb
def test_app(b, eventqueue_fixture, init_chain_request): def test_app(b, eventqueue_fixture, init_chain_request):
from planetmint.abci.core import App from planetmint.abci.application_logic import ApplicationLogic
from planetmint.abci.tendermint_utils import calculate_hash from planetmint.abci.utils import calculate_hash
from transactions.common.crypto import generate_key_pair from transactions.common.crypto import generate_key_pair
app = App(b, eventqueue_fixture) app = ApplicationLogic(validator=b, events_queue=eventqueue_fixture)
p = ProtocolHandler(app) p = ProtocolHandler(app)
data = p.process("info", types.Request(info=types.RequestInfo(version=__tm_supported_versions__[0]))) data = p.process("info", types.Request(info=types.RequestInfo(version=__tm_supported_versions__[0])))

View File

@ -29,7 +29,7 @@ from tests.utils import delete_unspent_outputs, get_utxoset_merkle_root, store_u
def test_asset_is_separated_from_transaciton(b): def test_asset_is_separated_from_transaciton(b):
import copy import copy
from transactions.common.crypto import generate_key_pair from transactions.common.crypto import generate_key_pair
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
if isinstance(b.models.connection, TarantoolDBConnection): if isinstance(b.models.connection, TarantoolDBConnection):
pytest.skip("This specific function is skipped because, assets are stored differently if using Tarantool") pytest.skip("This specific function is skipped because, assets are stored differently if using Tarantool")
@ -102,7 +102,7 @@ def test_validation_error(b):
@patch("requests.post") @patch("requests.post")
def test_write_and_post_transaction(mock_post, b, test_abci_rpc): def test_write_and_post_transaction(mock_post, b, test_abci_rpc):
from transactions.common.crypto import generate_key_pair from transactions.common.crypto import generate_key_pair
from planetmint.abci.tendermint_utils import encode_transaction from planetmint.abci.utils import encode_transaction
alice = generate_key_pair() alice = generate_key_pair()
tx = ( tx = (
@ -235,7 +235,7 @@ def test_store_zero_unspent_output(b):
@pytest.mark.bdb @pytest.mark.bdb
def test_store_one_unspent_output(b, unspent_output_1, utxo_collection): def test_store_one_unspent_output(b, unspent_output_1, utxo_collection):
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
res = store_unspent_outputs(b.models.connection, unspent_output_1) res = store_unspent_outputs(b.models.connection, unspent_output_1)
if not isinstance(b.models.connection, TarantoolDBConnection): if not isinstance(b.models.connection, TarantoolDBConnection):

View File

@ -11,7 +11,8 @@ from hashlib import sha3_256
def test_encode_decode_transaction(b): def test_encode_decode_transaction(b):
from planetmint.abci.tendermint_utils import encode_transaction, decode_transaction from planetmint.abci.utils import decode_transaction
from planetmint.abci.utils import encode_transaction
asset = {"value": "key"} asset = {"value": "key"}
@ -25,7 +26,7 @@ def test_encode_decode_transaction(b):
def test_calculate_hash_no_key(b): def test_calculate_hash_no_key(b):
from planetmint.abci.tendermint_utils import calculate_hash from planetmint.abci.utils import calculate_hash
# pass an empty list # pass an empty list
assert calculate_hash([]) == "" assert calculate_hash([]) == ""
@ -33,7 +34,7 @@ def test_calculate_hash_no_key(b):
# TODO test for the case of an empty list of hashes, and possibly other cases. # TODO test for the case of an empty list of hashes, and possibly other cases.
def test_merkleroot(): def test_merkleroot():
from planetmint.abci.tendermint_utils import merkleroot from planetmint.abci.utils import merkleroot
hashes = [sha3_256(i.encode()).digest() for i in "abc"] hashes = [sha3_256(i.encode()).digest() for i in "abc"]
assert merkleroot(hashes) == ("78c7c394d3158c218916b7ae0ebdea502e0f4e85c08e3b371e3dfd824d389fa3") assert merkleroot(hashes) == ("78c7c394d3158c218916b7ae0ebdea502e0f4e85c08e3b371e3dfd824d389fa3")
@ -49,14 +50,15 @@ SAMPLE_PUBLIC_KEY = {
reason="ripemd160, the core of pulbic_key64_to_address is no longer supported by hashlib (from python 3.9.13 on)" reason="ripemd160, the core of pulbic_key64_to_address is no longer supported by hashlib (from python 3.9.13 on)"
) )
def test_convert_base64_public_key_to_address(): def test_convert_base64_public_key_to_address():
from planetmint.abci.tendermint_utils import public_key64_to_address from planetmint.abci.utils import public_key64_to_address
address = public_key64_to_address(SAMPLE_PUBLIC_KEY["pub_key"]["value"]) address = public_key64_to_address(SAMPLE_PUBLIC_KEY["pub_key"]["value"])
assert address == SAMPLE_PUBLIC_KEY["address"] assert address == SAMPLE_PUBLIC_KEY["address"]
def test_public_key_encoding_decoding(): def test_public_key_encoding_decoding():
from planetmint.abci.tendermint_utils import public_key_from_base64, public_key_to_base64 from planetmint.abci.utils import public_key_to_base64
from planetmint.abci.utils import public_key_from_base64
public_key = public_key_from_base64(SAMPLE_PUBLIC_KEY["pub_key"]["value"]) public_key = public_key_from_base64(SAMPLE_PUBLIC_KEY["pub_key"]["value"])
base64_public_key = public_key_to_base64(public_key) base64_public_key = public_key_to_base64(public_key)

View File

@ -7,7 +7,7 @@ import pytest
import codecs import codecs
from planetmint.abci.rpc import MODE_LIST, MODE_COMMIT from planetmint.abci.rpc import MODE_LIST, MODE_COMMIT
from planetmint.abci.tendermint_utils import public_key_to_base64 from planetmint.abci.utils import public_key_to_base64
from transactions.types.elections.validator_election import ValidatorElection from transactions.types.elections.validator_election import ValidatorElection
from transactions.common.exceptions import AmountError from transactions.common.exceptions import AmountError

View File

@ -7,7 +7,7 @@ import pytest
from argparse import Namespace from argparse import Namespace
from unittest.mock import patch from unittest.mock import patch
from planetmint.abci.tendermint_utils import public_key_to_base64 from planetmint.abci.utils import public_key_to_base64
from transactions.types.elections.validator_election import ValidatorElection from transactions.types.elections.validator_election import ValidatorElection
from transactions.common.exceptions import ( from transactions.common.exceptions import (
DuplicateTransaction, DuplicateTransaction,

View File

@ -13,14 +13,14 @@ from functools import singledispatch
from planetmint import backend from planetmint import backend
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.sync_io.connection import TarantoolDBConnection
from planetmint.backend.schema import TABLES from planetmint.backend.schema import TABLES
from transactions.common import crypto from transactions.common import crypto
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.types.assets.create import Create from transactions.types.assets.create import Create
from transactions.types.elections.vote import Vote from transactions.types.elections.vote import Vote
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.abci.tendermint_utils import key_to_base64, merkleroot from planetmint.abci.utils import merkleroot, key_to_base64
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST

View File

@ -38,7 +38,7 @@ def test_middleware_does_notstrip_content_type_from_other_methods():
assert "CONTENT_TYPE" in mock.call_args[0][0] assert "CONTENT_TYPE" in mock.call_args[0][0]
def test_get_outputs_endpoint_with_content_type(client, user_pk): def test_get_outputs_endpoint_with_content_type(client, user_pk, _bdb):
res = client.get( res = client.get(
OUTPUTS_ENDPOINT + "?public_key={}".format(user_pk), headers=[("Content-Type", "application/json")] OUTPUTS_ENDPOINT + "?public_key={}".format(user_pk), headers=[("Content-Type", "application/json")]
) )

View File

@ -18,7 +18,7 @@ def test_get_outputs_endpoint(client, user_pk):
m = MagicMock() m = MagicMock()
m.txid = "a" m.txid = "a"
m.output = 0 m.output = 0
with patch("planetmint.model.models.Models.get_outputs_filtered") as gof: with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
gof.return_value = [m, m] gof.return_value = [m, m]
res = client.get(OUTPUTS_ENDPOINT + "?public_key={}".format(user_pk)) res = client.get(OUTPUTS_ENDPOINT + "?public_key={}".format(user_pk))
assert res.json == [{"transaction_id": "a", "output_index": 0}, {"transaction_id": "a", "output_index": 0}] assert res.json == [{"transaction_id": "a", "output_index": 0}, {"transaction_id": "a", "output_index": 0}]
@ -30,7 +30,7 @@ def test_get_outputs_endpoint_unspent(client, user_pk):
m = MagicMock() m = MagicMock()
m.txid = "a" m.txid = "a"
m.output = 0 m.output = 0
with patch("planetmint.model.models.Models.get_outputs_filtered") as gof: with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
gof.return_value = [m] gof.return_value = [m]
params = "?spent=False&public_key={}".format(user_pk) params = "?spent=False&public_key={}".format(user_pk)
res = client.get(OUTPUTS_ENDPOINT + params) res = client.get(OUTPUTS_ENDPOINT + params)
@ -45,7 +45,7 @@ def test_get_outputs_endpoint_spent(client, user_pk):
m = MagicMock() m = MagicMock()
m.txid = "a" m.txid = "a"
m.output = 0 m.output = 0
with patch("planetmint.model.models.Models.get_outputs_filtered") as gof: with patch("planetmint.model.dataaccessor.DataAccessor.get_outputs_filtered") as gof:
gof.return_value = [m] gof.return_value = [m]
params = "?spent=true&public_key={}".format(user_pk) params = "?spent=true&public_key={}".format(user_pk)
res = client.get(OUTPUTS_ENDPOINT + params) res = client.get(OUTPUTS_ENDPOINT + params)

View File

@ -40,7 +40,7 @@ def test_get_transaction_endpoint(client, posted_create_tx):
assert res.status_code == 200 assert res.status_code == 200
def test_get_transaction_returns_404_if_not_found(client): def test_get_transaction_returns_404_if_not_found(client, b):
res = client.get(TX_ENDPOINT + "123") res = client.get(TX_ENDPOINT + "123")
assert res.status_code == 404 assert res.status_code == 404
@ -404,7 +404,7 @@ def test_transactions_get_list_good(client):
asset_ids = ["1" * 64] asset_ids = ["1" * 64]
with patch("planetmint.model.models.Models.get_transactions_filtered", get_txs_patched): with patch("planetmint.model.dataaccessor.DataAccessor.get_transactions_filtered", get_txs_patched):
url = TX_ENDPOINT + "?asset_ids=" + ",".join(asset_ids) url = TX_ENDPOINT + "?asset_ids=" + ",".join(asset_ids)
assert client.get(url).json == [ assert client.get(url).json == [
["asset_ids", asset_ids], ["asset_ids", asset_ids],
@ -430,7 +430,7 @@ def test_transactions_get_list_bad(client):
assert False assert False
with patch( with patch(
"planetmint.model.models.Models.get_transactions_filtered", "planetmint.model.dataaccessor.DataAccessor.get_transactions_filtered",
lambda *_, **__: should_not_be_called(), lambda *_, **__: should_not_be_called(),
): ):
# Test asset id validated # Test asset id validated