diff --git a/benchmarking-tests/benchmark_utils.py b/benchmarking-tests/benchmark_utils.py index ce462bdf..86242753 100644 --- a/benchmarking-tests/benchmark_utils.py +++ b/benchmarking-tests/benchmark_utils.py @@ -8,7 +8,7 @@ import logging import rethinkdb as r from os.path import expanduser -from bigchaindb.common.transaction import Transaction +from bigchaindb_common.transaction import Transaction from bigchaindb import Bigchain from bigchaindb.util import ProcessGroup diff --git a/bigchaindb/commands/bigchain.py b/bigchaindb/commands/bigchain.py index 40891416..cd5ce6db 100644 --- a/bigchaindb/commands/bigchain.py +++ b/bigchaindb/commands/bigchain.py @@ -13,8 +13,8 @@ import builtins import logstats -from bigchaindb.common import crypto -from bigchaindb.common.exceptions import (StartupError, +from bigchaindb_common import crypto +from bigchaindb_common.exceptions import (StartupError, DatabaseAlreadyExists, KeypairNotFoundException) import rethinkdb as r diff --git a/bigchaindb/commands/utils.py b/bigchaindb/commands/utils.py index 65459737..1391f18f 100644 --- a/bigchaindb/commands/utils.py +++ b/bigchaindb/commands/utils.py @@ -3,7 +3,7 @@ for ``argparse.ArgumentParser``. """ import argparse -from bigchaindb.common.exceptions import StartupError +from bigchaindb_common.exceptions import StartupError import multiprocessing as mp import subprocess @@ -25,7 +25,7 @@ def start_rethinkdb(): starting the db Raises: - ``bigchaindb.common.exceptions.StartupError`` if RethinkDB cannot + ``bigchaindb_common.exceptions.StartupError`` if RethinkDB cannot be started. """ diff --git a/bigchaindb/common/crypto.py b/bigchaindb/common/crypto.py deleted file mode 100644 index e440f81d..00000000 --- a/bigchaindb/common/crypto.py +++ /dev/null @@ -1,18 +0,0 @@ -# Separate all crypto code so that we can easily test several implementations - -import sha3 -from cryptoconditions import crypto - - -def hash_data(data): - """Hash the provided data using SHA3-256""" - return sha3.sha3_256(data.encode()).hexdigest() - - -def generate_key_pair(): - # TODO FOR CC: Adjust interface so that this function becomes unnecessary - private_key, public_key = crypto.ed25519_generate_key_pair() - return private_key.decode(), public_key.decode() - -SigningKey = crypto.Ed25519SigningKey -VerifyingKey = crypto.Ed25519VerifyingKey diff --git a/bigchaindb/common/exceptions.py b/bigchaindb/common/exceptions.py deleted file mode 100644 index 2e1dc670..00000000 --- a/bigchaindb/common/exceptions.py +++ /dev/null @@ -1,82 +0,0 @@ -"""Custom exceptions used in the `bigchaindb` package. -""" - - -class ConfigurationError(Exception): - """Raised when there is a problem with server configuration""" - - -class OperationError(Exception): - """Raised when an operation cannot go through""" - - -class TransactionDoesNotExist(Exception): - """Raised if the transaction is not in the database""" - - -class TransactionOwnerError(Exception): - """Raised if a user tries to transfer a transaction they don't own""" - - -class DoubleSpend(Exception): - """Raised if a double spend is found""" - - -class InvalidHash(Exception): - """Raised if there was an error checking the hash for a particular - operation""" - - -class InvalidSignature(Exception): - """Raised if there was an error checking the signature for a particular - operation""" - - -class DatabaseAlreadyExists(Exception): - """Raised when trying to create the database but the db is already there""" - - -class DatabaseDoesNotExist(Exception): - """Raised when trying to delete the database but the db is not there""" - - -class KeypairNotFoundException(Exception): - """Raised if operation cannot proceed because the keypair was not given""" - - -class KeypairMismatchException(Exception): - """Raised if the private key(s) provided for signing don't match any of the - current owner(s)""" - - -class StartupError(Exception): - """Raised when there is an error starting up the system""" - - -class ImproperVoteError(Exception): - """Raised if a vote is not constructed correctly, or signed incorrectly""" - - -class MultipleVotesError(Exception): - """Raised if a voter has voted more than once""" - - -class GenesisBlockAlreadyExistsError(Exception): - """Raised when trying to create the already existing genesis block""" - - -class CyclicBlockchainError(Exception): - """Raised when there is a cycle in the blockchain""" - - -class FulfillmentNotInValidBlock(Exception): - """Raised when a transaction depends on an invalid or undecided - fulfillment""" - - -class AssetIdMismatch(Exception): - """Raised when multiple transaction inputs related to different assets""" - - -class AmountError(Exception): - """Raised when the amount of a non-divisible asset is different then 1""" diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py deleted file mode 100644 index 41d8a30c..00000000 --- a/bigchaindb/common/transaction.py +++ /dev/null @@ -1,1246 +0,0 @@ -from copy import deepcopy -from functools import reduce -from uuid import uuid4 - -from cryptoconditions import (Fulfillment as CCFulfillment, - ThresholdSha256Fulfillment, Ed25519Fulfillment, - PreimageSha256Fulfillment) -from cryptoconditions.exceptions import ParsingError - -from bigchaindb.common.crypto import SigningKey, hash_data -from bigchaindb.common.exceptions import (KeypairMismatchException, - InvalidHash, InvalidSignature) -from bigchaindb.common.util import serialize, gen_timestamp - - -class Fulfillment(object): - """A Fulfillment is used to spend assets locked by a Condition. - - Attributes: - fulfillment (:class:`cryptoconditions.Fulfillment`): A Fulfillment - to be signed with a private key. - owners_before (:obj:`list` of :obj:`str`): A list of owners after a - Transaction was confirmed. - tx_input (:class:`~bigchaindb.common.transaction. TransactionLink`, - optional): A link representing the input of a `TRANSFER` - Transaction. - """ - - def __init__(self, fulfillment, owners_before, tx_input=None): - """Fulfillment shims a Cryptocondition Fulfillment for BigchainDB. - - Args: - fulfillment (:class:`cryptoconditions.Fulfillment`): A - Fulfillment to be signed with a private key. - owners_before (:obj:`list` of :obj:`str`): A list of owners - after a Transaction was confirmed. - tx_input (:class:`~bigchaindb.common.transaction. - TransactionLink`, optional): A link representing the input - of a `TRANSFER` Transaction. - """ - self.fulfillment = fulfillment - - if tx_input is not None and not isinstance(tx_input, TransactionLink): - raise TypeError('`tx_input` must be a TransactionLink instance') - else: - self.tx_input = tx_input - - if not isinstance(owners_before, list): - raise TypeError('`owners_after` must be a list instance') - else: - self.owners_before = owners_before - - def __eq__(self, other): - # TODO: If `other !== Fulfillment` return `False` - return self.to_dict() == other.to_dict() - - def to_dict(self, fid=None): - """Transforms the object to a Python dictionary. - - Note: - A `fid` can be submitted to be included in the dictionary - representation. - - If a Fulfillment hasn't been signed yet, this method returns a - dictionary representation. - - Args: - fid (int, optional): The Fulfillment's index in a Transaction. - - Returns: - dict: The Fulfillment as an alternative serialization format. - """ - try: - fulfillment = self.fulfillment.serialize_uri() - except (TypeError, AttributeError): - # NOTE: When a non-signed transaction is casted to a dict, - # `self.fulfillments` value is lost, as in the node's - # transaction model that is saved to the database, does not - # account for its dictionary form but just for its signed uri - # form. - # Hence, when a non-signed fulfillment is to be cast to a - # dict, we just call its internal `to_dict` method here and - # its `from_dict` method in `Fulfillment.from_dict`. - fulfillment = self.fulfillment.to_dict() - - try: - # NOTE: `self.tx_input` can be `None` and that's fine - tx_input = self.tx_input.to_dict() - except AttributeError: - tx_input = None - - ffill = { - 'owners_before': self.owners_before, - 'input': tx_input, - 'fulfillment': fulfillment, - } - if fid is not None: - ffill['fid'] = fid - return ffill - - @classmethod - def from_dict(cls, ffill): - """Transforms a Python dictionary to a Fulfillment object. - - Note: - Optionally, this method can also serialize a Cryptoconditions- - Fulfillment that is not yet signed. - - Args: - ffill (dict): The Fulfillment to be transformed. - - Returns: - :class:`~bigchaindb.common.transaction.Fulfillment` - - Raises: - InvalidSignature: If a Fulfillment's URI couldn't be parsed. - """ - try: - fulfillment = CCFulfillment.from_uri(ffill['fulfillment']) - except ValueError: - # TODO FOR CC: Throw an `InvalidSignature` error in this case. - raise InvalidSignature("Fulfillment URI couldn't been parsed") - except TypeError: - # NOTE: See comment about this special case in - # `Fulfillment.to_dict` - fulfillment = CCFulfillment.from_dict(ffill['fulfillment']) - input_ = TransactionLink.from_dict(ffill['input']) - return cls(fulfillment, ffill['owners_before'], input_) - - -class TransactionLink(object): - """An object for unidirectional linking to a Transaction's Condition. - - Attributes: - txid (str, optional): A Transaction to link to. - cid (int, optional): A Condition's index in a Transaction with id - `txid`. - """ - - def __init__(self, txid=None, cid=None): - """Used to point to a specific Condition of a Transaction. - - Note: - In an IPLD implementation, this class is not necessary anymore, - as an IPLD link can simply point to an object, as well as an - objects properties. So instead of having a (de)serializable - class, we can have a simple IPLD link of the form: - `//transaction/conditions//`. - - Args: - txid (str, optional): A Transaction to link to. - cid (int, optional): A Condition's index in a Transaction with - id `txid`. - """ - self.txid = txid - self.cid = cid - - def __bool__(self): - return self.txid is not None and self.cid is not None - - def __eq__(self, other): - # TODO: If `other !== TransactionLink` return `False` - return self.to_dict() == self.to_dict() - - @classmethod - def from_dict(cls, link): - """Transforms a Python dictionary to a TransactionLink object. - - Args: - link (dict): The link to be transformed. - - Returns: - :class:`~bigchaindb.common.transaction.TransactionLink` - """ - try: - return cls(link['txid'], link['cid']) - except TypeError: - return cls() - - def to_dict(self): - """Transforms the object to a Python dictionary. - - Returns: - (dict|None): The link as an alternative serialization format. - """ - if self.txid is None and self.cid is None: - return None - else: - return { - 'txid': self.txid, - 'cid': self.cid, - } - - -class Condition(object): - """A Condition is used to lock an asset. - - Attributes: - fulfillment (:class:`cryptoconditions.Fulfillment`): A Fulfillment - to extract a Condition from. - owners_after (:obj:`list` of :obj:`str`, optional): A list of - owners before a Transaction was confirmed. - """ - - def __init__(self, fulfillment, owners_after=None, amount=1): - """Condition shims a Cryptocondition condition for BigchainDB. - - Args: - fulfillment (:class:`cryptoconditions.Fulfillment`): A - Fulfillment to extract a Condition from. - owners_after (:obj:`list` of :obj:`str`, optional): A list of - owners before a Transaction was confirmed. - amount (int): The amount of Assets to be locked with this - Condition. - - Raises: - TypeError: if `owners_after` is not instance of `list`. - """ - self.fulfillment = fulfillment - # TODO: Not sure if we should validate for value here - self.amount = amount - - if not isinstance(owners_after, list) and owners_after is not None: - raise TypeError('`owners_after` must be a list instance or None') - else: - self.owners_after = owners_after - - def __eq__(self, other): - # TODO: If `other !== Condition` return `False` - return self.to_dict() == other.to_dict() - - def to_dict(self, cid=None): - """Transforms the object to a Python dictionary. - - Note: - A `cid` can be submitted to be included in the dictionary - representation. - - A dictionary serialization of the Fulfillment the Condition was - derived from is always provided. - - Args: - cid (int, optional): The Condition's index in a Transaction. - - Returns: - dict: The Condition as an alternative serialization format. - """ - # TODO FOR CC: It must be able to recognize a hashlock condition - # and fulfillment! - condition = {} - try: - condition['details'] = self.fulfillment.to_dict() - except AttributeError: - pass - - try: - condition['uri'] = self.fulfillment.condition_uri - except AttributeError: - condition['uri'] = self.fulfillment - - cond = { - 'owners_after': self.owners_after, - 'condition': condition, - 'amount': self.amount - } - if cid is not None: - cond['cid'] = cid - return cond - - @classmethod - def generate(cls, owners_after): - """Generates a Condition from a specifically formed tuple or list. - - Note: - If a ThresholdCondition has to be generated where the threshold - is always the number of subconditions it is split between, a - list of the following structure is sufficient: - - [(address|condition)*, [(address|condition)*, ...], ...] - - If however, the thresholds of individual threshold conditions - to be created have to be set specifically, a tuple of the - following structure is necessary: - - ([(address|condition)*, - ([(address|condition)*, ...], subthreshold), - ...], threshold) - - Args: - owners_after (:obj:`list` of :obj:`str`|tuple): The users that - should be able to fulfill the Condition that is being - created. - - Returns: - A Condition that can be used in a Transaction. - - Returns: - TypeError: If `owners_after` is not an instance of `list`. - TypeError: If `owners_after` is an empty list. - """ - # TODO: We probably want to remove the tuple logic for weights here - # again: - # github.com/bigchaindb/bigchaindb/issues/730#issuecomment-255144756 - if isinstance(owners_after, tuple): - owners_after, threshold = owners_after - else: - threshold = len(owners_after) - - if not isinstance(owners_after, list): - raise TypeError('`owners_after` must be an instance of list') - if len(owners_after) == 0: - raise ValueError('`owners_after` needs to contain at least one' - 'owner') - elif len(owners_after) == 1 and not isinstance(owners_after[0], list): - try: - ffill = Ed25519Fulfillment(public_key=owners_after[0]) - except TypeError: - ffill = owners_after[0] - return cls(ffill, owners_after) - else: - initial_cond = ThresholdSha256Fulfillment(threshold=threshold) - threshold_cond = reduce(cls._gen_condition, owners_after, - initial_cond) - return cls(threshold_cond, owners_after) - - @classmethod - def _gen_condition(cls, initial, current): - """Generates ThresholdSha256 conditions from a list of new owners. - - Note: - This method is intended only to be used with a reduce function. - For a description on how to use this method, see - `Condition.generate`. - - Args: - initial (:class:`cryptoconditions.ThresholdSha256Fulfillment`): - A Condition representing the overall root. - current (:obj:`list` of :obj:`str`|str): A list of new owners - or a single new owner. - - Returns: - :class:`cryptoconditions.ThresholdSha256Fulfillment`: - """ - if isinstance(current, tuple): - owners_after, threshold = current - else: - owners_after = current - try: - threshold = len(owners_after) - except TypeError: - threshold = None - - if isinstance(owners_after, list) and len(owners_after) > 1: - ffill = ThresholdSha256Fulfillment(threshold=threshold) - reduce(cls._gen_condition, owners_after, ffill) - elif isinstance(owners_after, list) and len(owners_after) <= 1: - raise ValueError('Sublist cannot contain single owner') - else: - try: - owners_after = owners_after.pop() - except AttributeError: - pass - try: - ffill = Ed25519Fulfillment(public_key=owners_after) - except TypeError: - # NOTE: Instead of submitting base58 encoded addresses, a user - # of this class can also submit fully instantiated - # Cryptoconditions. In the case of casting `owners_after` - # to a Ed25519Fulfillment with the result of a - # `TypeError`, we're assuming that `owners_after` is a - # Cryptocondition then. - ffill = owners_after - initial.add_subfulfillment(ffill) - return initial - - @classmethod - def from_dict(cls, cond): - """Transforms a Python dictionary to a Condition object. - - Note: - To pass a serialization cycle multiple times, a - Cryptoconditions Fulfillment needs to be present in the - passed-in dictionary, as Condition URIs are not serializable - anymore. - - Args: - cond (dict): The Condition to be transformed. - - Returns: - :class:`~bigchaindb.common.transaction.Condition` - """ - try: - fulfillment = CCFulfillment.from_dict(cond['condition']['details']) - except KeyError: - # NOTE: Hashlock condition case - fulfillment = cond['condition']['uri'] - return cls(fulfillment, cond['owners_after'], cond['amount']) - - -class Asset(object): - """An Asset is a fungible unit to spend and lock with Transactions. - - Note: - Currently, the following flags are not yet fully supported: - - `divisible` - - `updatable` - - `refillable` - - Attributes: - data (dict): A dictionary of data that can be added to an Asset. - data_id (str): A unique identifier of `data`'s content. - divisible (bool): A flag indicating if an Asset can be divided. - updatable (bool): A flag indicating if an Asset can be updated. - refillable (bool): A flag indicating if an Asset can be refilled. - """ - - def __init__(self, data=None, data_id=None, divisible=False, - updatable=False, refillable=False): - """An Asset is not required to contain any extra data from outside.""" - self.data = data - self.data_id = data_id if data_id is not None else self.to_hash() - self.divisible = divisible - self.updatable = updatable - self.refillable = refillable - - self._validate_asset() - - def __eq__(self, other): - try: - other_dict = other.to_dict() - except AttributeError: - return False - return self.to_dict() == other_dict - - def to_dict(self): - """Transforms the object to a Python dictionary. - - Returns: - (dict): The Asset object as an alternative serialization - format. - """ - return { - 'id': self.data_id, - 'divisible': self.divisible, - 'updatable': self.updatable, - 'refillable': self.refillable, - 'data': self.data, - } - - @classmethod - def from_dict(cls, asset): - """Transforms a Python dictionary to an Asset object. - - Args: - asset (dict): The dictionary to be serialized. - - Returns: - :class:`~bigchaindb.common.transaction.Asset` - """ - return cls(asset.get('data'), asset['id'], - asset.get('divisible', False), - asset.get('updatable', False), - asset.get('refillable', False)) - - def to_hash(self): - """Generates a unqiue uuid for an Asset""" - return str(uuid4()) - - def _validate_asset(self): - """Validates the asset""" - if self.data is not None and not isinstance(self.data, dict): - raise TypeError('`data` must be a dict instance or None') - if not isinstance(self.divisible, bool): - raise TypeError('`divisible` must be a boolean') - if not isinstance(self.refillable, bool): - raise TypeError('`refillable` must be a boolean') - if not isinstance(self.updatable, bool): - raise TypeError('`updatable` must be a boolean') - - -class Metadata(object): - """Metadata is used to store a dictionary and its hash in a Transaction.""" - - def __init__(self, data=None, data_id=None): - """Metadata stores a payload `data` as well as data's hash, `data_id`. - - Note: - When no `data_id` is provided, one is being generated by - this method. - - Args: - data (dict): A dictionary to be held by Metadata. - data_id (str): A hash corresponding to the contents of - `data`. - """ - # TODO: Rename `payload_id` to `id` - if data_id is not None: - self.data_id = data_id - else: - self.data_id = self.to_hash() - - if data is not None and not isinstance(data, dict): - raise TypeError('`data` must be a dict instance or None') - else: - self.data = data - - def __eq__(self, other): - # TODO: If `other !== Data` return `False` - return self.to_dict() == other.to_dict() - - @classmethod - def from_dict(cls, data): - """Transforms a Python dictionary to a Metadata object. - - Args: - data (dict): The dictionary to be serialized. - - Returns: - :class:`~bigchaindb.common.transaction.Metadata` - """ - try: - return cls(data['data'], data['id']) - except TypeError: - return cls() - - def to_dict(self): - """Transforms the object to a Python dictionary. - - Returns: - (dict|None): The Metadata object as an alternative - serialization format. - """ - if self.data is None: - return None - else: - return { - 'data': self.data, - 'id': self.data_id, - } - - def to_hash(self): - """A hash corresponding to the contents of `payload`.""" - return str(uuid4()) - - -class Transaction(object): - """A Transaction is used to create and transfer assets. - - Note: - For adding Fulfillments and Conditions, this class provides methods - to do so. - - Attributes: - operation (str): Defines the operation of the Transaction. - fulfillments (:obj:`list` of :class:`~bigchaindb.common. - transaction.Fulfillment`, optional): Define the assets to - spend. - conditions (:obj:`list` of :class:`~bigchaindb.common. - transaction.Condition`, optional): Define the assets to lock. - metadata (:class:`~bigchaindb.common.transaction.Metadata`): - Metadata to be stored along with the Transaction. - timestamp (int): Defines the time a Transaction was created. - version (int): Defines the version number of a Transaction. - """ - CREATE = 'CREATE' - TRANSFER = 'TRANSFER' - GENESIS = 'GENESIS' - ALLOWED_OPERATIONS = (CREATE, TRANSFER, GENESIS) - VERSION = 1 - - def __init__(self, operation, asset, fulfillments=None, conditions=None, - metadata=None, timestamp=None, version=None): - """The constructor allows to create a customizable Transaction. - - Note: - When no `version` or `timestamp`, is provided, one is being - generated by this method. - - Args: - operation (str): Defines the operation of the Transaction. - asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset - to be transferred or created in a Transaction. - fulfillments (:obj:`list` of :class:`~bigchaindb.common. - transaction.Fulfillment`, optional): Define the assets to - spend. - conditions (:obj:`list` of :class:`~bigchaindb.common. - transaction.Condition`, optional): Define the assets to - lock. - metadata (:class:`~bigchaindb.common.transaction.Metadata`): - Metadata to be stored along with the Transaction. - timestamp (int): Defines the time a Transaction was created. - version (int): Defines the version number of a Transaction. - - """ - if version is not None: - self.version = version - else: - self.version = self.__class__.VERSION - - if timestamp is not None: - self.timestamp = timestamp - else: - self.timestamp = gen_timestamp() - - if operation not in Transaction.ALLOWED_OPERATIONS: - allowed_ops = ', '.join(self.__class__.ALLOWED_OPERATIONS) - raise ValueError('`operation` must be one of {}' - .format(allowed_ops)) - else: - self.operation = operation - - # If an asset is not defined in a `CREATE` transaction, create a - # default one. - if asset is None and operation == Transaction.CREATE: - asset = Asset() - - if not isinstance(asset, Asset): - raise TypeError('`asset` must be an Asset instance') - else: - self.asset = asset - - if conditions is not None and not isinstance(conditions, list): - raise TypeError('`conditions` must be a list instance or None') - elif conditions is None: - self.conditions = [] - else: - self.conditions = conditions - - if fulfillments is not None and not isinstance(fulfillments, list): - raise TypeError('`fulfillments` must be a list instance or None') - elif fulfillments is None: - self.fulfillments = [] - else: - self.fulfillments = fulfillments - - if metadata is not None and not isinstance(metadata, Metadata): - raise TypeError('`metadata` must be a Metadata instance or None') - else: - self.metadata = metadata - - @classmethod - def create(cls, owners_before, owners_after, metadata=None, asset=None, - secret=None, time_expire=None): - """A simple way to generate a `CREATE` transaction. - - Note: - This method currently supports the following Cryptoconditions - use cases: - - Ed25519 - - ThresholdSha256 - - PreimageSha256. - - Additionally, it provides support for the following BigchainDB - use cases: - - Multiple inputs and outputs. - - Args: - owners_before (:obj:`list` of :obj:`str`): A list of keys that - represent the creators of this asset. - owners_after (:obj:`list` of :obj:`str`): A list of keys that - represent the receivers of this Transaction. - metadata (dict): Python dictionary to be stored along with the - Transaction. - asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset - to be created in this Transaction. - secret (binarystr, optional): A secret string to create a hash- - lock Condition. - time_expire (int, optional): The UNIX time a Transaction is - valid. - - Returns: - :class:`~bigchaindb.common.transaction.Transaction` - """ - if not isinstance(owners_before, list): - raise TypeError('`owners_before` must be a list instance') - if not isinstance(owners_after, list): - raise TypeError('`owners_after` must be a list instance') - - metadata = Metadata(metadata) - if len(owners_before) == len(owners_after) and len(owners_after) == 1: - # NOTE: Standard case, one owner before, one after. - # NOTE: For this case its sufficient to use the same - # fulfillment for the fulfillment and condition. - ffill = Ed25519Fulfillment(public_key=owners_before[0]) - ffill_tx = Fulfillment(ffill, owners_before) - cond_tx = Condition.generate(owners_after) - return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata) - - elif len(owners_before) == len(owners_after) and len(owners_after) > 1: - raise NotImplementedError('Multiple inputs and outputs not' - 'available for CREATE') - # NOTE: Multiple inputs and outputs case. Currently not supported. - ffills = [Fulfillment(Ed25519Fulfillment(public_key=owner_before), - [owner_before]) - for owner_before in owners_before] - conds = [Condition.generate(owners) for owners in owners_after] - return cls(cls.CREATE, asset, ffills, conds, metadata) - - elif len(owners_before) == 1 and len(owners_after) > 1: - # NOTE: Multiple owners case - cond_tx = Condition.generate(owners_after) - ffill = Ed25519Fulfillment(public_key=owners_before[0]) - ffill_tx = Fulfillment(ffill, owners_before) - return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata) - - elif (len(owners_before) == 1 and len(owners_after) == 0 and - secret is not None): - # NOTE: Hashlock condition case - hashlock = PreimageSha256Fulfillment(preimage=secret) - cond_tx = Condition(hashlock.condition_uri) - ffill = Ed25519Fulfillment(public_key=owners_before[0]) - ffill_tx = Fulfillment(ffill, owners_before) - return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata) - - elif (len(owners_before) > 0 and len(owners_after) == 0 and - time_expire is not None): - raise NotImplementedError('Timeout conditions will be implemented ' - 'later') - - elif (len(owners_before) > 0 and len(owners_after) == 0 and - secret is None): - raise ValueError('Define a secret to create a hashlock condition') - - else: - raise ValueError("These are not the cases you're looking for ;)") - - @classmethod - def transfer(cls, inputs, owners_after, asset, metadata=None): - """A simple way to generate a `TRANSFER` transaction. - - Note: - Different cases for threshold conditions: - - Combining multiple `inputs` with an arbitrary number of - `owners_after` can yield interesting cases for the creation of - threshold conditions we'd like to support. The following - notation is proposed: - - 1. The index of an `owner_after` corresponds to the index of - an input: - e.g. `transfer([input1], [a])`, means `input1` would now be - owned by user `a`. - - 2. `owners_after` can (almost) get arbitrary deeply nested, - creating various complex threshold conditions: - e.g. `transfer([inp1, inp2], [[a, [b, c]], d])`, means - `a`'s signature would have a 50% weight on `inp1` - compared to `b` and `c` that share 25% of the leftover - weight respectively. `inp2` is owned completely by `d`. - - Args: - inputs (:obj:`list` of :class:`~bigchaindb.common.transaction. - Fulfillment`): Converted "output" Conditions, intended to - be used as "input" Fulfillments in the transfer to - generate. - owners_after (:obj:`list` of :obj:`str`): A list of keys that - represent the receivers of this Transaction. - asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset - to be transferred in this Transaction. - metadata (dict): Python dictionary to be stored along with the - Transaction. - - Returns: - :class:`~bigchaindb.common.transaction.Transaction` - """ - if not isinstance(inputs, list): - raise TypeError('`inputs` must be a list instance') - if len(inputs) == 0: - raise ValueError('`inputs` must contain at least one item') - if not isinstance(owners_after, list): - raise TypeError('`owners_after` must be a list instance') - - # NOTE: See doc strings `Note` for description. - if len(inputs) == len(owners_after): - if len(owners_after) == 1: - conditions = [Condition.generate(owners_after)] - elif len(owners_after) > 1: - conditions = [Condition.generate(owners) for owners - in owners_after] - else: - raise ValueError("`inputs` and `owners_after`'s count must be the " - "same") - - metadata = Metadata(metadata) - inputs = deepcopy(inputs) - return cls(cls.TRANSFER, asset, inputs, conditions, metadata) - - def __eq__(self, other): - try: - other = other.to_dict() - except AttributeError: - return False - return self.to_dict() == other - - def to_inputs(self, condition_indices=None): - """Converts a Transaction's Conditions to spendable Fulfillments. - - Note: - Takes the Transaction's Conditions and derives Fulfillments - from it that can then be passed into `Transaction.transfer` as - `inputs`. - A list of integers can be passed to `condition_indices` that - defines which Conditions should be returned as inputs. - If no `condition_indices` are passed (empty list or None) all - Condition of the Transaction are passed. - - Args: - condition_indices (:obj:`list` of int): Defines which - Conditions should be returned as inputs. - - Returns: - :obj:`list` of :class:`~bigchaindb.common.transaction. - Fulfillment` - """ - inputs = [] - if condition_indices is None or len(condition_indices) == 0: - # NOTE: If no condition indices are passed, we just assume to - # take all conditions as inputs. - condition_indices = [index for index, _ - in enumerate(self.conditions)] - - for cid in condition_indices: - input_cond = self.conditions[cid] - ffill = Fulfillment(input_cond.fulfillment, - input_cond.owners_after, - TransactionLink(self.id, cid)) - inputs.append(ffill) - return inputs - - def add_fulfillment(self, fulfillment): - """Adds a Fulfillment to a Transaction's list of Fulfillments. - - Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`): A Fulfillment to be added to the - Transaction. - """ - if not isinstance(fulfillment, Fulfillment): - raise TypeError('`fulfillment` must be a Fulfillment instance') - self.fulfillments.append(fulfillment) - - def add_condition(self, condition): - """Adds a Condition to a Transaction's list of Conditions. - - Args: - condition (:class:`~bigchaindb.common.transaction. - Condition`): A Condition to be added to the - Transaction. - """ - if not isinstance(condition, Condition): - raise TypeError('`condition` must be a Condition instance or None') - self.conditions.append(condition) - - def sign(self, private_keys): - """Fulfills a previous Transaction's Condition by signing Fulfillments. - - Note: - This method works only for the following Cryptoconditions - currently: - - Ed25519Fulfillment - - ThresholdSha256Fulfillment - Furthermore, note that all keys required to fully sign the - Transaction have to be passed to this method. A subset of all - will cause this method to fail. - - Args: - private_keys (:obj:`list` of :obj:`str`): A complete list of - all private keys needed to sign all Fulfillments of this - Transaction. - - Returns: - :class:`~bigchaindb.common.transaction.Transaction` - """ - # TODO: Singing should be possible with at least one of all private - # keys supplied to this method. - if private_keys is None or not isinstance(private_keys, list): - raise TypeError('`private_keys` must be a list instance') - - # NOTE: Generate public keys from private keys and match them in a - # dictionary: - # key: public_key - # value: private_key - def gen_public_key(private_key): - # TODO FOR CC: Adjust interface so that this function becomes - # unnecessary - - # cc now provides a single method `encode` to return the key - # in several different encodings. - public_key = private_key.get_verifying_key().encode() - # Returned values from cc are always bytestrings so here we need - # to decode to convert the bytestring into a python str - return public_key.decode() - - key_pairs = {gen_public_key(SigningKey(private_key)): - SigningKey(private_key) for private_key in private_keys} - - zippedIO = enumerate(zip(self.fulfillments, self.conditions)) - for index, (fulfillment, condition) in zippedIO: - # NOTE: We clone the current transaction but only add the condition - # and fulfillment we're currently working on plus all - # previously signed ones. - tx_partial = Transaction(self.operation, self.asset, [fulfillment], - [condition], self.metadata, - self.timestamp, self.version) - - tx_partial_dict = tx_partial.to_dict() - tx_partial_dict = Transaction._remove_signatures(tx_partial_dict) - tx_serialized = Transaction._to_str(tx_partial_dict) - self._sign_fulfillment(fulfillment, index, tx_serialized, - key_pairs) - return self - - def _sign_fulfillment(self, fulfillment, index, tx_serialized, key_pairs): - """Signs a single Fulfillment with a partial Transaction as message. - - Note: - This method works only for the following Cryptoconditions - currently: - - Ed25519Fulfillment - - ThresholdSha256Fulfillment. - - Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`) The Fulfillment to be signed. - index (int): The index (or `fid`) of the Fulfillment to be - signed. - tx_serialized (str): The Transaction to be used as message. - key_pairs (dict): The keys to sign the Transaction with. - """ - if isinstance(fulfillment.fulfillment, Ed25519Fulfillment): - self._sign_simple_signature_fulfillment(fulfillment, index, - tx_serialized, key_pairs) - elif isinstance(fulfillment.fulfillment, ThresholdSha256Fulfillment): - self._sign_threshold_signature_fulfillment(fulfillment, index, - tx_serialized, - key_pairs) - else: - raise ValueError("Fulfillment couldn't be matched to " - 'Cryptocondition fulfillment type.') - - def _sign_simple_signature_fulfillment(self, fulfillment, index, - tx_serialized, key_pairs): - """Signs a Ed25519Fulfillment. - - Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`) The Fulfillment to be signed. - index (int): The index (or `fid`) of the Fulfillment to be - signed. - tx_serialized (str): The Transaction to be used as message. - key_pairs (dict): The keys to sign the Transaction with. - """ - # NOTE: To eliminate the dangers of accidentally signing a condition by - # reference, we remove the reference of fulfillment here - # intentionally. If the user of this class knows how to use it, - # this should never happen, but then again, never say never. - fulfillment = deepcopy(fulfillment) - owner_before = fulfillment.owners_before[0] - try: - # cryptoconditions makes no assumptions of the encoding of the - # message to sign or verify. It only accepts bytestrings - fulfillment.fulfillment.sign(tx_serialized.encode(), - key_pairs[owner_before]) - except KeyError: - raise KeypairMismatchException('Public key {} is not a pair to ' - 'any of the private keys' - .format(owner_before)) - self.fulfillments[index] = fulfillment - - def _sign_threshold_signature_fulfillment(self, fulfillment, index, - tx_serialized, key_pairs): - """Signs a ThresholdSha256Fulfillment. - - Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`) The Fulfillment to be signed. - index (int): The index (or `fid`) of the Fulfillment to be - signed. - tx_serialized (str): The Transaction to be used as message. - key_pairs (dict): The keys to sign the Transaction with. - """ - fulfillment = deepcopy(fulfillment) - for owner_before in fulfillment.owners_before: - try: - # TODO: CC should throw a KeypairMismatchException, instead of - # our manual mapping here - - # TODO FOR CC: Naming wise this is not so smart, - # `get_subcondition` in fact doesn't return a - # condition but a fulfillment - - # TODO FOR CC: `get_subcondition` is singular. One would not - # expect to get a list back. - ccffill = fulfillment.fulfillment - subffill = ccffill.get_subcondition_from_vk(owner_before)[0] - except IndexError: - raise KeypairMismatchException('Public key {} cannot be found ' - 'in the fulfillment' - .format(owner_before)) - try: - private_key = key_pairs[owner_before] - except KeyError: - raise KeypairMismatchException('Public key {} is not a pair ' - 'to any of the private keys' - .format(owner_before)) - - # cryptoconditions makes no assumptions of the encoding of the - # message to sign or verify. It only accepts bytestrings - subffill.sign(tx_serialized.encode(), private_key) - self.fulfillments[index] = fulfillment - - def fulfillments_valid(self, input_conditions=None): - """Validates the Fulfillments in the Transaction against given - Conditions. - - Note: - Given a `CREATE` or `GENESIS` Transaction is passed, - dummyvalues for Conditions are submitted for validation that - evaluate parts of the validation-checks to `True`. - - Args: - input_conditions (:obj:`list` of :class:`~bigchaindb.common. - transaction.Condition`): A list of Conditions to check the - Fulfillments against. - - Returns: - bool: If all Fulfillments are valid. - """ - if self.operation in (Transaction.CREATE, Transaction.GENESIS): - # NOTE: Since in the case of a `CREATE`-transaction we do not have - # to check for input_conditions, we're just submitting dummy - # values to the actual method. This simplifies it's logic - # greatly, as we do not have to check against `None` values. - return self._fulfillments_valid(['dummyvalue' - for cond in self.fulfillments]) - elif self.operation == Transaction.TRANSFER: - return self._fulfillments_valid([cond.fulfillment.condition_uri - for cond in input_conditions]) - else: - allowed_ops = ', '.join(self.__class__.ALLOWED_OPERATIONS) - raise TypeError('`operation` must be one of {}' - .format(allowed_ops)) - - def _fulfillments_valid(self, input_condition_uris): - """Validates a Fulfillment against a given set of Conditions. - - Note: - The number of `input_condition_uris` must be equal to the - number of Fulfillments a Transaction has. - - Args: - input_condition_uris (:obj:`list` of :obj:`str`): A list of - Conditions to check the Fulfillments against. - - Returns: - bool: If all Fulfillments are valid. - """ - input_condition_uris_count = len(input_condition_uris) - fulfillments_count = len(self.fulfillments) - conditions_count = len(self.conditions) - - def gen_tx(fulfillment, condition, input_condition_uri=None): - """Splits multiple IO Transactions into partial single IO - Transactions. - """ - tx = Transaction(self.operation, self.asset, [fulfillment], - [condition], self.metadata, self.timestamp, - self.version) - tx_dict = tx.to_dict() - tx_dict = Transaction._remove_signatures(tx_dict) - tx_serialized = Transaction._to_str(tx_dict) - - # TODO: Use local reference to class, not `Transaction.` - return Transaction._fulfillment_valid(fulfillment, self.operation, - tx_serialized, - input_condition_uri) - - if not fulfillments_count == conditions_count == \ - input_condition_uris_count: - raise ValueError('Fulfillments, conditions and ' - 'input_condition_uris must have the same count') - else: - partial_transactions = map(gen_tx, self.fulfillments, - self.conditions, input_condition_uris) - return all(partial_transactions) - - @staticmethod - def _fulfillment_valid(fulfillment, operation, tx_serialized, - input_condition_uri=None): - """Validates a single Fulfillment against a single Condition. - - Note: - In case of a `CREATE` or `GENESIS` Transaction, this method - does not validate against `input_condition_uri`. - - Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`) The Fulfillment to be signed. - operation (str): The type of Transaction. - tx_serialized (str): The Transaction used as a message when - initially signing it. - input_condition_uri (str, optional): A Condition to check the - Fulfillment against. - - Returns: - bool: If the Fulfillment is valid. - """ - ccffill = fulfillment.fulfillment - try: - parsed_ffill = CCFulfillment.from_uri(ccffill.serialize_uri()) - except (TypeError, ValueError, ParsingError): - return False - - if operation in (Transaction.CREATE, Transaction.GENESIS): - # NOTE: In the case of a `CREATE` or `GENESIS` transaction, the - # input condition is always validate to `True`. - input_cond_valid = True - else: - input_cond_valid = input_condition_uri == ccffill.condition_uri - - # NOTE: We pass a timestamp to `.validate`, as in case of a timeout - # condition we'll have to validate against it - - # cryptoconditions makes no assumptions of the encoding of the - # message to sign or verify. It only accepts bytestrings - return parsed_ffill.validate(message=tx_serialized.encode(), - now=gen_timestamp()) and input_cond_valid - - def to_dict(self): - """Transforms the object to a Python dictionary. - - Returns: - dict: The Transaction as an alternative serialization format. - """ - try: - metadata = self.metadata.to_dict() - except AttributeError: - # NOTE: metadata can be None and that's OK - metadata = None - - if self.operation in (self.__class__.GENESIS, self.__class__.CREATE): - asset = self.asset.to_dict() - else: - # NOTE: An `asset` in a `TRANSFER` only contains the asset's id - asset = {'id': self.asset.data_id} - - tx_body = { - 'fulfillments': [fulfillment.to_dict(fid) for fid, fulfillment - in enumerate(self.fulfillments)], - 'conditions': [condition.to_dict(cid) for cid, condition - in enumerate(self.conditions)], - 'operation': str(self.operation), - 'timestamp': self.timestamp, - 'metadata': metadata, - 'asset': asset, - } - tx = { - 'version': self.version, - 'transaction': tx_body, - } - - tx_no_signatures = Transaction._remove_signatures(tx) - tx_serialized = Transaction._to_str(tx_no_signatures) - tx_id = Transaction._to_hash(tx_serialized) - - tx['id'] = tx_id - return tx - - @staticmethod - # TODO: Remove `_dict` prefix of variable. - def _remove_signatures(tx_dict): - """Takes a Transaction dictionary and removes all signatures. - - Args: - tx_dict (dict): The Transaction to remove all signatures from. - - Returns: - dict - - """ - # NOTE: We remove the reference since we need `tx_dict` only for the - # transaction's hash - tx_dict = deepcopy(tx_dict) - for fulfillment in tx_dict['transaction']['fulfillments']: - # NOTE: Not all Cryptoconditions return a `signature` key (e.g. - # ThresholdSha256Fulfillment), so setting it to `None` in any - # case could yield incorrect signatures. This is why we only - # set it to `None` if it's set in the dict. - fulfillment['fulfillment'] = None - return tx_dict - - @staticmethod - def _to_hash(value): - return hash_data(value) - - @property - def id(self): - return self.to_hash() - - def to_hash(self): - return self.to_dict()['id'] - - @staticmethod - def _to_str(value): - return serialize(value) - - # TODO: This method shouldn't call `_remove_signatures` - def __str__(self): - tx = Transaction._remove_signatures(self.to_dict()) - return Transaction._to_str(tx) - - @classmethod - # TODO: Make this method more pretty - def from_dict(cls, tx_body): - """Transforms a Python dictionary to a Transaction object. - - Args: - tx_body (dict): The Transaction to be transformed. - - Returns: - :class:`~bigchaindb.common.transaction.Transaction` - """ - # NOTE: Remove reference to avoid side effects - tx_body = deepcopy(tx_body) - try: - proposed_tx_id = tx_body.pop('id') - except KeyError: - raise InvalidHash() - - tx_body_no_signatures = Transaction._remove_signatures(tx_body) - tx_body_serialized = Transaction._to_str(tx_body_no_signatures) - valid_tx_id = Transaction._to_hash(tx_body_serialized) - - if proposed_tx_id != valid_tx_id: - raise InvalidHash() - else: - tx = tx_body['transaction'] - fulfillments = [Fulfillment.from_dict(fulfillment) for fulfillment - in tx['fulfillments']] - conditions = [Condition.from_dict(condition) for condition - in tx['conditions']] - metadata = Metadata.from_dict(tx['metadata']) - asset = Asset.from_dict(tx['asset']) - - return cls(tx['operation'], asset, fulfillments, conditions, - metadata, tx['timestamp'], tx_body['version']) diff --git a/bigchaindb/common/util.py b/bigchaindb/common/util.py deleted file mode 100644 index f6f671db..00000000 --- a/bigchaindb/common/util.py +++ /dev/null @@ -1,48 +0,0 @@ -import time - -import rapidjson - - -def gen_timestamp(): - """The Unix time, rounded to the nearest second. - See https://en.wikipedia.org/wiki/Unix_time - - Returns: - str: the Unix time - """ - return str(round(time.time())) - - -def serialize(data): - """Serialize a dict into a JSON formatted string. - - This function enforces rules like the separator and order of keys. - This ensures that all dicts are serialized in the same way. - - This is specially important for hashing data. We need to make sure that - everyone serializes their data in the same way so that we do not have - hash mismatches for the same structure due to serialization - differences. - - Args: - data (dict): dict to serialize - - Returns: - str: JSON formatted string - - """ - return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, - sort_keys=True) - - -def deserialize(data): - """Deserialize a JSON formatted string into a dict. - - Args: - data (str): JSON formatted string. - - Returns: - dict: dict resulting from the serialization of a JSON formatted - string. - """ - return rapidjson.loads(data) diff --git a/bigchaindb/config_utils.py b/bigchaindb/config_utils.py index 3b571835..f72c348b 100644 --- a/bigchaindb/config_utils.py +++ b/bigchaindb/config_utils.py @@ -17,7 +17,7 @@ import json import logging import collections -from bigchaindb.common import exceptions +from bigchaindb_common import exceptions import bigchaindb diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 2724080f..3930496d 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -4,9 +4,9 @@ import collections from time import time from itertools import compress -from bigchaindb.common import crypto, exceptions -from bigchaindb.common.util import gen_timestamp, serialize -from bigchaindb.common.transaction import TransactionLink, Metadata +from bigchaindb_common import crypto, exceptions +from bigchaindb_common.util import gen_timestamp, serialize +from bigchaindb_common.transaction import TransactionLink, Metadata import rethinkdb as r diff --git a/bigchaindb/db/utils.py b/bigchaindb/db/utils.py index 92e0fdd3..d51563d5 100644 --- a/bigchaindb/db/utils.py +++ b/bigchaindb/db/utils.py @@ -3,7 +3,7 @@ import time import logging -from bigchaindb.common import exceptions +from bigchaindb_common import exceptions import rethinkdb as r import bigchaindb diff --git a/bigchaindb/models.py b/bigchaindb/models.py index 1334b2de..7ed81061 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -1,11 +1,11 @@ -from bigchaindb.common.crypto import hash_data, VerifyingKey, SigningKey -from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature, +from bigchaindb_common.crypto import hash_data, VerifyingKey, SigningKey +from bigchaindb_common.exceptions import (InvalidHash, InvalidSignature, OperationError, DoubleSpend, TransactionDoesNotExist, FulfillmentNotInValidBlock, AssetIdMismatch) -from bigchaindb.common.transaction import Transaction, Asset -from bigchaindb.common.util import gen_timestamp, serialize +from bigchaindb_common.transaction import Transaction, Asset +from bigchaindb_common.util import gen_timestamp, serialize class Asset(Asset): diff --git a/bigchaindb/pipelines/vote.py b/bigchaindb/pipelines/vote.py index 6b12f55b..3d79c265 100644 --- a/bigchaindb/pipelines/vote.py +++ b/bigchaindb/pipelines/vote.py @@ -8,7 +8,7 @@ function. from collections import Counter from multipipes import Pipeline, Node -from bigchaindb.common import exceptions +from bigchaindb_common import exceptions from bigchaindb.consensus import BaseConsensusRules from bigchaindb.models import Transaction, Block diff --git a/bigchaindb/util.py b/bigchaindb/util.py index fcaa5e00..d8fb13e3 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -3,8 +3,8 @@ import threading import queue import multiprocessing as mp -from bigchaindb.common import crypto -from bigchaindb.common.util import serialize +from bigchaindb_common import crypto +from bigchaindb_common.util import serialize class ProcessGroup(object): diff --git a/bigchaindb/web/views/transactions.py b/bigchaindb/web/views/transactions.py index 8780fda8..6439ff4c 100644 --- a/bigchaindb/web/views/transactions.py +++ b/bigchaindb/web/views/transactions.py @@ -6,7 +6,7 @@ For more information please refer to the documentation on ReadTheDocs: from flask import current_app, request, Blueprint from flask_restful import Resource, Api -from bigchaindb.common.exceptions import InvalidHash, InvalidSignature +from bigchaindb_common.exceptions import InvalidHash, InvalidSignature import bigchaindb from bigchaindb.models import Transaction diff --git a/deploy-cluster-aws/write_keypairs_file.py b/deploy-cluster-aws/write_keypairs_file.py index da4fc1b1..2037141c 100644 --- a/deploy-cluster-aws/write_keypairs_file.py +++ b/deploy-cluster-aws/write_keypairs_file.py @@ -1,5 +1,5 @@ """A Python 3 script to write a file with a specified number -of keypairs, using bigchaindb.common.crypto.generate_key_pair() +of keypairs, using bigchaindb_common.crypto.generate_key_pair() The written file is always named keypairs.py and it should be interpreted as a Python 2 script. @@ -16,7 +16,7 @@ Using the list in other Python scripts: import argparse -from bigchaindb.common import crypto +from bigchaindb_common import crypto # Parse the command-line arguments diff --git a/setup.py b/setup.py index 67a367f6..13c13e93 100644 --- a/setup.py +++ b/setup.py @@ -103,6 +103,7 @@ setup( 'requests~=2.9', 'gunicorn~=19.0', 'multipipes~=0.1.0', + 'bigchaindb-common==0.0.6', ], setup_requires=['pytest-runner'], tests_require=tests_require, diff --git a/tests/assets/test_digital_assets.py b/tests/assets/test_digital_assets.py index e18684c5..64da4c11 100644 --- a/tests/assets/test_digital_assets.py +++ b/tests/assets/test_digital_assets.py @@ -76,7 +76,7 @@ def test_validate_bad_asset_creation(b, user_vk): @pytest.mark.usefixtures('inputs') def test_validate_transfer_asset_id_mismatch(b, user_vk, user_sk): - from bigchaindb.common.exceptions import AssetIdMismatch + from bigchaindb_common.exceptions import AssetIdMismatch from bigchaindb.models import Transaction tx_create = b.get_owned_ids(user_vk).pop() @@ -121,7 +121,7 @@ def test_get_asset_id_transfer_transaction(b, user_vk, user_sk): def test_asset_id_mismatch(b, user_vk): from bigchaindb.models import Transaction, Asset - from bigchaindb.common.exceptions import AssetIdMismatch + from bigchaindb_common.exceptions import AssetIdMismatch tx1 = Transaction.create([b.me], [user_vk]) tx2 = Transaction.create([b.me], [user_vk]) diff --git a/tests/common/conftest.py b/tests/common/conftest.py deleted file mode 100644 index 8b1a47db..00000000 --- a/tests/common/conftest.py +++ /dev/null @@ -1,162 +0,0 @@ -import pytest - - -USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie' -USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE' - -USER2_PRIVATE_KEY = 'F86PQPiqMTwM2Qi2Sda3U4Vdh3AgadMdX3KNVsu5wNJr' -USER2_PUBLIC_KEY = 'GDxwMFbwdATkQELZbMfW8bd9hbNYMZLyVXA3nur2aNbE' - -USER3_PRIVATE_KEY = '4rNQFzWQbVwuTiDVxwuFMvLG5zd8AhrQKCtVovBvcYsB' -USER3_PUBLIC_KEY = 'Gbrg7JtxdjedQRmr81ZZbh1BozS7fBW88ZyxNDy7WLNC' - - -CC_FULFILLMENT_URI = 'cf:0:' -CC_CONDITION_URI = 'cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0' - -DATA = { - 'msg': 'Hello BigchainDB!' -} -DATA_ID = '872fa6e6f46246cd44afdb2ee9cfae0e72885fb0910e2bcf9a5a2a4eadb417b8' - - -@pytest.fixture -def user_priv(): - return USER_PRIVATE_KEY - - -@pytest.fixture -def user_pub(): - return USER_PUBLIC_KEY - - -@pytest.fixture -def user2_priv(): - return USER2_PRIVATE_KEY - - -@pytest.fixture -def user2_pub(): - return USER2_PUBLIC_KEY - - -@pytest.fixture -def user3_priv(): - return USER3_PRIVATE_KEY - - -@pytest.fixture -def user3_pub(): - return USER3_PUBLIC_KEY - - -@pytest.fixture -def ffill_uri(): - return CC_FULFILLMENT_URI - - -@pytest.fixture -def cond_uri(): - return CC_CONDITION_URI - - -@pytest.fixture -def user_Ed25519(user_pub): - from cryptoconditions import Ed25519Fulfillment - return Ed25519Fulfillment(public_key=user_pub) - - -@pytest.fixture -def user_user2_threshold(user_pub, user2_pub): - from cryptoconditions import (ThresholdSha256Fulfillment, - Ed25519Fulfillment) - user_pub_keys = [user_pub, user2_pub] - threshold = ThresholdSha256Fulfillment(threshold=len(user_pub_keys)) - for user_pub in user_pub_keys: - threshold.add_subfulfillment(Ed25519Fulfillment(public_key=user_pub)) - return threshold - - -@pytest.fixture -def user2_Ed25519(user2_pub): - from cryptoconditions import Ed25519Fulfillment - return Ed25519Fulfillment(public_key=user2_pub) - - -@pytest.fixture -def user_ffill(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Fulfillment - return Fulfillment(user_Ed25519, [user_pub]) - - -@pytest.fixture -def user2_ffill(user2_Ed25519, user2_pub): - from bigchaindb.common.transaction import Fulfillment - return Fulfillment(user2_Ed25519, [user2_pub]) - - -@pytest.fixture -def user_user2_threshold_cond(user_user2_threshold, user_pub, user2_pub): - from bigchaindb.common.transaction import Condition - return Condition(user_user2_threshold, [user_pub, user2_pub]) - - -@pytest.fixture -def user_user2_threshold_ffill(user_user2_threshold, user_pub, user2_pub): - from bigchaindb.common.transaction import Fulfillment - return Fulfillment(user_user2_threshold, [user_pub, user2_pub]) - - -@pytest.fixture -def user_cond(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Condition - return Condition(user_Ed25519, [user_pub]) - - -@pytest.fixture -def user2_cond(user2_Ed25519, user2_pub): - from bigchaindb.common.transaction import Condition - return Condition(user2_Ed25519, [user2_pub]) - - -@pytest.fixture -def data(): - return DATA - - -@pytest.fixture -def data_id(): - return DATA_ID - - -@pytest.fixture -def metadata(data, data_id): - from bigchaindb.common.transaction import Metadata - return Metadata(data, data_id) - - -@pytest.fixture -def utx(user_ffill, user_cond): - from bigchaindb.common.transaction import Transaction, Asset - return Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond]) - - -@pytest.fixture -def tx(utx, user_priv): - return utx.sign([user_priv]) - - -@pytest.fixture -def transfer_utx(user_cond, user2_cond, utx): - from bigchaindb.common.transaction import (Fulfillment, TransactionLink, - Transaction, Asset) - user_cond = user_cond.to_dict() - ffill = Fulfillment(utx.conditions[0].fulfillment, - user_cond['owners_after'], - TransactionLink(utx.id, 0)) - return Transaction('TRANSFER', Asset(), [ffill], [user2_cond]) - - -@pytest.fixture -def transfer_tx(transfer_utx, user_priv): - return transfer_utx.sign([user_priv]) diff --git a/tests/common/test_asset.py b/tests/common/test_asset.py deleted file mode 100644 index edfbcb5f..00000000 --- a/tests/common/test_asset.py +++ /dev/null @@ -1,80 +0,0 @@ -from pytest import raises - - -def test_asset_default_values(): - from bigchaindb.common.transaction import Asset - - asset = Asset() - assert asset.data is None - assert asset.data_id - assert asset.divisible is False - assert asset.updatable is False - assert asset.refillable is False - - -def test_asset_creation_with_data(data): - from bigchaindb.common.transaction import Asset - - asset = Asset(data) - assert asset.data == data - - -def test_asset_invalid_asset_initialization(): - from bigchaindb.common.transaction import Asset - - with raises(TypeError): - Asset(data='some wrong type') - with raises(TypeError): - Asset(divisible=1) - with raises(TypeError): - Asset(refillable=1) - with raises(TypeError): - Asset(updatable=1) - - -def test_invalid_asset_comparison(data, data_id): - from bigchaindb.common.transaction import Asset - - assert Asset(data, data_id) != 'invalid comparison' - - -def test_asset_serialization(data, data_id): - from bigchaindb.common.transaction import Asset - - expected = { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - } - asset = Asset(data, data_id) - assert asset.to_dict() == expected - - -def test_asset_deserialization(data, data_id): - from bigchaindb.common.transaction import Asset - - asset_dict = { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - } - asset = Asset.from_dict(asset_dict) - expected = Asset(data, data_id) - assert asset == expected - - -def test_validate_asset(): - from bigchaindb.common.transaction import Asset - - with raises(TypeError): - Asset(divisible=1) - with raises(TypeError): - Asset(refillable=1) - with raises(TypeError): - Asset(updatable=1) - with raises(TypeError): - Asset(data='we need more lemon pledge') diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py deleted file mode 100644 index 5f2d58fb..00000000 --- a/tests/common/test_transaction.py +++ /dev/null @@ -1,1106 +0,0 @@ -from pytest import raises, mark - - -def test_fulfillment_serialization(ffill_uri, user_pub): - from bigchaindb.common.transaction import Fulfillment - from cryptoconditions import Fulfillment as CCFulfillment - - expected = { - 'owners_before': [user_pub], - 'fulfillment': ffill_uri, - 'input': None, - } - ffill = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) - assert ffill.to_dict() == expected - - -def test_fulfillment_deserialization_with_uri(ffill_uri, user_pub): - from bigchaindb.common.transaction import Fulfillment - from cryptoconditions import Fulfillment as CCFulfillment - - expected = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) - ffill = { - 'owners_before': [user_pub], - 'fulfillment': ffill_uri, - 'input': None, - } - ffill = Fulfillment.from_dict(ffill) - - assert ffill == expected - - -def test_fulfillment_deserialization_with_invalid_fulfillment(user_pub): - from bigchaindb.common.transaction import Fulfillment - - ffill = { - 'owners_before': [user_pub], - 'fulfillment': None, - 'input': None, - } - with raises(TypeError): - Fulfillment.from_dict(ffill) - - -def test_fulfillment_deserialization_with_invalid_fulfillment_uri(user_pub): - from bigchaindb.common.exceptions import InvalidSignature - from bigchaindb.common.transaction import Fulfillment - - ffill = { - 'owners_before': [user_pub], - 'fulfillment': 'an invalid fulfillment', - 'input': None, - } - with raises(InvalidSignature): - Fulfillment.from_dict(ffill) - - -def test_fulfillment_deserialization_with_unsigned_fulfillment(ffill_uri, - user_pub): - from bigchaindb.common.transaction import Fulfillment - from cryptoconditions import Fulfillment as CCFulfillment - - expected = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) - ffill = { - 'owners_before': [user_pub], - 'fulfillment': CCFulfillment.from_uri(ffill_uri), - 'input': None, - } - ffill = Fulfillment.from_dict(ffill) - - assert ffill == expected - - -def test_condition_serialization(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Condition - - expected = { - 'condition': { - 'uri': user_Ed25519.condition_uri, - 'details': user_Ed25519.to_dict(), - }, - 'owners_after': [user_pub], - 'amount': 1, - } - - cond = Condition(user_Ed25519, [user_pub], 1) - - assert cond.to_dict() == expected - - -def test_condition_deserialization(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Condition - - expected = Condition(user_Ed25519, [user_pub], 1) - cond = { - 'condition': { - 'uri': user_Ed25519.condition_uri, - 'details': user_Ed25519.to_dict() - }, - 'owners_after': [user_pub], - 'amount': 1, - } - cond = Condition.from_dict(cond) - - assert cond == expected - - -def test_condition_hashlock_serialization(): - from bigchaindb.common.transaction import Condition - from cryptoconditions import PreimageSha256Fulfillment - - secret = b'wow much secret' - hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri - - expected = { - 'condition': { - 'uri': hashlock, - }, - 'owners_after': None, - 'amount': 1, - } - cond = Condition(hashlock, amount=1) - - assert cond.to_dict() == expected - - -def test_condition_hashlock_deserialization(): - from bigchaindb.common.transaction import Condition - from cryptoconditions import PreimageSha256Fulfillment - - secret = b'wow much secret' - hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri - expected = Condition(hashlock, amount=1) - - cond = { - 'condition': { - 'uri': hashlock - }, - 'owners_after': None, - 'amount': 1, - } - cond = Condition.from_dict(cond) - - assert cond == expected - - -def test_invalid_condition_initialization(cond_uri, user_pub): - from bigchaindb.common.transaction import Condition - - with raises(TypeError): - Condition(cond_uri, user_pub) - - -def test_generate_conditions_split_half_recursive(user_pub, user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=2) - expected.add_subfulfillment(expected_simple1) - expected_threshold = ThresholdSha256Fulfillment(threshold=2) - expected_threshold.add_subfulfillment(expected_simple2) - expected_threshold.add_subfulfillment(expected_simple3) - expected.add_subfulfillment(expected_threshold) - - cond = Condition.generate([user_pub, [user2_pub, expected_simple3]]) - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_split_half_recursive_custom_threshold(user_pub, - user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=1) - expected.add_subfulfillment(expected_simple1) - expected_threshold = ThresholdSha256Fulfillment(threshold=1) - expected_threshold.add_subfulfillment(expected_simple2) - expected_threshold.add_subfulfillment(expected_simple3) - expected.add_subfulfillment(expected_threshold) - - cond = Condition.generate(([user_pub, ([user2_pub, expected_simple3], 1)], - 1)) - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_split_half_single_owner(user_pub, user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=2) - expected_threshold = ThresholdSha256Fulfillment(threshold=2) - expected_threshold.add_subfulfillment(expected_simple2) - expected_threshold.add_subfulfillment(expected_simple3) - expected.add_subfulfillment(expected_threshold) - expected.add_subfulfillment(expected_simple1) - - cond = Condition.generate([[expected_simple2, user3_pub], user_pub]) - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_flat_ownage(user_pub, user2_pub, user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple2 = Ed25519Fulfillment(public_key=user2_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=3) - expected.add_subfulfillment(expected_simple1) - expected.add_subfulfillment(expected_simple2) - expected.add_subfulfillment(expected_simple3) - - cond = Condition.generate([user_pub, user2_pub, expected_simple3]) - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_single_owner(user_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment - - expected = Ed25519Fulfillment(public_key=user_pub) - cond = Condition.generate([user_pub]) - - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_single_owner_with_condition(user_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment - - expected = Ed25519Fulfillment(public_key=user_pub) - cond = Condition.generate([expected]) - - assert cond.fulfillment.to_dict() == expected.to_dict() - - -# TODO FOR CC: see skip reason -@mark.skip(reason='threshold(hashlock).to_dict() exposes secret') -def test_generate_threshold_condition_with_hashlock(user_pub, user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import (PreimageSha256Fulfillment, - Ed25519Fulfillment, - ThresholdSha256Fulfillment) - - secret = b'much secret, wow' - hashlock = PreimageSha256Fulfillment(preimage=secret) - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=2) - expected_sub = ThresholdSha256Fulfillment(threshold=2) - expected_sub.add_subfulfillment(expected_simple1) - expected_sub.add_subfulfillment(hashlock) - expected.add_subfulfillment(expected_simple3) - - cond = Condition.generate([[user_pub, hashlock], expected_simple3]) - assert cond.fulfillment.to_dict() == expected.to_dict() - - -def test_generate_conditions_invalid_parameters(user_pub, user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - - with raises(ValueError): - Condition.generate([]) - with raises(TypeError): - Condition.generate('not a list') - with raises(ValueError): - Condition.generate([[user_pub, [user2_pub, [user3_pub]]]]) - with raises(ValueError): - Condition.generate([[user_pub]]) - - -def test_invalid_transaction_initialization(): - from bigchaindb.common.transaction import Transaction, Asset - - with raises(ValueError): - Transaction(operation='invalid operation', asset=Asset()) - with raises(TypeError): - Transaction(operation='CREATE', asset='invalid asset') - with raises(TypeError): - Transaction( - operation='CREATE', - asset=Asset(), - conditions='invalid conditions' - ) - with raises(TypeError): - Transaction( - operation='CREATE', - asset=Asset(), - conditions=[], - fulfillments='invalid fulfillments' - ) - with raises(TypeError): - Transaction( - operation='CREATE', - asset=Asset(), - conditions=[], - fulfillments=[], - metadata='invalid metadata' - ) - - -def test_create_default_asset_on_tx_initialization(): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, None) - expected = Asset() - asset = tx.asset - - expected.data_id = None - asset.data_id = None - assert asset == expected - - -def test_transaction_serialization(user_ffill, user_cond, data, data_id): - from bigchaindb.common.transaction import Transaction, Asset - - tx_id = 'l0l' - timestamp = '66666666666' - - expected = { - 'id': tx_id, - 'version': Transaction.VERSION, - 'transaction': { - # NOTE: This test assumes that Fulfillments and Conditions can - # successfully be serialized - 'fulfillments': [user_ffill.to_dict(0)], - 'conditions': [user_cond.to_dict(0)], - 'operation': Transaction.CREATE, - 'timestamp': timestamp, - 'metadata': None, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - } - } - } - - tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_ffill], - [user_cond]) - tx_dict = tx.to_dict() - tx_dict['id'] = tx_id - tx_dict['transaction']['asset']['id'] = data_id - tx_dict['transaction']['timestamp'] = timestamp - - assert tx_dict == expected - - -def test_transaction_deserialization(user_ffill, user_cond, data, data_id): - from bigchaindb.common.transaction import Transaction, Asset - - timestamp = '66666666666' - - expected_asset = Asset(data, data_id) - expected = Transaction(Transaction.CREATE, expected_asset, [user_ffill], - [user_cond], None, timestamp, Transaction.VERSION) - - tx = { - 'version': Transaction.VERSION, - 'transaction': { - # NOTE: This test assumes that Fulfillments and Conditions can - # successfully be serialized - 'fulfillments': [user_ffill.to_dict()], - 'conditions': [user_cond.to_dict()], - 'operation': Transaction.CREATE, - 'timestamp': timestamp, - 'metadata': None, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - } - } - } - tx_no_signatures = Transaction._remove_signatures(tx) - tx['id'] = Transaction._to_hash(Transaction._to_str(tx_no_signatures)) - tx = Transaction.from_dict(tx) - - assert tx == expected - - -def test_tx_serialization_with_incorrect_hash(utx): - from bigchaindb.common.transaction import Transaction - from bigchaindb.common.exceptions import InvalidHash - - utx_dict = utx.to_dict() - utx_dict['id'] = 'abc' - with raises(InvalidHash): - Transaction.from_dict(utx_dict) - utx_dict.pop('id') - with raises(InvalidHash): - Transaction.from_dict(utx_dict) - utx_dict['id'] = [] - with raises(InvalidHash): - Transaction.from_dict(utx_dict) - - -def test_invalid_fulfillment_initialization(user_ffill, user_pub): - from bigchaindb.common.transaction import Fulfillment - - with raises(TypeError): - Fulfillment(user_ffill, user_pub) - with raises(TypeError): - Fulfillment(user_ffill, [], tx_input='somethingthatiswrong') - - -def test_invalid_metadata_initialization(): - from bigchaindb.common.transaction import Metadata - - with raises(TypeError): - Metadata([]) - - -def test_metadata_serialization(data, data_id): - from bigchaindb.common.transaction import Metadata - - expected = { - 'data': data, - 'id': data_id, - } - metadata = Metadata(data, data_id) - - assert metadata.to_dict() == expected - - -def test_metadata_deserialization(data, data_id): - from bigchaindb.common.transaction import Metadata - - expected = Metadata(data, data_id) - metadata = Metadata.from_dict({'data': data, 'id': data_id}) - - assert metadata == expected - - -def test_transaction_link_serialization(): - from bigchaindb.common.transaction import TransactionLink - - tx_id = 'a transaction id' - expected = { - 'txid': tx_id, - 'cid': 0, - } - tx_link = TransactionLink(tx_id, 0) - - assert tx_link.to_dict() == expected - - -def test_transaction_link_serialization_with_empty_payload(): - from bigchaindb.common.transaction import TransactionLink - - expected = None - tx_link = TransactionLink() - - assert tx_link.to_dict() == expected - - -def test_transaction_link_deserialization(): - from bigchaindb.common.transaction import TransactionLink - - tx_id = 'a transaction id' - expected = TransactionLink(tx_id, 0) - tx_link = { - 'txid': tx_id, - 'cid': 0, - } - tx_link = TransactionLink.from_dict(tx_link) - - assert tx_link == expected - - -def test_transaction_link_deserialization_with_empty_payload(): - from bigchaindb.common.transaction import TransactionLink - - expected = TransactionLink() - tx_link = TransactionLink.from_dict(None) - - assert tx_link == expected - - -def test_cast_transaction_link_to_boolean(): - from bigchaindb.common.transaction import TransactionLink - - assert bool(TransactionLink()) is False - assert bool(TransactionLink('a', None)) is False - assert bool(TransactionLink(None, 'b')) is False - assert bool(TransactionLink('a', 'b')) is True - assert bool(TransactionLink(False, False)) is True - - -def test_add_fulfillment_to_tx(user_ffill): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset(), [], []) - tx.add_fulfillment(user_ffill) - - assert len(tx.fulfillments) == 1 - - -def test_add_fulfillment_to_tx_with_invalid_parameters(): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset()) - with raises(TypeError): - tx.add_fulfillment('somewronginput') - - -def test_add_condition_to_tx(user_cond): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset()) - tx.add_condition(user_cond) - - assert len(tx.conditions) == 1 - - -def test_add_condition_to_tx_with_invalid_parameters(): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset(), [], []) - with raises(TypeError): - tx.add_condition('somewronginput') - - -def test_sign_with_invalid_parameters(utx, user_priv): - with raises(TypeError): - utx.sign(None) - with raises(TypeError): - utx.sign(user_priv) - - -def test_validate_tx_simple_create_signature(user_ffill, user_cond, user_priv): - from copy import deepcopy - from bigchaindb.common.crypto import SigningKey - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond]) - expected = deepcopy(user_cond) - expected.fulfillment.sign(str(tx).encode(), SigningKey(user_priv)) - tx.sign([user_priv]) - - assert tx.fulfillments[0].to_dict()['fulfillment'] == \ - expected.fulfillment.serialize_uri() - assert tx.fulfillments_valid() is True - - -def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, - user_ffill): - from bigchaindb.common.exceptions import KeypairMismatchException - - with raises(KeypairMismatchException): - invalid_key_pair = {'wrong_pub_key': 'wrong_priv_key'} - utx._sign_simple_signature_fulfillment(user_ffill, - 0, - 'somemessage', - invalid_key_pair) - - -def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_ffill, - user3_pub, user3_priv): - from bigchaindb.common.exceptions import KeypairMismatchException - - with raises(KeypairMismatchException): - utx._sign_threshold_signature_fulfillment(user_user2_threshold_ffill, - 0, - 'somemessage', - {user3_pub: user3_priv}) - with raises(KeypairMismatchException): - user_user2_threshold_ffill.owners_before = ['somewrongvalue'] - utx._sign_threshold_signature_fulfillment(user_user2_threshold_ffill, - 0, - 'somemessage', - None) - - -def test_validate_fulfillment_with_invalid_parameters(utx): - from bigchaindb.common.transaction import Transaction - - input_conditions = [cond.fulfillment.condition_uri for cond - in utx.conditions] - tx_dict = utx.to_dict() - tx_dict = Transaction._remove_signatures(tx_dict) - tx_serialized = Transaction._to_str(tx_dict) - assert utx._fulfillment_valid(utx.fulfillments[0], - tx_serialized, - input_conditions) is False - - -def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv): - from copy import deepcopy - - from bigchaindb.common.crypto import SigningKey - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset(), - [user_ffill, deepcopy(user_ffill)], - [user_ffill, deepcopy(user_cond)]) - - expected_first = deepcopy(tx) - expected_second = deepcopy(tx) - expected_first.fulfillments = [expected_first.fulfillments[0]] - expected_first.conditions = [expected_first.conditions[0]] - expected_second.fulfillments = [expected_second.fulfillments[1]] - expected_second.conditions = [expected_second.conditions[1]] - - expected_first_bytes = str(expected_first).encode() - expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes, - SigningKey(user_priv)) - expected_second_bytes = str(expected_second).encode() - expected_second.fulfillments[0].fulfillment.sign(expected_second_bytes, - SigningKey(user_priv)) - tx.sign([user_priv]) - - assert tx.fulfillments[0].to_dict()['fulfillment'] == \ - expected_first.fulfillments[0].fulfillment.serialize_uri() - assert tx.fulfillments[1].to_dict()['fulfillment'] == \ - expected_second.fulfillments[0].fulfillment.serialize_uri() - assert tx.fulfillments_valid() is True - - -def test_validate_tx_threshold_create_signature(user_user2_threshold_ffill, - user_user2_threshold_cond, - user_pub, - user2_pub, - user_priv, - user2_priv): - from copy import deepcopy - - from bigchaindb.common.crypto import SigningKey - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction(Transaction.CREATE, Asset(), [user_user2_threshold_ffill], - [user_user2_threshold_cond]) - expected = deepcopy(user_user2_threshold_cond) - expected.fulfillment.subconditions[0]['body'].sign(str(tx).encode(), - SigningKey(user_priv)) - expected.fulfillment.subconditions[1]['body'].sign(str(tx).encode(), - SigningKey(user2_priv)) - tx.sign([user_priv, user2_priv]) - - assert tx.fulfillments[0].to_dict()['fulfillment'] == \ - expected.fulfillment.serialize_uri() - assert tx.fulfillments_valid() is True - - -def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond, - user_priv, user2_pub, - user2_priv, user3_pub, - user3_priv): - from copy import deepcopy - from bigchaindb.common.transaction import (Transaction, TransactionLink, - Fulfillment, Condition, Asset) - from cryptoconditions import Ed25519Fulfillment - - tx = Transaction(Transaction.CREATE, Asset(), - [user_ffill, deepcopy(user_ffill)], - [user_cond, deepcopy(user_cond)]) - tx.sign([user_priv]) - - fulfillments = [Fulfillment(cond.fulfillment, cond.owners_after, - TransactionLink(tx.id, index)) - for index, cond in enumerate(tx.conditions)] - conditions = [Condition(Ed25519Fulfillment(public_key=user3_pub), - [user3_pub]), - Condition(Ed25519Fulfillment(public_key=user3_pub), - [user3_pub])] - transfer_tx = Transaction('TRANSFER', tx.asset, fulfillments, conditions) - transfer_tx = transfer_tx.sign([user_priv]) - - assert transfer_tx.fulfillments_valid(tx.conditions) is True - - -def test_validate_fulfillments_of_transfer_tx_with_invalid_params(transfer_tx, - cond_uri, - utx, - user2_pub, - user_priv): - from bigchaindb.common.transaction import Condition - from cryptoconditions import Ed25519Fulfillment - - invalid_cond = Condition(Ed25519Fulfillment.from_uri('cf:0:'), ['invalid']) - assert transfer_tx.fulfillments_valid([invalid_cond]) is False - invalid_cond = utx.conditions[0] - invalid_cond.owners_after = 'invalid' - assert transfer_tx.fulfillments_valid([invalid_cond]) is True - - with raises(TypeError): - assert transfer_tx.fulfillments_valid(None) is False - with raises(AttributeError): - transfer_tx.fulfillments_valid('not a list') - with raises(ValueError): - transfer_tx.fulfillments_valid([]) - with raises(TypeError): - transfer_tx.operation = "Operation that doesn't exist" - transfer_tx.fulfillments_valid([utx.conditions[0]]) - with raises(ValueError): - tx = utx.sign([user_priv]) - tx.conditions = [] - tx.fulfillments_valid() - - -def test_create_create_transaction_single_io(user_cond, user_pub, data, - data_id): - from bigchaindb.common.transaction import Transaction, Asset - - expected = { - 'transaction': { - 'conditions': [user_cond.to_dict(0)], - 'metadata': { - 'data': data, - }, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub - ], - 'fid': 0, - 'fulfillment': None, - 'input': None - } - ], - 'operation': 'CREATE', - }, - 'version': 1 - } - - asset = Asset(data, data_id) - tx = Transaction.create([user_pub], [user_pub], data, asset).to_dict() - tx.pop('id') - tx['transaction']['metadata'].pop('id') - tx['transaction'].pop('timestamp') - tx['transaction']['fulfillments'][0]['fulfillment'] = None - - assert tx == expected - - -def test_validate_single_io_create_transaction(user_pub, user_priv, data): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction.create([user_pub], [user_pub], data, Asset()) - tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True - - -@mark.skip(reason='Multiple inputs and outputs in CREATE not supported') -# TODO: Add digital assets -def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub, - user2_pub): - from bigchaindb.common.transaction import Transaction - - expected = { - 'transaction': { - 'conditions': [user_cond.to_dict(0), user2_cond.to_dict(1)], - 'metadata': { - 'data': { - 'message': 'hello' - } - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub, - ], - 'fid': 0, - 'fulfillment': None, - 'input': None - }, - { - 'owners_before': [ - user2_pub, - ], - 'fid': 1, - 'fulfillment': None, - 'input': None - } - ], - 'operation': 'CREATE', - }, - 'version': 1 - } - tx = Transaction.create([user_pub, user2_pub], [user_pub, user2_pub], - {'message': 'hello'}).to_dict() - tx.pop('id') - tx['transaction']['metadata'].pop('id') - tx['transaction'].pop('timestamp') - - assert tx == expected - - -@mark.skip(reason='Multiple inputs and outputs in CREATE not supported') -# TODO: Add digital assets -def test_validate_multiple_io_create_transaction(user_pub, user_priv, - user2_pub, user2_priv): - from bigchaindb.common.transaction import Transaction - - tx = Transaction.create([user_pub, user2_pub], [user_pub, user2_pub], - {'message': 'hello'}) - tx = tx.sign([user_priv, user2_priv]) - assert tx.fulfillments_valid() is True - - -def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, - user_user2_threshold_cond, - user_user2_threshold_ffill, data, - data_id): - from bigchaindb.common.transaction import Transaction, Asset - - expected = { - 'transaction': { - 'conditions': [user_user2_threshold_cond.to_dict(0)], - 'metadata': { - 'data': data, - }, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub, - ], - 'fid': 0, - 'fulfillment': None, - 'input': None - }, - ], - 'operation': 'CREATE', - }, - 'version': 1 - } - asset = Asset(data, data_id) - tx = Transaction.create([user_pub], [user_pub, user2_pub], data, asset) - tx_dict = tx.to_dict() - tx_dict.pop('id') - tx_dict['transaction']['metadata'].pop('id') - tx_dict['transaction'].pop('timestamp') - tx_dict['transaction']['fulfillments'][0]['fulfillment'] = None - - assert tx_dict == expected - - -def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub, - data): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction.create([user_pub], [user_pub, user2_pub], data, Asset()) - tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True - - -def test_create_create_transaction_hashlock(user_pub, data, data_id): - from cryptoconditions import PreimageSha256Fulfillment - from bigchaindb.common.transaction import Transaction, Condition, Asset - - secret = b'much secret, wow' - hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri - cond = Condition(hashlock) - - expected = { - 'transaction': { - 'conditions': [cond.to_dict(0)], - 'metadata': { - 'data': data, - }, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub, - ], - 'fid': 0, - 'fulfillment': None, - 'input': None - }, - ], - 'operation': 'CREATE', - }, - 'version': 1 - } - - asset = Asset(data, data_id) - tx = Transaction.create([user_pub], [], data, asset, secret).to_dict() - tx.pop('id') - tx['transaction']['metadata'].pop('id') - tx['transaction'].pop('timestamp') - tx['transaction']['fulfillments'][0]['fulfillment'] = None - - assert tx == expected - - -def test_validate_hashlock_create_transaction(user_pub, user_priv, data): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction.create([user_pub], [], data, Asset(), b'much secret, wow') - tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True - - -def test_create_create_transaction_with_invalid_parameters(): - from bigchaindb.common.transaction import Transaction - - with raises(TypeError): - Transaction.create('not a list') - with raises(TypeError): - Transaction.create([], 'not a list') - with raises(NotImplementedError): - Transaction.create(['a', 'b'], ['c', 'd']) - with raises(NotImplementedError): - Transaction.create(['a'], [], time_expire=123) - with raises(ValueError): - Transaction.create(['a'], [], secret=None) - with raises(ValueError): - Transaction.create([], [], secret='wow, much secret') - - -def test_conditions_to_inputs(tx): - ffills = tx.to_inputs([0]) - assert len(ffills) == 1 - ffill = ffills.pop() - assert ffill.fulfillment == tx.conditions[0].fulfillment - assert ffill.owners_before == tx.conditions[0].owners_after - assert ffill.tx_input.txid == tx.id - assert ffill.tx_input.cid == 0 - - -def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, - user2_cond, user_priv, data_id): - from copy import deepcopy - from bigchaindb.common.crypto import SigningKey - from bigchaindb.common.transaction import Transaction, Asset - from bigchaindb.common.util import serialize - - expected = { - 'transaction': { - 'conditions': [user2_cond.to_dict(0)], - 'metadata': None, - 'asset': { - 'id': data_id, - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub - ], - 'fid': 0, - 'fulfillment': None, - 'input': { - 'txid': tx.id, - 'cid': 0 - } - } - ], - 'operation': 'TRANSFER', - }, - 'version': 1 - } - inputs = tx.to_inputs([0]) - asset = Asset(None, data_id) - transfer_tx = Transaction.transfer(inputs, [user2_pub], asset=asset) - transfer_tx = transfer_tx.sign([user_priv]) - transfer_tx = transfer_tx.to_dict() - transfer_tx_body = transfer_tx['transaction'] - - expected_input = deepcopy(inputs[0]) - expected['id'] = transfer_tx['id'] - expected['transaction']['timestamp'] = transfer_tx_body['timestamp'] - expected_input.fulfillment.sign(serialize(expected).encode(), - SigningKey(user_priv)) - expected_ffill = expected_input.fulfillment.serialize_uri() - transfer_ffill = transfer_tx_body['fulfillments'][0]['fulfillment'] - - assert transfer_ffill == expected_ffill - - transfer_tx = Transaction.from_dict(transfer_tx) - assert transfer_tx.fulfillments_valid([tx.conditions[0]]) is True - - -@mark.skip(reason='FIXME: When divisible assets land') -def test_create_transfer_transaction_multiple_io(user_pub, user_priv, - user2_pub, user2_priv, - user3_pub, user2_cond): - from bigchaindb.common.transaction import Transaction - - tx1 = Transaction.create([user_pub], [user_pub], {'message': 'hello'}) - tx1 = tx1.sign([user_priv]) - tx2 = Transaction.create([user2_pub], [user2_pub], {'message': 'hello'}) - tx2 = tx2.sign([user2_priv]) - - expected = { - 'transaction': { - 'conditions': [user2_cond.to_dict(0), user2_cond.to_dict(1)], - 'metadata': None, - 'fulfillments': [ - { - 'owners_before': [ - user_pub - ], - 'fid': 0, - 'fulfillment': None, - 'input': { - 'txid': tx1.id, - 'cid': 0 - } - }, { - 'owners_before': [ - user2_pub - ], - 'fid': 1, - 'fulfillment': None, - 'input': { - 'txid': tx2.id, - 'cid': 0 - } - } - ], - 'operation': 'TRANSFER', - }, - 'version': 1 - } - tx1_inputs = tx1.to_inputs() - tx2_inputs = tx2.to_inputs() - tx_inputs = tx1_inputs + tx2_inputs - - transfer_tx = Transaction.transfer(tx_inputs, [[user2_pub], [user2_pub]]) - transfer_tx = transfer_tx.sign([user_priv, user2_priv]) - transfer_tx = transfer_tx - - assert len(transfer_tx.fulfillments) == 2 - assert len(transfer_tx.conditions) == 2 - - combined_conditions = tx1.conditions + tx2.conditions - assert transfer_tx.fulfillments_valid(combined_conditions) is True - - transfer_tx = transfer_tx.to_dict() - transfer_tx['transaction']['fulfillments'][0]['fulfillment'] = None - transfer_tx['transaction']['fulfillments'][1]['fulfillment'] = None - transfer_tx['transaction'].pop('timestamp') - transfer_tx.pop('id') - - assert expected == transfer_tx - - -def test_create_transfer_with_invalid_parameters(): - from bigchaindb.common.transaction import Transaction, Asset - - with raises(TypeError): - Transaction.transfer({}, [], Asset()) - with raises(ValueError): - Transaction.transfer([], [], Asset()) - with raises(TypeError): - Transaction.transfer(['fulfillment'], {}, Asset()) - with raises(ValueError): - Transaction.transfer(['fulfillment'], [], Asset()) - - -def test_cant_add_empty_condition(): - from bigchaindb.common.transaction import Transaction - tx = Transaction(Transaction.CREATE, None) - with raises(TypeError): - tx.add_condition(None) - - -def test_cant_add_empty_fulfillment(): - from bigchaindb.common.transaction import Transaction - tx = Transaction(Transaction.CREATE, None) - with raises(TypeError): - tx.add_fulfillment(None) diff --git a/tests/db/conftest.py b/tests/db/conftest.py index f55a4c34..ec4afc9b 100644 --- a/tests/db/conftest.py +++ b/tests/db/conftest.py @@ -11,7 +11,7 @@ import rethinkdb as r from bigchaindb import Bigchain from bigchaindb.db import get_conn -from bigchaindb.common import crypto +from bigchaindb_common import crypto USER2_SK, USER2_VK = crypto.generate_key_pair() @@ -107,7 +107,7 @@ def cleanup_tables(request, node_config): @pytest.fixture def inputs(user_vk): from bigchaindb.models import Transaction - from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError + from bigchaindb_common.exceptions import GenesisBlockAlreadyExistsError # 1. create the genesis block b = Bigchain() try: @@ -144,7 +144,7 @@ def user2_vk(): @pytest.fixture def inputs_shared(user_vk, user2_vk): from bigchaindb.models import Transaction - from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError + from bigchaindb_common.exceptions import GenesisBlockAlreadyExistsError # 1. create the genesis block b = Bigchain() try: diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index f0a88c44..48a87fdc 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -30,9 +30,9 @@ def dummy_block(): class TestBigchainApi(object): def test_get_last_voted_block_cyclic_blockchain(self, b, monkeypatch): - from bigchaindb.common.crypto import SigningKey - from bigchaindb.common.exceptions import CyclicBlockchainError - from bigchaindb.common.util import serialize + from bigchaindb_common.crypto import SigningKey + from bigchaindb_common.exceptions import CyclicBlockchainError + from bigchaindb_common.util import serialize from bigchaindb.models import Transaction b.create_genesis_block() @@ -55,7 +55,7 @@ class TestBigchainApi(object): def test_try_voting_while_constructing_cyclic_blockchain(self, b, monkeypatch): - from bigchaindb.common.exceptions import CyclicBlockchainError + from bigchaindb_common.exceptions import CyclicBlockchainError from bigchaindb.models import Transaction b.create_genesis_block() @@ -94,7 +94,7 @@ class TestBigchainApi(object): assert not matches def test_get_spent_with_double_spend(self, b, monkeypatch): - from bigchaindb.common.exceptions import DoubleSpend + from bigchaindb_common.exceptions import DoubleSpend from bigchaindb.models import Transaction b.create_genesis_block() @@ -128,7 +128,7 @@ class TestBigchainApi(object): b.get_spent(tx.id, 0) def test_get_block_status_for_tx_with_double_spend(self, b, monkeypatch): - from bigchaindb.common.exceptions import DoubleSpend + from bigchaindb_common.exceptions import DoubleSpend from bigchaindb.models import Transaction b.create_genesis_block() @@ -277,7 +277,7 @@ class TestBigchainApi(object): def test_create_genesis_block_fails_if_table_not_empty(self, b): import rethinkdb as r - from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError + from bigchaindb_common.exceptions import GenesisBlockAlreadyExistsError from bigchaindb.util import is_genesis_block from bigchaindb.db.utils import get_conn @@ -333,7 +333,7 @@ class TestBigchainApi(object): assert prev_block_id == last_block['id'] def test_create_empty_block(self, b): - from bigchaindb.common.exceptions import OperationError + from bigchaindb_common.exceptions import OperationError with pytest.raises(OperationError) as excinfo: b.create_block([]) @@ -433,7 +433,7 @@ class TestBigchainApi(object): def test_more_votes_than_voters(self, b): import rethinkdb as r - from bigchaindb.common.exceptions import MultipleVotesError + from bigchaindb_common.exceptions import MultipleVotesError from bigchaindb.db.utils import get_conn b.create_genesis_block() @@ -453,7 +453,7 @@ class TestBigchainApi(object): def test_multiple_votes_single_node(self, b): import rethinkdb as r - from bigchaindb.common.exceptions import MultipleVotesError + from bigchaindb_common.exceptions import MultipleVotesError from bigchaindb.db.utils import get_conn genesis = b.create_genesis_block() @@ -475,7 +475,7 @@ class TestBigchainApi(object): def test_improper_vote_error(selfs, b): import rethinkdb as r - from bigchaindb.common.exceptions import ImproperVoteError + from bigchaindb_common.exceptions import ImproperVoteError from bigchaindb.db.utils import get_conn b.create_genesis_block() @@ -512,7 +512,7 @@ class TestBigchainApi(object): @pytest.mark.usefixtures('inputs') def test_assign_transaction_multiple_nodes(self, b, user_vk, user_sk): import rethinkdb as r - from bigchaindb.common.crypto import generate_key_pair + from bigchaindb_common.crypto import generate_key_pair from bigchaindb.models import Transaction from bigchaindb.db.utils import get_conn @@ -539,8 +539,8 @@ class TestBigchainApi(object): @pytest.mark.usefixtures('inputs') def test_non_create_input_not_found(self, b, user_vk): from cryptoconditions import Ed25519Fulfillment - from bigchaindb.common.exceptions import TransactionDoesNotExist - from bigchaindb.common.transaction import (Fulfillment, Asset, + from bigchaindb_common.exceptions import TransactionDoesNotExist + from bigchaindb_common.transaction import (Fulfillment, Asset, TransactionLink) from bigchaindb.models import Transaction from bigchaindb import Bigchain @@ -557,7 +557,7 @@ class TestBigchainApi(object): class TestTransactionValidation(object): def test_create_operation_with_inputs(self, b, user_vk, create_tx): - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common.transaction import TransactionLink # Manipulate fulfillment so that it has a `tx_input` defined even # though it shouldn't have one @@ -575,8 +575,8 @@ class TestTransactionValidation(object): assert excinfo.value.args[0] == 'Only `CREATE` transactions can have null inputs' def test_non_create_input_not_found(self, b, user_vk, signed_transfer_tx): - from bigchaindb.common.exceptions import TransactionDoesNotExist - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common.exceptions import TransactionDoesNotExist + from bigchaindb_common.transaction import TransactionLink signed_transfer_tx.fulfillments[0].tx_input = TransactionLink('c', 0) with pytest.raises(TransactionDoesNotExist): @@ -584,8 +584,8 @@ class TestTransactionValidation(object): @pytest.mark.usefixtures('inputs') def test_non_create_valid_input_wrong_owner(self, b, user_vk): - from bigchaindb.common.crypto import generate_key_pair - from bigchaindb.common.exceptions import InvalidSignature + from bigchaindb_common.crypto import generate_key_pair + from bigchaindb_common.exceptions import InvalidSignature from bigchaindb.models import Transaction input_tx = b.get_owned_ids(user_vk).pop() @@ -602,7 +602,7 @@ class TestTransactionValidation(object): @pytest.mark.usefixtures('inputs') def test_non_create_double_spend(self, b, signed_create_tx, signed_transfer_tx): - from bigchaindb.common.exceptions import DoubleSpend + from bigchaindb_common.exceptions import DoubleSpend block1 = b.create_block([signed_create_tx]) b.write_block(block1) @@ -652,7 +652,7 @@ class TestTransactionValidation(object): @pytest.mark.usefixtures('inputs') def test_fulfillment_not_in_valid_block(self, b, user_vk, user_sk): from bigchaindb.models import Transaction - from bigchaindb.common.exceptions import FulfillmentNotInValidBlock + from bigchaindb_common.exceptions import FulfillmentNotInValidBlock input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) @@ -681,9 +681,9 @@ class TestBlockValidation(object): @pytest.mark.skipif(reason='Separated tx validation from block creation.') @pytest.mark.usefixtures('inputs') def test_invalid_transactions_in_block(self, b, user_vk): - from bigchaindb.common import crypto - from bigchaindb.common.exceptions import TransactionOwnerError - from bigchaindb.common.util import gen_timestamp + from bigchaindb_common import crypto + from bigchaindb_common.exceptions import TransactionOwnerError + from bigchaindb_common.util import gen_timestamp from bigchaindb import util @@ -722,8 +722,8 @@ class TestBlockValidation(object): assert excinfo.value.args[0] == 'owner_before `a` does not own the input `{}`'.format(valid_input) def test_invalid_signature(self, b): - from bigchaindb.common.exceptions import InvalidSignature - from bigchaindb.common import crypto + from bigchaindb_common.exceptions import InvalidSignature + from bigchaindb_common import crypto # create a valid block block = dummy_block() @@ -736,8 +736,8 @@ class TestBlockValidation(object): b.validate_block(block) def test_invalid_node_pubkey(self, b): - from bigchaindb.common.exceptions import OperationError - from bigchaindb.common import crypto + from bigchaindb_common.exceptions import OperationError + from bigchaindb_common import crypto # blocks can only be created by a federation node # create a valid block @@ -761,7 +761,7 @@ class TestBlockValidation(object): class TestMultipleInputs(object): def test_transfer_single_owner_single_input(self, b, inputs, user_vk, user_sk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -781,7 +781,7 @@ class TestMultipleInputs(object): 'multiple assets')) @pytest.mark.usefixtures('inputs') def test_transfer_single_owners_multiple_inputs(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -804,7 +804,7 @@ class TestMultipleInputs(object): def test_transfer_single_owners_single_input_from_multiple_outputs(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -843,7 +843,7 @@ class TestMultipleInputs(object): user_sk, user_vk, inputs): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -866,7 +866,7 @@ class TestMultipleInputs(object): def test_single_owner_before_multiple_owners_after_multiple_inputs(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -897,7 +897,7 @@ class TestMultipleInputs(object): def test_multiple_owners_before_single_owner_after_single_input(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -930,7 +930,7 @@ class TestMultipleInputs(object): @pytest.mark.usefixtures('inputs_shared') def test_multiple_owners_before_single_owner_after_multiple_inputs(self, b, user_sk, user_vk, user2_vk, user2_sk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction # create a new users @@ -951,7 +951,7 @@ class TestMultipleInputs(object): def test_multiple_owners_before_multiple_owners_after_single_input(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -985,7 +985,7 @@ class TestMultipleInputs(object): def test_multiple_owners_before_multiple_owners_after_multiple_inputs(self, b, user_sk, user_vk, user2_sk, user2_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction # create a new users @@ -1004,8 +1004,8 @@ class TestMultipleInputs(object): assert len(tx.conditions) == len(inputs) def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_vk): - from bigchaindb.common import crypto - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common import crypto + from bigchaindb_common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -1033,8 +1033,8 @@ class TestMultipleInputs(object): def test_get_owned_ids_single_tx_single_output_invalid_block(self, b, user_sk, user_vk): - from bigchaindb.common import crypto - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common import crypto + from bigchaindb_common.transaction import TransactionLink from bigchaindb.models import Transaction genesis = b.create_genesis_block() @@ -1078,8 +1078,8 @@ class TestMultipleInputs(object): def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_vk): import random - from bigchaindb.common import crypto - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common import crypto + from bigchaindb_common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -1115,8 +1115,8 @@ class TestMultipleInputs(object): TransactionLink(tx.id, 1)] def test_get_owned_ids_multiple_owners(self, b, user_sk, user_vk): - from bigchaindb.common import crypto - from bigchaindb.common.transaction import TransactionLink + from bigchaindb_common import crypto + from bigchaindb_common.transaction import TransactionLink from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -1145,7 +1145,7 @@ class TestMultipleInputs(object): assert owned_inputs_user1 == [] def test_get_spent_single_tx_single_output(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() @@ -1173,7 +1173,7 @@ class TestMultipleInputs(object): assert spent_inputs_user1 == tx def test_get_spent_single_tx_single_output_invalid_block(self, b, user_sk, user_vk): - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction genesis = b.create_genesis_block() @@ -1219,7 +1219,7 @@ class TestMultipleInputs(object): 'multiple assets')) def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_vk): import random - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction # create a new users @@ -1259,7 +1259,7 @@ class TestMultipleInputs(object): def test_get_spent_multiple_owners(self, b, user_sk, user_vk): import random - from bigchaindb.common import crypto + from bigchaindb_common import crypto from bigchaindb.models import Transaction user2_sk, user2_vk = crypto.generate_key_pair() diff --git a/tests/db/test_utils.py b/tests/db/test_utils.py index 75d14c02..56f09ac5 100644 --- a/tests/db/test_utils.py +++ b/tests/db/test_utils.py @@ -1,6 +1,6 @@ import builtins -from bigchaindb.common import exceptions +from bigchaindb_common import exceptions import pytest import rethinkdb as r diff --git a/tests/doc/run_doc_python_server_api_examples.py b/tests/doc/run_doc_python_server_api_examples.py index a7bf89d5..b1f0ed9b 100644 --- a/tests/doc/run_doc_python_server_api_examples.py +++ b/tests/doc/run_doc_python_server_api_examples.py @@ -2,7 +2,7 @@ import json from time import sleep import cryptoconditions as cc -from bigchaindb.common.util import gen_timestamp +from bigchaindb_common.util import gen_timestamp from bigchaindb import Bigchain, util, crypto, exceptions diff --git a/tests/pipelines/test_election.py b/tests/pipelines/test_election.py index 669a75cb..7887b4c9 100644 --- a/tests/pipelines/test_election.py +++ b/tests/pipelines/test_election.py @@ -1,7 +1,7 @@ import time from unittest.mock import patch -from bigchaindb.common import crypto +from bigchaindb_common import crypto import rethinkdb as r from multipipes import Pipe, Pipeline diff --git a/tests/pipelines/test_vote.py b/tests/pipelines/test_vote.py index 5bd0eb52..52ac117e 100644 --- a/tests/pipelines/test_vote.py +++ b/tests/pipelines/test_vote.py @@ -19,8 +19,8 @@ def dummy_block(b): def test_vote_creation_valid(b): - from bigchaindb.common import crypto - from bigchaindb.common.util import serialize + from bigchaindb_common import crypto + from bigchaindb_common.util import serialize # create valid block block = dummy_block(b) @@ -38,8 +38,8 @@ def test_vote_creation_valid(b): def test_vote_creation_invalid(b): - from bigchaindb.common import crypto - from bigchaindb.common.util import serialize + from bigchaindb_common import crypto + from bigchaindb_common.util import serialize # create valid block block = dummy_block(b) @@ -154,7 +154,7 @@ def test_vote_accumulates_transactions(b): def test_valid_block_voting_sequential(b, monkeypatch): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.pipelines import vote monkeypatch.setattr('time.time', lambda: 1) @@ -182,7 +182,7 @@ def test_valid_block_voting_sequential(b, monkeypatch): def test_valid_block_voting_multiprocessing(b, monkeypatch): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.pipelines import vote inpipe = Pipe() @@ -216,7 +216,7 @@ def test_valid_block_voting_multiprocessing(b, monkeypatch): def test_valid_block_voting_with_create_transaction(b, monkeypatch): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.models import Transaction from bigchaindb.pipelines import vote @@ -257,7 +257,7 @@ def test_valid_block_voting_with_create_transaction(b, monkeypatch): def test_valid_block_voting_with_transfer_transactions(monkeypatch, b): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.models import Transaction from bigchaindb.pipelines import vote @@ -325,7 +325,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b): def test_unsigned_tx_in_block_voting(monkeypatch, b, user_vk): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.models import Transaction from bigchaindb.pipelines import vote @@ -362,7 +362,7 @@ def test_unsigned_tx_in_block_voting(monkeypatch, b, user_vk): def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_vk): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.models import Transaction from bigchaindb.pipelines import vote @@ -401,7 +401,7 @@ def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_vk): def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_vk): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.models import Transaction from bigchaindb.pipelines import vote @@ -440,7 +440,7 @@ def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_vk): def test_invalid_block_voting(monkeypatch, b, user_vk): - from bigchaindb.common import crypto, util + from bigchaindb_common import crypto, util from bigchaindb.pipelines import vote inpipe = Pipe() diff --git a/tests/test_commands.py b/tests/test_commands.py index 95fc4179..6cb19d48 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -22,7 +22,7 @@ def mock_write_config(monkeypatch): @pytest.fixture def mock_db_init_with_existing_db(monkeypatch): from bigchaindb import db - from bigchaindb.common.exceptions import DatabaseAlreadyExists + from bigchaindb_common.exceptions import DatabaseAlreadyExists def mockreturn(): raise DatabaseAlreadyExists @@ -48,7 +48,7 @@ def mock_rethink_db_drop(monkeypatch): @pytest.fixture def mock_generate_key_pair(monkeypatch): - monkeypatch.setattr('bigchaindb.common.crypto.generate_key_pair', lambda: ('privkey', 'pubkey')) + monkeypatch.setattr('bigchaindb_common.crypto.generate_key_pair', lambda: ('privkey', 'pubkey')) @pytest.fixture @@ -283,14 +283,14 @@ def test_start_rethinkdb_returns_a_process_when_successful(mock_popen): @patch('subprocess.Popen') def test_start_rethinkdb_exits_when_cannot_start(mock_popen): - from bigchaindb.common import exceptions + from bigchaindb_common import exceptions from bigchaindb.commands import utils mock_popen.return_value = Mock(stdout=['Nopety nope']) with pytest.raises(exceptions.StartupError): utils.start_rethinkdb() -@patch('bigchaindb.common.crypto.generate_key_pair', +@patch('bigchaindb_common.crypto.generate_key_pair', return_value=('private_key', 'public_key')) def test_allow_temp_keypair_generates_one_on_the_fly(mock_gen_keypair, mock_processes_start, @@ -307,7 +307,7 @@ def test_allow_temp_keypair_generates_one_on_the_fly(mock_gen_keypair, assert bigchaindb.config['keypair']['public'] == 'public_key' -@patch('bigchaindb.common.crypto.generate_key_pair', +@patch('bigchaindb_common.crypto.generate_key_pair', return_value=('private_key', 'public_key')) def test_allow_temp_keypair_doesnt_override_if_keypair_found(mock_gen_keypair, mock_processes_start, diff --git a/tests/test_config_utils.py b/tests/test_config_utils.py index ea0a0528..5ffd5b9a 100644 --- a/tests/test_config_utils.py +++ b/tests/test_config_utils.py @@ -42,7 +42,7 @@ def test_bigchain_instance_is_initialized_when_conf_provided(): def test_bigchain_instance_raises_when_not_configured(monkeypatch): from bigchaindb import config_utils - from bigchaindb.common import exceptions + from bigchaindb_common import exceptions assert 'CONFIGURED' not in bigchaindb.config # We need to disable ``bigchaindb.config_utils.autoconfigure`` to avoid reading @@ -204,7 +204,7 @@ def test_file_config(): def test_invalid_file_config(): from bigchaindb.config_utils import file_config - from bigchaindb.common import exceptions + from bigchaindb_common import exceptions with patch('builtins.open', mock_open(read_data='{_INVALID_JSON_}')): with pytest.raises(exceptions.ConfigurationError): file_config() diff --git a/tests/test_models.py b/tests/test_models.py index 5033aebb..faef5353 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -37,8 +37,8 @@ class TestBlockModel(object): 'not a list or None') def test_block_serialization(self, b): - from bigchaindb.common.crypto import hash_data - from bigchaindb.common.util import gen_timestamp, serialize + from bigchaindb_common.crypto import hash_data + from bigchaindb_common.util import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [b.me])] @@ -61,7 +61,7 @@ class TestBlockModel(object): assert block.to_dict() == expected def test_block_invalid_serializaton(self): - from bigchaindb.common.exceptions import OperationError + from bigchaindb_common.exceptions import OperationError from bigchaindb.models import Block block = Block([]) @@ -69,8 +69,8 @@ class TestBlockModel(object): block.to_dict() def test_block_deserialization(self, b): - from bigchaindb.common.crypto import hash_data - from bigchaindb.common.util import gen_timestamp, serialize + from bigchaindb_common.crypto import hash_data + from bigchaindb_common.util import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [b.me])] @@ -94,7 +94,7 @@ class TestBlockModel(object): assert expected == Block.from_dict(block_body) def test_block_invalid_id_deserialization(self, b): - from bigchaindb.common.exceptions import InvalidHash + from bigchaindb_common.exceptions import InvalidHash from bigchaindb.models import Block block = { @@ -108,9 +108,9 @@ class TestBlockModel(object): Block.from_dict(block) def test_block_invalid_signature_deserialization(self, b): - from bigchaindb.common.crypto import hash_data - from bigchaindb.common.exceptions import InvalidSignature - from bigchaindb.common.util import gen_timestamp, serialize + from bigchaindb_common.crypto import hash_data + from bigchaindb_common.exceptions import InvalidSignature + from bigchaindb_common.util import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [b.me])] @@ -142,8 +142,8 @@ class TestBlockModel(object): assert Block(transactions) == Block(transactions) def test_sign_block(self, b): - from bigchaindb.common.crypto import SigningKey, VerifyingKey - from bigchaindb.common.util import gen_timestamp, serialize + from bigchaindb_common.crypto import SigningKey, VerifyingKey + from bigchaindb_common.util import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [b.me])] diff --git a/tests/web/test_basic_views.py b/tests/web/test_basic_views.py index 00e40a37..b941011a 100644 --- a/tests/web/test_basic_views.py +++ b/tests/web/test_basic_views.py @@ -1,7 +1,7 @@ import json import pytest -from bigchaindb.common import crypto +from bigchaindb_common import crypto TX_ENDPOINT = '/api/v1/transactions/'