diff --git a/docs/root/generate_http_server_api_documentation.py b/docs/root/generate_http_server_api_documentation.py index 400fcf7..a406a80 100644 --- a/docs/root/generate_http_server_api_documentation.py +++ b/docs/root/generate_http_server_api_documentation.py @@ -11,6 +11,8 @@ import os.path from transactions.common.input import Input from transactions.common.transaction_link import TransactionLink + +import planetmint.abci.block from planetmint import lib from transactions.types.assets.create import Create from transactions.types.assets.transfer import Transfer @@ -210,7 +212,7 @@ def main(): signature = "53wxrEQDYk1dXzmvNSytbCfmNVnPqPkDQaTnAe8Jf43s6ssejPxezkCvUnGTnduNUmaLjhaan1iRLi3peu6s5DzA" app_hash = "f6e0c49c6d94d6924351f25bb334cf2a99af4206339bf784e741d1a5ab599056" - block = lib.Block(height=1, transactions=[tx.to_dict()], app_hash=app_hash) + block = planetmint.abci.block.Block(height=1, transactions=[tx.to_dict()], app_hash=app_hash) block_dict = block._asdict() block_dict.pop("app_hash") ctx["block"] = pretty_json(block_dict) diff --git a/planetmint/abci/block.py b/planetmint/abci/block.py new file mode 100644 index 0000000..1ce1984 --- /dev/null +++ b/planetmint/abci/block.py @@ -0,0 +1,3 @@ +from collections import namedtuple + +Block = namedtuple("Block", ("app_hash", "height", "transactions")) diff --git a/planetmint/abci/core.py b/planetmint/abci/core.py index a36066a..c63b525 100644 --- a/planetmint/abci/core.py +++ b/planetmint/abci/core.py @@ -23,7 +23,7 @@ from tendermint.abci.types_pb2 import ( ) from planetmint import Planetmint from planetmint.abci.tendermint_utils import decode_transaction, calculate_hash, decode_validator -from planetmint.lib import Block +from planetmint.abci.block import Block from planetmint.ipc.events import EventTypes, Event diff --git a/planetmint/abci/utils.py b/planetmint/abci/utils.py new file mode 100644 index 0000000..bc3deca --- /dev/null +++ b/planetmint/abci/utils.py @@ -0,0 +1,35 @@ +import json + +from packaging import version +from transactions.common.crypto import key_pair_from_ed25519_key + +from planetmint.abci.tendermint_utils import key_from_base64 +from planetmint.version import __tm_supported_versions__ + + +def load_node_key(path): + with open(path) as json_data: + priv_validator = json.load(json_data) + priv_key = priv_validator["priv_key"]["value"] + hex_private_key = key_from_base64(priv_key) + return key_pair_from_ed25519_key(hex_private_key) + + +def tendermint_version_is_compatible(running_tm_ver): + """ + Check Tendermint compatability with Planetmint server + + :param running_tm_ver: Version number of the connected Tendermint instance + :type running_tm_ver: str + :return: True/False depending on the compatability with Planetmint server + :rtype: bool + """ + + # Splitting because version can look like this e.g. 0.22.8-40d6dc2e + tm_ver = running_tm_ver.split("-") + if not tm_ver: + return False + for ver in __tm_supported_versions__: + if version.parse(ver) == version.parse(tm_ver[0]): + return True + return False diff --git a/planetmint/commands/planetmint.py b/planetmint/commands/planetmint.py index 9bac9a7..e41c05d 100644 --- a/planetmint/commands/planetmint.py +++ b/planetmint/commands/planetmint.py @@ -15,7 +15,7 @@ import sys import planetmint from planetmint.abci.core import rollback -from planetmint.utils import load_node_key +from planetmint.abci.utils import load_node_key from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT from transactions.common.exceptions import DatabaseDoesNotExist, ValidationError from transactions.types.elections.vote import Vote @@ -26,7 +26,7 @@ from planetmint import ValidatorElection, Planetmint from planetmint.backend import schema from planetmint.commands import utils from planetmint.commands.utils import configure_planetmint, input_on_stderr -from planetmint.log import setup_logging +from planetmint.config_utils import setup_logging from planetmint.abci.tendermint_utils import public_key_from_base64 from planetmint.commands.election_types import elections from planetmint.version import __tm_supported_versions__ diff --git a/planetmint/config_utils.py b/planetmint/config_utils.py index 0945157..f523476 100644 --- a/planetmint/config_utils.py +++ b/planetmint/config_utils.py @@ -23,8 +23,12 @@ import logging import collections.abc from functools import lru_cache +from logging.config import dictConfig as set_logging_config + from pkg_resources import iter_entry_points, ResolutionError -from planetmint.config import Config +from transactions.common.exceptions import ConfigurationError + +from planetmint.config import Config, DEFAULT_LOGGING_CONFIG from planetmint.validation import BaseValidationRules from transactions.common import exceptions @@ -306,3 +310,69 @@ def load_events_plugins(names=None): plugins.append((name, entry_point.load())) return plugins + + +def _normalize_log_level(level): + try: + return level.upper() + except AttributeError as exc: + raise ConfigurationError("Log level must be a string!") from exc + + +def setup_logging(): + """Function to configure log handlers. + .. important:: + + Configuration, if needed, should be applied before invoking this + decorator, as starting the subscriber process for logging will + configure the root logger for the child process based on the + state of :obj:`planetmint.config` at the moment this decorator + is invoked. + + """ + + logging_configs = DEFAULT_LOGGING_CONFIG + new_logging_configs = Config().get()["log"] + + if "file" in new_logging_configs: + filename = new_logging_configs["file"] + logging_configs["handlers"]["file"]["filename"] = filename + + if "error_file" in new_logging_configs: + error_filename = new_logging_configs["error_file"] + logging_configs["handlers"]["errors"]["filename"] = error_filename + + if "level_console" in new_logging_configs: + level = _normalize_log_level(new_logging_configs["level_console"]) + logging_configs["handlers"]["console"]["level"] = level + + if "level_logfile" in new_logging_configs: + level = _normalize_log_level(new_logging_configs["level_logfile"]) + logging_configs["handlers"]["file"]["level"] = level + + if "fmt_console" in new_logging_configs: + fmt = new_logging_configs["fmt_console"] + logging_configs["formatters"]["console"]["format"] = fmt + + if "fmt_logfile" in new_logging_configs: + fmt = new_logging_configs["fmt_logfile"] + logging_configs["formatters"]["file"]["format"] = fmt + + if "datefmt_console" in new_logging_configs: + fmt = new_logging_configs["datefmt_console"] + logging_configs["formatters"]["console"]["datefmt"] = fmt + + if "datefmt_logfile" in new_logging_configs: + fmt = new_logging_configs["datefmt_logfile"] + logging_configs["formatters"]["file"]["datefmt"] = fmt + + log_levels = new_logging_configs.get("granular_levels", {}) + + for logger_name, level in log_levels.items(): + level = _normalize_log_level(level) + try: + logging_configs["loggers"][logger_name]["level"] = level + except KeyError: + logging_configs["loggers"][logger_name] = {"level": level} + + set_logging_config(logging_configs) diff --git a/planetmint/fastquery.py b/planetmint/fastquery.py index 0baba95..b8ef775 100644 --- a/planetmint/fastquery.py +++ b/planetmint/fastquery.py @@ -3,10 +3,11 @@ # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # Code is Apache-2.0 and docs are CC-BY-4.0 -from planetmint.utils import condition_details_has_owner from planetmint.backend import query from transactions.common.transaction import TransactionLink +from planetmint.backend.models.output import ConditionDetails + class FastQuery: """Database queries that join on block results from a single node.""" @@ -45,3 +46,31 @@ class FastQuery: txs = query.get_spending_transactions(self.connection, links) spends = {TransactionLink.from_dict(input.fulfills.to_dict()) for tx in txs for input in tx.inputs} return [ff for ff in outputs if ff in spends] + + +# TODO: Rename this function, it's handling fulfillments not conditions +def condition_details_has_owner(condition_details, owner): + """Check if the public_key of owner is in the condition details + as an Ed25519Fulfillment.public_key + + Args: + condition_details (dict): dict with condition details + owner (str): base58 public key of owner + + Returns: + bool: True if the public key is found in the condition details, False otherwise + + """ + if isinstance(condition_details, ConditionDetails) and condition_details.sub_conditions is not None: + result = condition_details_has_owner(condition_details.sub_conditions, owner) + if result: + return True + elif isinstance(condition_details, list): + for subcondition in condition_details: + result = condition_details_has_owner(subcondition, owner) + if result: + return True + else: + if condition_details.public_key is not None and owner == condition_details.public_key: + return True + return False diff --git a/planetmint/lib.py b/planetmint/lib.py index a98f002..be38d9d 100644 --- a/planetmint/lib.py +++ b/planetmint/lib.py @@ -15,7 +15,7 @@ import rapidjson import requests from itertools import chain -from collections import namedtuple, OrderedDict +from collections import OrderedDict from uuid import uuid4 from hashlib import sha3_256 from transactions import Transaction, Vote @@ -948,5 +948,3 @@ class Planetmint(object): self.store_validator_set(new_height + 1, updated_validator_set) return encode_validator(election.assets[0].data) - -Block = namedtuple("Block", ("app_hash", "height", "transactions")) diff --git a/planetmint/log.py b/planetmint/log.py index 07d95f8..a77e2cd 100644 --- a/planetmint/log.py +++ b/planetmint/log.py @@ -3,73 +3,4 @@ # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # Code is Apache-2.0 and docs are CC-BY-4.0 -from transactions.common.exceptions import ConfigurationError -from logging.config import dictConfig as set_logging_config -from planetmint.config import Config, DEFAULT_LOGGING_CONFIG - -def _normalize_log_level(level): - try: - return level.upper() - except AttributeError as exc: - raise ConfigurationError("Log level must be a string!") from exc - - -def setup_logging(): - """Function to configure log hadlers. - - .. important:: - - Configuration, if needed, should be applied before invoking this - decorator, as starting the subscriber process for logging will - configure the root logger for the child process based on the - state of :obj:`planetmint.config` at the moment this decorator - is invoked. - - """ - - logging_configs = DEFAULT_LOGGING_CONFIG - new_logging_configs = Config().get()["log"] - - if "file" in new_logging_configs: - filename = new_logging_configs["file"] - logging_configs["handlers"]["file"]["filename"] = filename - - if "error_file" in new_logging_configs: - error_filename = new_logging_configs["error_file"] - logging_configs["handlers"]["errors"]["filename"] = error_filename - - if "level_console" in new_logging_configs: - level = _normalize_log_level(new_logging_configs["level_console"]) - logging_configs["handlers"]["console"]["level"] = level - - if "level_logfile" in new_logging_configs: - level = _normalize_log_level(new_logging_configs["level_logfile"]) - logging_configs["handlers"]["file"]["level"] = level - - if "fmt_console" in new_logging_configs: - fmt = new_logging_configs["fmt_console"] - logging_configs["formatters"]["console"]["format"] = fmt - - if "fmt_logfile" in new_logging_configs: - fmt = new_logging_configs["fmt_logfile"] - logging_configs["formatters"]["file"]["format"] = fmt - - if "datefmt_console" in new_logging_configs: - fmt = new_logging_configs["datefmt_console"] - logging_configs["formatters"]["console"]["datefmt"] = fmt - - if "datefmt_logfile" in new_logging_configs: - fmt = new_logging_configs["datefmt_logfile"] - logging_configs["formatters"]["file"]["datefmt"] = fmt - - log_levels = new_logging_configs.get("granular_levels", {}) - - for logger_name, level in log_levels.items(): - level = _normalize_log_level(level) - try: - logging_configs["loggers"][logger_name]["level"] = level - except KeyError: - logging_configs["loggers"][logger_name] = {"level": level} - - set_logging_config(logging_configs) diff --git a/planetmint/utils.py b/planetmint/utils.py index 436e682..c4522ab 100644 --- a/planetmint/utils.py +++ b/planetmint/utils.py @@ -7,15 +7,8 @@ import contextlib import threading import queue import multiprocessing -import json import setproctitle -from packaging import version -from planetmint.version import __tm_supported_versions__ -from planetmint.abci.tendermint_utils import key_from_base64 -from planetmint.backend.models.output import ConditionDetails -from transactions.common.crypto import key_pair_from_ed25519_key - class ProcessGroup(object): def __init__(self, concurrency=None, group=None, target=None, name=None, args=None, kwargs=None, daemon=None): @@ -108,34 +101,6 @@ def pool(builder, size, timeout=None): return pooled -# TODO: Rename this function, it's handling fulfillments not conditions -def condition_details_has_owner(condition_details, owner): - """Check if the public_key of owner is in the condition details - as an Ed25519Fulfillment.public_key - - Args: - condition_details (dict): dict with condition details - owner (str): base58 public key of owner - - Returns: - bool: True if the public key is found in the condition details, False otherwise - - """ - if isinstance(condition_details, ConditionDetails) and condition_details.sub_conditions is not None: - result = condition_details_has_owner(condition_details.sub_conditions, owner) - if result: - return True - elif isinstance(condition_details, list): - for subcondition in condition_details: - result = condition_details_has_owner(subcondition, owner) - if result: - return True - else: - if condition_details.public_key is not None and owner == condition_details.public_key: - return True - return False - - class Lazy: """Lazy objects are useful to create chains of methods to execute later. @@ -181,31 +146,3 @@ class Lazy: self.stack = [] return last - -# Load Tendermint's public and private key from the file path -def load_node_key(path): - with open(path) as json_data: - priv_validator = json.load(json_data) - priv_key = priv_validator["priv_key"]["value"] - hex_private_key = key_from_base64(priv_key) - return key_pair_from_ed25519_key(hex_private_key) - - -def tendermint_version_is_compatible(running_tm_ver): - """ - Check Tendermint compatability with Planetmint server - - :param running_tm_ver: Version number of the connected Tendermint instance - :type running_tm_ver: str - :return: True/False depending on the compatability with Planetmint server - :rtype: bool - """ - - # Splitting because version can look like this e.g. 0.22.8-40d6dc2e - tm_ver = running_tm_ver.split("-") - if not tm_ver: - return False - for ver in __tm_supported_versions__: - if version.parse(ver) == version.parse(tm_ver[0]): - return True - return False diff --git a/tests/backend/tarantool/test_queries.py b/tests/backend/tarantool/test_queries.py index 982bf5d..4b68ed3 100644 --- a/tests/backend/tarantool/test_queries.py +++ b/tests/backend/tarantool/test_queries.py @@ -48,7 +48,7 @@ def test_get_owned_ids(signed_create_tx, user_pk, db_conn): def test_store_block(db_conn): - from planetmint.lib import Block + from planetmint.abci.block import Block from planetmint.backend.tarantool import query block = Block(app_hash="random_utxo", height=3, transactions=[]) @@ -59,7 +59,7 @@ def test_store_block(db_conn): def test_get_block(db_conn): - from planetmint.lib import Block + from planetmint.abci.block import Block from planetmint.backend.tarantool import query block = Block(app_hash="random_utxo", height=3, transactions=[]) diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index 3552b6e..8c2529a 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -63,7 +63,7 @@ def run_start_args(request): @pytest.fixture def mocked_setup_logging(mocker): return mocker.patch( - "planetmint.log.setup_logging", + "planetmint.config_utils.setup_logging", autospec=True, spec_set=True, ) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 3252b1f..e9bdd2d 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -14,7 +14,7 @@ from planetmint import ValidatorElection from planetmint.commands.planetmint import run_election_show from planetmint.commands.planetmint import run_election_new_chain_migration from planetmint.backend.connection import Connection -from planetmint.lib import Block +from planetmint.abci.block import Block from transactions.types.elections.chain_migration_election import ChainMigrationElection from tests.utils import generate_election, generate_validators @@ -62,7 +62,7 @@ def test_main_entrypoint(mock_start): assert mock_start.called -@patch("planetmint.log.setup_logging") +@patch("planetmint.config_utils.setup_logging") @patch("planetmint.commands.planetmint._run_init") @patch("planetmint.config_utils.autoconfigure") def test_bigchain_run_start(mock_setup_logging, mock_run_init, mock_autoconfigure, mock_processes_start): @@ -261,7 +261,7 @@ def test_recover_db_on_start(mock_run_recover, mock_start, mocked_setup_logging) def test_run_recover(b, alice, bob): from planetmint.commands.planetmint import run_recover from transactions.types.assets.create import Create - from planetmint.lib import Block + from planetmint.abci.block import Block from planetmint.backend import query tx1 = Create.generate( diff --git a/tests/conftest.py b/tests/conftest.py index 9a092e2..249502c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,7 @@ from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT from planetmint.abci.tendermint_utils import key_from_base64 from planetmint.backend import schema, query from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key -from planetmint.lib import Block +from planetmint.abci.block import Block from tests.utils import gen_vote from planetmint.config import Config from transactions.types.elections.validator_election import ValidatorElection # noqa diff --git a/tests/elections/test_election.py b/tests/elections/test_election.py index 7a00bdf..2439b53 100644 --- a/tests/elections/test_election.py +++ b/tests/elections/test_election.py @@ -1,7 +1,7 @@ import pytest from tests.utils import generate_election, generate_validators -from planetmint.lib import Block +from planetmint.abci.block import Block from transactions.types.elections.election import Election from transactions.types.elections.chain_migration_election import ChainMigrationElection from transactions.types.elections.validator_election import ValidatorElection diff --git a/tests/tendermint/test_core.py b/tests/tendermint/test_core.py index 6a57646..46ee438 100644 --- a/tests/tendermint/test_core.py +++ b/tests/tendermint/test_core.py @@ -18,7 +18,7 @@ from transactions.types.assets.transfer import Transfer from planetmint import App from planetmint.backend import query from planetmint.abci.core import OkCode, CodeTypeError, rollback -from planetmint.lib import Block +from planetmint.abci.block import Block from planetmint.abci.tendermint_utils import new_validator_set from planetmint.abci.tendermint_utils import public_key_to_base64 from planetmint.version import __tm_supported_versions__ diff --git a/tests/tendermint/test_lib.py b/tests/tendermint/test_lib.py index 6216768..af80257 100644 --- a/tests/tendermint/test_lib.py +++ b/tests/tendermint/test_lib.py @@ -17,7 +17,7 @@ from transactions.common.transaction_mode_types import ( BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC, ) -from planetmint.lib import Block +from planetmint.abci.block import Block from ipld import marshal, multihash from uuid import uuid4 @@ -72,7 +72,7 @@ def test_asset_is_separated_from_transaciton(b): @pytest.mark.bdb def test_get_latest_block(b): - from planetmint.lib import Block + from planetmint.abci.block import Block for i in range(10): app_hash = os.urandom(16).hex() diff --git a/tests/web/test_block_tendermint.py b/tests/web/test_block_tendermint.py index 56a7fe4..c051a96 100644 --- a/tests/web/test_block_tendermint.py +++ b/tests/web/test_block_tendermint.py @@ -6,7 +6,7 @@ import pytest from transactions.types.assets.create import Create -from planetmint.lib import Block +from planetmint.abci.block import Block from ipld import marshal, multihash BLOCKS_ENDPOINT = "/api/v1/blocks/"