bigchaindb/bigchaindb/utils.py

210 lines
6.2 KiB
Python
Raw Normal View History

# Copyright © 2020 Interplanetary Database Association e.V.,
# BigchainDB 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
2016-04-14 18:55:09 +02:00
import contextlib
import threading
import queue
import multiprocessing as mp
import json
2016-02-10 19:55:33 +01:00
import setproctitle
from packaging import version
from bigchaindb.version import __tm_supported_versions__
from bigchaindb.tendermint_utils import key_from_base64
from bigchaindb.common.crypto import key_pair_from_ed25519_key
2016-02-10 19:55:33 +01:00
class ProcessGroup(object):
def __init__(self, concurrency=None, group=None, target=None, name=None,
args=None, kwargs=None, daemon=None):
self.concurrency = concurrency or mp.cpu_count()
self.group = group
self.target = target
self.name = name
self.args = args or ()
self.kwargs = kwargs or {}
self.daemon = daemon
self.processes = []
def start(self):
for i in range(self.concurrency):
proc = mp.Process(group=self.group, target=self.target,
name=self.name, args=self.args,
kwargs=self.kwargs, daemon=self.daemon)
proc.start()
self.processes.append(proc)
2016-02-22 23:46:32 +01:00
class Process(mp.Process):
"""Wrapper around multiprocessing.Process that uses
setproctitle to set the name of the process when running
the target task.
"""
def run(self):
setproctitle.setproctitle(self.name)
super().run()
2016-04-14 18:55:09 +02:00
# Inspired by:
# - http://stackoverflow.com/a/24741694/597097
def pool(builder, size, timeout=None):
"""Create a pool that imposes a limit on the number of stored
instances.
Args:
builder: a function to build an instance.
size: the size of the pool.
2016-04-18 12:02:05 +02:00
timeout(Optional[float]): the seconds to wait before raising
a ``queue.Empty`` exception if no instances are available
within that time.
Raises:
If ``timeout`` is defined but the request is taking longer
than the specified time, the context manager will raise
a ``queue.Empty`` exception.
Returns:
A context manager that can be used with the ``with``
statement.
2016-04-18 12:02:05 +02:00
"""
2016-04-14 18:55:09 +02:00
lock = threading.Lock()
local_pool = queue.Queue()
current_size = 0
2016-04-14 18:55:09 +02:00
@contextlib.contextmanager
def pooled():
nonlocal current_size
instance = None
# If we still have free slots, then we have room to create new
# instances.
if current_size < size:
2016-04-14 18:55:09 +02:00
with lock:
# We need to check again if we have slots available, since
# the situation might be different after acquiring the lock
if current_size < size:
current_size += 1
2016-04-14 18:55:09 +02:00
instance = builder()
# Watchout: current_size can be equal to size if the previous part of
# the function has been executed, that's why we need to check if the
# instance is None.
2016-04-18 12:02:05 +02:00
if instance is None:
instance = local_pool.get(timeout=timeout)
2016-04-14 18:55:09 +02:00
yield instance
2016-04-14 18:55:09 +02:00
local_pool.put(instance)
return pooled
Rebase/feat/586/integrate tx model (#641) * Adjust imports to bigchaindb_common * Adjust get_spent function signature * Adjust block serialization * Fix BigchainApi Test * Fix TestTransactionValidation tests * Fix TestBlockValidation tests * WIP: TestMultipleInputs * Adjust tests to tx-model interface changes - Fix old tests - Fix tests in TestMultipleInputs class * Remove fulfillment message tests * Fix TransactionMalleability tests * Remove Cryptoconditions tests * Remove create_transaction * Remove signing logic * Remove consensus plugin * Fix block_creation pipeline * Fix election pipeline * Replace some util functions with bdb_common ones - timestamp ==> gen_timestamp - serialize. * Implement Block model * Simplify function signatures for vote functions Change parameter interface for the following functions: - has_previous_vote - verify_vote_signature - block_election_status so that they take a block's id and voters instead of a fake block. * Integrate Block and Transaction model * Fix leftover tests and cleanup conftest * Add bigchaindb-common to install_requires * Delete transactions after block is written (#609) * delete transactions after block is written * cleanup transaction_exists * check for duplicate transactions * delete invalid tx from backlog * test duplicate transaction * Remove dead code * Test processes.py * Test invalid tx in on server * Fix tests for core.py * Fix models tests * Test commands main fn * Add final coverage to vote pipeline * Add more tests to voting pipeline * Remove consensus plugin docs and misc * Post rebase fixes * Fix rebase mess * Remove extra blank line * Improve docstring * Remove comment handled in bigchaindb/cryptoconditions#27; see https://github.com/bigchaindb/cryptoconditions/issues/27 * Fix block serialization in block creation * Add signed_ prefix to transfer_tx * Improve docs * Add library documentation page on pipelines * PR feedback for models.py * Impr. readability of get_last_voted_block * Use dict comprehension * Add docker-compose file to build and serve docs locally for development purposes * Change private_key for signing_key * Improve docstrings * Remove consensus docs * Document new consensus module * Create different transactions for the block * Cleanup variable names in block.py * Create different transactions for the block * Cleanup variable names in block.py
2016-09-29 10:29:41 +02:00
# TODO: Rename this function, it's handling fulfillments not conditions
2016-05-25 16:40:47 +02:00
def condition_details_has_owner(condition_details, owner):
"""Check if the public_key of owner is in the condition details
2016-05-25 16:40:47 +02:00
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 'subconditions' in condition_details:
result = condition_details_has_owner(condition_details['subconditions'], owner)
2016-05-25 16:40:47 +02:00
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 'public_key' in condition_details \
and owner == condition_details['public_key']:
return True
return False
2017-01-25 19:05:48 +01:00
class Lazy:
2017-01-27 14:35:37 +01:00
"""Lazy objects are useful to create chains of methods to
execute later.
A lazy object records the methods that has been called, and
replay them when the :py:meth:`run` method is called. Note that
:py:meth:`run` needs an object `instance` to replay all the
methods that have been recorded.
"""
2017-01-25 19:05:48 +01:00
def __init__(self):
2017-01-27 14:35:37 +01:00
"""Instantiate a new Lazy object."""
2017-01-25 19:05:48 +01:00
self.stack = []
def __getattr__(self, name):
self.stack.append(name)
return self
def __call__(self, *args, **kwargs):
self.stack.append((args, kwargs))
return self
def __getitem__(self, key):
self.stack.append('__getitem__')
self.stack.append(([key], {}))
return self
def run(self, instance):
2017-01-27 14:35:37 +01:00
"""Run the recorded chain of methods on `instance`.
Args:
instance: an object.
"""
2017-01-25 19:05:48 +01:00
last = instance
2017-01-26 17:12:35 +01:00
for item in self.stack:
if isinstance(item, str):
last = getattr(last, item)
else:
last = last(*item[0], **item[1])
2017-01-25 19:05:48 +01:00
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 BigchainDB 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 BigchainDB 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