diff --git a/bigchaindb/backend/rethinkdb/query.py b/bigchaindb/backend/rethinkdb/query.py index 8fa6a512..e3428fc5 100644 --- a/bigchaindb/backend/rethinkdb/query.py +++ b/bigchaindb/backend/rethinkdb/query.py @@ -95,13 +95,13 @@ def get_asset_by_id(connection, asset_id): @register_query(RethinkDBConnection) -def get_spent(connection, transaction_id, condition_id): +def get_spent(connection, transaction_id, output_id): # TODO: use index! return connection.run( r.table('bigchain', read_mode=READ_MODE) .concat_map(lambda doc: doc['block']['transactions']) - .filter(lambda transaction: transaction['fulfillments'].contains( - lambda fulfillment: fulfillment['input'] == {'txid': transaction_id, 'cid': condition_id}))) + .filter(lambda transaction: transaction['inputs'].contains( + lambda input: input['fulfills'] == {'txid': transaction_id, 'idx': output_id}))) @register_query(RethinkDBConnection) @@ -110,8 +110,8 @@ def get_owned_ids(connection, owner): return connection.run( r.table('bigchain', read_mode=READ_MODE) .concat_map(lambda doc: doc['block']['transactions']) - .filter(lambda tx: tx['conditions'].contains( - lambda c: c['owners_after'].contains(owner)))) + .filter(lambda tx: tx['outputs'].contains( + lambda c: c['public_keys'].contains(owner)))) @register_query(RethinkDBConnection) diff --git a/bigchaindb/common/exceptions.py b/bigchaindb/common/exceptions.py index 661a9c92..cec64706 100644 --- a/bigchaindb/common/exceptions.py +++ b/bigchaindb/common/exceptions.py @@ -79,7 +79,7 @@ class CyclicBlockchainError(Exception): class TransactionNotInValidBlock(Exception): """Raised when a transfer transaction is attempting to fulfill the - conditions of a transaction that is in an invalid or undecided block""" + outputs of a transaction that is in an invalid or undecided block""" class AssetIdMismatch(Exception): diff --git a/bigchaindb/common/schema/transaction.yaml b/bigchaindb/common/schema/transaction.yaml index 08dba158..3c78d8aa 100644 --- a/bigchaindb/common/schema/transaction.yaml +++ b/bigchaindb/common/schema/transaction.yaml @@ -88,11 +88,10 @@ definitions: Type of the transaction: A ``CREATE`` transaction creates an asset in BigchainDB. This - transaction has outputs (conditions) but no inputs (fulfillments), - so a dummy fulfillment is used. + transaction has outputs but no inputs, so a dummy fulfillment is used. A ``TRANSFER`` transaction transfers ownership of an asset, by providing - fulfillments to conditions of earlier transactions. + fulfillments to outputs conditions of earlier transactions. A ``GENESIS`` transaction is a special case transaction used as the sole member of the first block in a BigchainDB ledger. diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py index c4f646dc..ed79ed6d 100644 --- a/bigchaindb/common/transaction.py +++ b/bigchaindb/common/transaction.py @@ -13,20 +13,20 @@ from bigchaindb.common.exceptions import (KeypairMismatchException, from bigchaindb.common.util import serialize, gen_timestamp -class Fulfillment(object): - """A Fulfillment is used to spend assets locked by a Condition. +class Input(object): + """A Input is used to spend assets locked by an Output. 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`, + fulfills (:class:`~bigchaindb.common.transaction. TransactionLink`, optional): A link representing the input of a `TRANSFER` Transaction. """ - def __init__(self, fulfillment, owners_before, tx_input=None): + def __init__(self, fulfillment, owners_before, fulfills=None): """Fulfillment shims a Cryptocondition Fulfillment for BigchainDB. Args: @@ -34,18 +34,17 @@ class Fulfillment(object): 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. + fulfills (:class:`~bigchaindb.common.transaction. TransactionLink`, optional): A link representing the input of a `TRANSFER` Transaction. """ - if tx_input is not None and not isinstance(tx_input, TransactionLink): - raise TypeError('`tx_input` must be a TransactionLink instance') - + if fulfills is not None and not isinstance(fulfills, TransactionLink): + raise TypeError('`fulfills` must be a TransactionLink instance') if not isinstance(owners_before, list): raise TypeError('`owners_after` must be a list instance') self.fulfillment = fulfillment - self.tx_input = tx_input + self.fulfills = fulfills self.owners_before = owners_before def __eq__(self, other): @@ -56,17 +55,17 @@ class Fulfillment(object): """Transforms the object to a Python dictionary. Note: - If a Fulfillment hasn't been signed yet, this method returns a + If an Input hasn't been signed yet, this method returns a dictionary representation. Returns: - dict: The Fulfillment as an alternative serialization format. + dict: The Input 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 + # `self.inputs` 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. @@ -76,54 +75,54 @@ class Fulfillment(object): fulfillment = self.fulfillment.to_dict() try: - # NOTE: `self.tx_input` can be `None` and that's fine - tx_input = self.tx_input.to_dict() + # NOTE: `self.fulfills` can be `None` and that's fine + fulfills = self.fulfills.to_dict() except AttributeError: - tx_input = None + fulfills = None - ffill = { + input = { 'owners_before': self.owners_before, - 'input': tx_input, + 'fulfills': fulfills, 'fulfillment': fulfillment, } - return ffill + return input @classmethod - def generate(cls, owners_before): + def generate(cls, public_keys): # TODO: write docstring # The amount here does not really matter. It is only use on the - # condition data model but here we only care about the fulfillment - condition = Condition.generate(owners_before, 1) - return cls(condition.fulfillment, condition.owners_after) + # output data model but here we only care about the fulfillment + output = Output.generate(public_keys, 1) + return cls(output.fulfillment, public_keys) @classmethod - def from_dict(cls, ffill): - """Transforms a Python dictionary to a Fulfillment object. + def from_dict(cls, data): + """Transforms a Python dictionary to an Input object. Note: Optionally, this method can also serialize a Cryptoconditions- Fulfillment that is not yet signed. Args: - ffill (dict): The Fulfillment to be transformed. + data (dict): The Fulfillment to be transformed. Returns: - :class:`~bigchaindb.common.transaction.Fulfillment` + :class:`~bigchaindb.common.transaction.Input` Raises: - InvalidSignature: If a Fulfillment's URI couldn't be parsed. + InvalidSignature: If an Input's URI couldn't be parsed. """ try: - fulfillment = CCFulfillment.from_uri(ffill['fulfillment']) + fulfillment = CCFulfillment.from_uri(data['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_) + # `Input.to_dict` + fulfillment = CCFulfillment.from_dict(data['fulfillment']) + fulfills = TransactionLink.from_dict(data['fulfills']) + return cls(fulfillment, data['owners_before'], fulfills) class TransactionLink(object): @@ -131,11 +130,11 @@ class TransactionLink(object): Attributes: txid (str, optional): A Transaction to link to. - cid (int, optional): A Condition's index in a Transaction with id + idx (int, optional): An output's index in a Transaction with id `txid`. """ - def __init__(self, txid=None, cid=None): + def __init__(self, txid=None, idx=None): """Used to point to a specific Condition of a Transaction. Note: @@ -143,18 +142,18 @@ class TransactionLink(object): 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//`. + `//transaction/outputs//`. Args: txid (str, optional): A Transaction to link to. - cid (int, optional): A Condition's index in a Transaction with + idx (int, optional): An Outputs's index in a Transaction with id `txid`. """ self.txid = txid - self.cid = cid + self.idx = idx def __bool__(self): - return self.txid is not None and self.cid is not None + return self.txid is not None and self.idx is not None def __eq__(self, other): # TODO: If `other !== TransactionLink` return `False` @@ -171,7 +170,7 @@ class TransactionLink(object): :class:`~bigchaindb.common.transaction.TransactionLink` """ try: - return cls(link['txid'], link['cid']) + return cls(link['txid'], link['idx']) except TypeError: return cls() @@ -181,12 +180,12 @@ class TransactionLink(object): Returns: (dict|None): The link as an alternative serialization format. """ - if self.txid is None and self.cid is None: + if self.txid is None and self.idx is None: return None else: return { 'txid': self.txid, - 'cid': self.cid, + 'idx': self.idx, } def to_uri(self, path=''): @@ -196,8 +195,8 @@ class TransactionLink(object): self.cid) -class Condition(object): - """A Condition is used to lock an asset. +class Output(object): + """An Output is used to lock an asset. Attributes: fulfillment (:class:`cryptoconditions.Fulfillment`): A Fulfillment @@ -206,27 +205,27 @@ class Condition(object): owners before a Transaction was confirmed. """ - def __init__(self, fulfillment, owners_after=None, amount=1): + def __init__(self, fulfillment, public_keys=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 + public_keys (: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`. + TypeError: if `public_keys` is not instance of `list`. """ - if not isinstance(owners_after, list) and owners_after is not None: - raise TypeError('`owners_after` must be a list instance or None') + if not isinstance(public_keys, list) and public_keys is not None: + raise TypeError('`public_keys` must be a list instance or None') self.fulfillment = fulfillment # TODO: Not sure if we should validate for value here self.amount = amount - self.owners_after = owners_after + self.public_keys = public_keys def __eq__(self, other): # TODO: If `other !== Condition` return `False` @@ -236,11 +235,11 @@ class Condition(object): """Transforms the object to a Python dictionary. Note: - A dictionary serialization of the Fulfillment the Condition was + A dictionary serialization of the Input the Output was derived from is always provided. Returns: - dict: The Condition as an alternative serialization format. + dict: The Output as an alternative serialization format. """ # TODO FOR CC: It must be able to recognize a hashlock condition # and fulfillment! @@ -255,15 +254,15 @@ class Condition(object): except AttributeError: condition['uri'] = self.fulfillment - cond = { - 'owners_after': self.owners_after, + output = { + 'public_keys': self.public_keys, 'condition': condition, 'amount': self.amount } - return cond + return output @classmethod - def generate(cls, owners_after, amount): + def generate(cls, public_keys, amount): """Generates a Condition from a specifically formed tuple or list. Note: @@ -274,7 +273,7 @@ class Condition(object): [(address|condition)*, [(address|condition)*, ...], ...] Args: - owners_after (:obj:`list` of :obj:`str`): The public key of + public_keys (:obj:`list` of :obj:`str`): The public key of the users that should be able to fulfill the Condition that is being created. amount (:obj:`int`): The amount locked by the condition. @@ -283,30 +282,30 @@ class Condition(object): A Condition that can be used in a Transaction. Raises: - TypeError: If `owners_after` is not an instance of `list`. - ValueError: If `owners_after` is an empty list. + TypeError: If `public_keys` is not an instance of `list`. + ValueError: If `public_keys` is an empty list. """ - threshold = len(owners_after) + threshold = len(public_keys) if not isinstance(amount, int): raise TypeError('`amount` must be a int') if amount < 1: raise AmountError('`amount` needs to be greater than zero') - 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' + if not isinstance(public_keys, list): + raise TypeError('`public_keys` must be an instance of list') + if len(public_keys) == 0: + raise ValueError('`public_keys` needs to contain at least one' 'owner') - elif len(owners_after) == 1 and not isinstance(owners_after[0], list): + elif len(public_keys) == 1 and not isinstance(public_keys[0], list): try: - ffill = Ed25519Fulfillment(public_key=owners_after[0]) + ffill = Ed25519Fulfillment(public_key=public_keys[0]) except TypeError: - ffill = owners_after[0] - return cls(ffill, owners_after, amount=amount) + ffill = public_keys[0] + return cls(ffill, public_keys, amount=amount) else: initial_cond = ThresholdSha256Fulfillment(threshold=threshold) - threshold_cond = reduce(cls._gen_condition, owners_after, + threshold_cond = reduce(cls._gen_condition, public_keys, initial_cond) - return cls(threshold_cond, owners_after, amount=amount) + return cls(threshold_cond, public_keys, amount=amount) @classmethod def _gen_condition(cls, initial, current): @@ -356,8 +355,8 @@ class Condition(object): return initial @classmethod - def from_dict(cls, cond): - """Transforms a Python dictionary to a Condition object. + def from_dict(cls, data): + """Transforms a Python dictionary to an Output object. Note: To pass a serialization cycle multiple times, a @@ -366,17 +365,17 @@ class Condition(object): anymore. Args: - cond (dict): The Condition to be transformed. + data (dict): The dict to be transformed. Returns: - :class:`~bigchaindb.common.transaction.Condition` + :class:`~bigchaindb.common.transaction.Output` """ try: - fulfillment = CCFulfillment.from_dict(cond['condition']['details']) + fulfillment = CCFulfillment.from_dict(data['condition']['details']) except KeyError: # NOTE: Hashlock condition case - fulfillment = cond['condition']['uri'] - return cls(fulfillment, cond['owners_after'], cond['amount']) + fulfillment = data['condition']['uri'] + return cls(fulfillment, data['public_keys'], data['amount']) class Asset(object): @@ -566,16 +565,16 @@ class Transaction(object): """A Transaction is used to create and transfer assets. Note: - For adding Fulfillments and Conditions, this class provides methods + For adding Inputs and Outputs, 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 + inputs (:obj:`list` of :class:`~bigchaindb.common. + transaction.Input`, optional): Define the assets to spend. - conditions (:obj:`list` of :class:`~bigchaindb.common. - transaction.Condition`, optional): Define the assets to lock. + outputs (:obj:`list` of :class:`~bigchaindb.common. + transaction.Output`, optional): Define the assets to lock. metadata (dict): Metadata to be stored along with the Transaction. version (int): Defines the version number of a Transaction. @@ -586,7 +585,7 @@ class Transaction(object): ALLOWED_OPERATIONS = (CREATE, TRANSFER, GENESIS) VERSION = 1 - def __init__(self, operation, asset, fulfillments=None, conditions=None, + def __init__(self, operation, asset, inputs=None, outputs=None, metadata=None, version=None): """The constructor allows to create a customizable Transaction. @@ -598,10 +597,10 @@ class Transaction(object): 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. + inputs (:obj:`list` of :class:`~bigchaindb.common. transaction.Fulfillment`, optional): Define the assets to spend. - conditions (:obj:`list` of :class:`~bigchaindb.common. + outputs (:obj:`list` of :class:`~bigchaindb.common. transaction.Condition`, optional): Define the assets to lock. metadata (dict): @@ -619,11 +618,11 @@ class Transaction(object): not asset and operation != Transaction.CREATE): raise TypeError('`asset` must be an Asset instance') - if conditions and not isinstance(conditions, list): - raise TypeError('`conditions` must be a list instance or None') + if outputs and not isinstance(outputs, list): + raise TypeError('`outputs` must be a list instance or None') - if fulfillments and not isinstance(fulfillments, list): - raise TypeError('`fulfillments` must be a list instance or None') + if inputs and not isinstance(inputs, list): + raise TypeError('`inputs` must be a list instance or None') if metadata is not None and not isinstance(metadata, dict): raise TypeError('`metadata` must be a dict or None') @@ -631,21 +630,21 @@ class Transaction(object): self.version = version if version is not None else self.VERSION self.operation = operation self.asset = asset if asset else Asset() - self.conditions = conditions if conditions else [] - self.fulfillments = fulfillments if fulfillments else [] + self.outputs = outputs if outputs else [] + self.inputs = inputs if inputs else [] self.metadata = metadata # validate asset # we know that each transaction relates to a single asset - # we can sum the amount of all the conditions + # we can sum the amount of all the outputs # for transactions other then CREATE we only have an id so there is # nothing we can validate if self.operation == self.CREATE: - amount = sum([condition.amount for condition in self.conditions]) + amount = sum([condition.amount for condition in self.outputs]) self.asset.validate_asset(amount=amount) @classmethod - def create(cls, owners_before, owners_after, metadata=None, asset=None): + def create(cls, creators, recipients, metadata=None, asset=None): """A simple way to generate a `CREATE` transaction. Note: @@ -659,10 +658,10 @@ class Transaction(object): - 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. + creators (:obj:`list` of :obj:`str`): A list of keys that + represent the creators of this Transaction. + recipients (:obj:`list` of :obj:`str`): A list of keys that + represent the recipients of this Transaction. metadata (dict): Python dictionary to be stored along with the Transaction. asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset @@ -671,50 +670,50 @@ class Transaction(object): 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') - if len(owners_before) == 0: - raise ValueError('`owners_before` list cannot be empty') - if len(owners_after) == 0: - raise ValueError('`owners_after` list cannot be empty') + if not isinstance(creators, list): + raise TypeError('`creators` must be a list instance') + if not isinstance(recipients, list): + raise TypeError('`recipients` must be a list instance') + if len(creators) == 0: + raise ValueError('`creators` list cannot be empty') + if len(recipients) == 0: + raise ValueError('`recipients` list cannot be empty') - fulfillments = [] - conditions = [] + inputs = [] + outputs = [] - # generate_conditions - for owner_after in owners_after: - if not isinstance(owner_after, tuple) or len(owner_after) != 2: - raise ValueError(('Each `owner_after` in the list must be a' + # generate_outputs + for recipient in recipients: + if not isinstance(recipient, tuple) or len(recipient) != 2: + raise ValueError(('Each `recipient` in the list must be a' ' tuple of `([],' ' )`')) - pub_keys, amount = owner_after - conditions.append(Condition.generate(pub_keys, amount)) + pub_keys, amount = recipient + outputs.append(Output.generate(pub_keys, amount)) - # generate fulfillments - fulfillments.append(Fulfillment.generate(owners_before)) + # generate inputs + inputs.append(Input.generate(creators)) - return cls(cls.CREATE, asset, fulfillments, conditions, metadata) + return cls(cls.CREATE, asset, inputs, outputs, metadata) @classmethod - def transfer(cls, inputs, owners_after, asset, metadata=None): + def transfer(cls, inputs, recipients, 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 + `recipients` 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 + 1. The index of a `recipient` 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, + 2. `recipients` 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` @@ -723,11 +722,11 @@ class Transaction(object): 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. + Input`): Converted `Output`s, intended to + be used as inputs in the transfer to generate. + recipients (:obj:`list` of :obj:`str`): A list of + ([keys],amount) that represent the recipients 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 @@ -740,22 +739,22 @@ class Transaction(object): 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') - if len(owners_after) == 0: - raise ValueError('`owners_after` list cannot be empty') + if not isinstance(recipients, list): + raise TypeError('`recipients` must be a list instance') + if len(recipients) == 0: + raise ValueError('`recipients` list cannot be empty') - conditions = [] - for owner_after in owners_after: - if not isinstance(owner_after, tuple) or len(owner_after) != 2: - raise ValueError(('Each `owner_after` in the list must be a' + outputs = [] + for recipient in recipients: + if not isinstance(recipient, tuple) or len(recipient) != 2: + raise ValueError(('Each `recipient` in the list must be a' ' tuple of `([],' ' )`')) - pub_keys, amount = owner_after - conditions.append(Condition.generate(pub_keys, amount)) + pub_keys, amount = recipient + outputs.append(Output.generate(pub_keys, amount)) inputs = deepcopy(inputs) - return cls(cls.TRANSFER, asset, inputs, conditions, metadata) + return cls(cls.TRANSFER, asset, inputs, outputs, metadata) def __eq__(self, other): try: @@ -764,62 +763,59 @@ class Transaction(object): return False return self.to_dict() == other - def to_inputs(self, condition_indices=None): - """Converts a Transaction's Conditions to spendable Fulfillments. + def to_inputs(self, indices=None): + """Converts a Transaction's outputs to spendable inputs. Note: - Takes the Transaction's Conditions and derives Fulfillments - from it that can then be passed into `Transaction.transfer` as + Takes the Transaction's outputs and derives inputs + from 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. + A list of integers can be passed to `indices` that + defines which outputs should be returned as inputs. + If no `indices` are passed (empty list or None) all + outputs of the Transaction are returned. Args: - condition_indices (:obj:`list` of int): Defines which - Conditions should be returned as inputs. + indices (:obj:`list` of int): Defines which + outputs should be returned as inputs. Returns: :obj:`list` of :class:`~bigchaindb.common.transaction. - Fulfillment` + Input` """ - # NOTE: If no condition indices are passed, we just assume to - # take all conditions as inputs. - indices = condition_indices or range(len(self.conditions)) + indices = indices or range(len(self.outputs)) return [ - Fulfillment(self.conditions[cid].fulfillment, - self.conditions[cid].owners_after, - TransactionLink(self.id, cid)) - for cid in indices + Input(self.outputs[idx].fulfillment, + self.outputs[idx].public_keys, + TransactionLink(self.id, idx)) + for idx in indices ] - def add_fulfillment(self, fulfillment): - """Adds a Fulfillment to a Transaction's list of Fulfillments. + def add_input(self, input): + """Adds an input to a Transaction's list of inputs. Args: fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`): A Fulfillment to be added to the - Transaction. + Input`): An Input to be added to the Transaction. """ - if not isinstance(fulfillment, Fulfillment): - raise TypeError('`fulfillment` must be a Fulfillment instance') - self.fulfillments.append(fulfillment) + if not isinstance(input, Input): + raise TypeError('`input` must be a Input instance') + self.inputs.append(input) - def add_condition(self, condition): - """Adds a Condition to a Transaction's list of Conditions. + def add_output(self, output): + """Adds an output to a Transaction's list of outputs. Args: - condition (:class:`~bigchaindb.common.transaction. - Condition`): A Condition to be added to the + output (:class:`~bigchaindb.common.transaction. + Output`): An Output 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) + if not isinstance(output, Output): + raise TypeError('`output` must be an Output instance or None') + self.outputs.append(output) def sign(self, private_keys): - """Fulfills a previous Transaction's Condition by signing Fulfillments. + """Fulfills a previous Transaction's Output by signing Inputs. Note: This method works only for the following Cryptoconditions @@ -861,23 +857,22 @@ class Transaction(object): key_pairs = {gen_public_key(PrivateKey(private_key)): PrivateKey(private_key) for private_key in private_keys} - for index, fulfillment in enumerate(self.fulfillments): - # NOTE: We clone the current transaction but only add the condition - # and fulfillment we're currently working on plus all + for index, input in enumerate(self.inputs): + # NOTE: We clone the current transaction but only add the output + # and input we're currently working on plus all # previously signed ones. - tx_partial = Transaction(self.operation, self.asset, [fulfillment], - self.conditions, self.metadata, + tx_partial = Transaction(self.operation, self.asset, [input], + self.outputs, self.metadata, 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) + self._sign_input(input, 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. + def _sign_input(self, input, index, tx_serialized, key_pairs): + """Signs a single Input with a partial Transaction as message. Note: This method works only for the following Cryptoconditions @@ -886,67 +881,65 @@ class Transaction(object): - 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. + input (:class:`~bigchaindb.common.transaction. + Input`) The Input to be signed. + index (int): The index of the input 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, + if isinstance(input.fulfillment, Ed25519Fulfillment): + self._sign_simple_signature_fulfillment(input, index, tx_serialized, key_pairs) - elif isinstance(fulfillment.fulfillment, ThresholdSha256Fulfillment): - self._sign_threshold_signature_fulfillment(fulfillment, index, + elif isinstance(input.fulfillment, ThresholdSha256Fulfillment): + self._sign_threshold_signature_fulfillment(input, 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, + def _sign_simple_signature_fulfillment(self, input, 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 + input (:class:`~bigchaindb.common.transaction. + Input`) The input to be signed. + index (int): The index of the input 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 + # reference, we remove the reference of input 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] + input = deepcopy(input) + public_key = input.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]) + input.fulfillment.sign(tx_serialized.encode(), key_pairs[public_key]) except KeyError: raise KeypairMismatchException('Public key {} is not a pair to ' 'any of the private keys' - .format(owner_before)) - self.fulfillments[index] = fulfillment + .format(public_key)) + self.inputs[index] = input - def _sign_threshold_signature_fulfillment(self, fulfillment, index, + def _sign_threshold_signature_fulfillment(self, input, 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 + input (:class:`~bigchaindb.common.transaction. + Input`) The Input to be signed. + index (int): The index of the Input 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: + input = deepcopy(input) + for owner_before in input.owners_before: try: # TODO: CC should throw a KeypairMismatchException, instead of # our manual mapping here @@ -957,7 +950,7 @@ class Transaction(object): # TODO FOR CC: `get_subcondition` is singular. One would not # expect to get a list back. - ccffill = fulfillment.fulfillment + ccffill = input.fulfillment subffill = ccffill.get_subcondition_from_vk(owner_before)[0] except IndexError: raise KeypairMismatchException('Public key {} cannot be found ' @@ -973,102 +966,98 @@ class Transaction(object): # 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 + self.inputs[index] = input - def fulfillments_valid(self, input_conditions=None): - """Validates the Fulfillments in the Transaction against given - Conditions. + def inputs_valid(self, outputs=None): + """Validates the Inputs in the Transaction against given + Outputs. Note: Given a `CREATE` or `GENESIS` Transaction is passed, - dummyvalues for Conditions are submitted for validation that + dummyvalues for Outputs 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. + outputs (:obj:`list` of :class:`~bigchaindb.common. + transaction.Output`): A list of Outputs to check the + Inputs against. Returns: - bool: If all Fulfillments are valid. + bool: If all Inputs 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 + # to check for outputs, 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]) + return self._inputs_valid(['dummyvalue' + for _ in self.inputs]) elif self.operation == Transaction.TRANSFER: - return self._fulfillments_valid([cond.fulfillment.condition_uri - for cond in input_conditions]) + return self._inputs_valid([output.fulfillment.condition_uri + for output in outputs]) 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. + def _inputs_valid(self, output_uris): + """Validates an Input against a given set of Outputs. Note: - The number of `input_condition_uris` must be equal to the - number of Fulfillments a Transaction has. + The number of `output_uris` must be equal to the + number of Inputs a Transaction has. Args: - input_condition_uris (:obj:`list` of :obj:`str`): A list of - Conditions to check the Fulfillments against. + output_uris (:obj:`list` of :obj:`str`): A list of + Outputs to check the Inputs against. Returns: - bool: If all Fulfillments are valid. + bool: If all Outputs are valid. """ - input_condition_uris_count = len(input_condition_uris) - fulfillments_count = len(self.fulfillments) - def gen_tx(fulfillment, condition, input_condition_uri=None): + if len(self.inputs) != len(output_uris): + raise ValueError('Inputs and ' + 'output_uris must have the same count') + + def gen_tx(input, output, output_uri=None): """Splits multiple IO Transactions into partial single IO Transactions. """ - tx = Transaction(self.operation, self.asset, [fulfillment], - self.conditions, self.metadata, self.version) + tx = Transaction(self.operation, self.asset, [input], + self.outputs, self.metadata, 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) + return Transaction._input_valid(input, self.operation, + tx_serialized, output_uri) - if not fulfillments_count == input_condition_uris_count: - raise ValueError('Fulfillments and ' - 'input_condition_uris must have the same count') - - partial_transactions = map(gen_tx, self.fulfillments, - self.conditions, input_condition_uris) + partial_transactions = map(gen_tx, self.inputs, + self.outputs, output_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. + def _input_valid(input, operation, tx_serialized, output_uri=None): + """Validates a single Input against a single Output. Note: In case of a `CREATE` or `GENESIS` Transaction, this method - does not validate against `input_condition_uri`. + does not validate against `output_uri`. Args: - fulfillment (:class:`~bigchaindb.common.transaction. - Fulfillment`) The Fulfillment to be signed. + input (:class:`~bigchaindb.common.transaction. + Input`) The Input 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. + output_uri (str, optional): An Output to check the + Input against. Returns: - bool: If the Fulfillment is valid. + bool: If the Input is valid. """ - ccffill = fulfillment.fulfillment + ccffill = input.fulfillment try: parsed_ffill = CCFulfillment.from_uri(ccffill.serialize_uri()) except (TypeError, ValueError, ParsingError): @@ -1076,18 +1065,19 @@ class Transaction(object): 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 + # output is always validate to `True`. + output_valid = True else: - input_cond_valid = input_condition_uri == ccffill.condition_uri + output_valid = output_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 + ffill_valid = parsed_ffill.validate(message=tx_serialized.encode(), + now=gen_timestamp()) + return output_valid and ffill_valid def to_dict(self): """Transforms the object to a Python dictionary. @@ -1102,10 +1092,8 @@ class Transaction(object): asset = {'id': self.asset.data_id} tx = { - 'fulfillments': [fulfillment.to_dict() for fulfillment - in self.fulfillments], - 'conditions': [condition.to_dict() for condition - in self.conditions], + 'inputs': [input.to_dict() for input in self.inputs], + 'outputs': [output.to_dict() for output in self.outputs], 'operation': str(self.operation), 'metadata': self.metadata, 'asset': asset, @@ -1134,12 +1122,12 @@ class Transaction(object): # 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['fulfillments']: + for input in tx_dict['inputs']: # 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 + input['fulfillment'] = None return tx_dict @staticmethod @@ -1196,14 +1184,12 @@ class Transaction(object): :class:`~bigchaindb.common.transaction.Transaction` """ cls.validate_structure(tx) - fulfillments = [Fulfillment.from_dict(fulfillment) for fulfillment - in tx['fulfillments']] - conditions = [Condition.from_dict(condition) for condition - in tx['conditions']] + inputs = [Input.from_dict(input) for input in tx['inputs']] + outputs = [Output.from_dict(output) for output in tx['outputs']] if tx['operation'] in [cls.CREATE, cls.GENESIS]: asset = Asset.from_dict(tx['asset']) else: asset = AssetLink.from_dict(tx['asset']) - return cls(tx['operation'], asset, fulfillments, conditions, + return cls(tx['operation'], asset, inputs, outputs, tx['metadata'], tx['version']) diff --git a/bigchaindb/core.py b/bigchaindb/core.py index f0e1b89c..a71bbf64 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -356,7 +356,7 @@ class Bigchain(object): if cursor: return Asset.from_dict(cursor[0]['asset']) - def get_spent(self, txid, cid): + def get_spent(self, txid, idx): """Check if a `txid` was already used as an input. A transaction can be used as an input for another transaction. Bigchain needs to make sure that a @@ -364,15 +364,16 @@ class Bigchain(object): Args: txid (str): The id of the transaction - cid (num): the index of the condition in the respective transaction + idx (num): the index of the output in the respective transaction Returns: The transaction (Transaction) that used the `txid` as an input else `None` """ # checks if an input was already spent - # checks if the bigchain has any transaction with input {'txid': ..., 'cid': ...} - transactions = list(backend.query.get_spent(self.connection, txid, cid)) + # checks if the bigchain has any transaction with input {'txid': ..., + # 'idx': ...} + transactions = list(backend.query.get_spent(self.connection, txid, idx)) # a transaction_id should have been spent at most one time if transactions: @@ -404,7 +405,7 @@ class Bigchain(object): owner (str): base58 encoded public key. Returns: - :obj:`list` of TransactionLink: list of ``txid`` s and ``cid`` s + :obj:`list` of TransactionLink: list of ``txid`` s and ``idx`` s pointing to another transaction's condition """ @@ -421,22 +422,22 @@ class Bigchain(object): # NOTE: It's OK to not serialize the transaction here, as we do not # use it after the execution of this function. - # a transaction can contain multiple outputs (conditions) so we need to iterate over all of them + # a transaction can contain multiple outputs so we need to iterate over all of them # to get a list of outputs available to spend - for index, cond in enumerate(tx['conditions']): + for index, out in enumerate(tx['outputs']): # for simple signature conditions there are no subfulfillments # check if the owner is in the condition `owners_after` - if len(cond['owners_after']) == 1: - if cond['condition']['details']['public_key'] == owner: + if len(out['public_keys']) == 1: + if out['condition']['details']['public_key'] == owner: tx_link = TransactionLink(tx['id'], index) else: - # for transactions with multiple `owners_after` there will be several subfulfillments nested + # for transactions with multiple `public_keys` there will be several subfulfillments nested # in the condition. We need to iterate the subfulfillments to make sure there is a # subfulfillment for `owner` - if util.condition_details_has_owner(cond['condition']['details'], owner): + if util.condition_details_has_owner(out['condition']['details'], owner): tx_link = TransactionLink(tx['id'], index) # check if input was already spent - if not self.get_spent(tx_link.txid, tx_link.cid): + if not self.get_spent(tx_link.txid, tx_link.idx): owned.append(tx_link) return owned diff --git a/bigchaindb/models.py b/bigchaindb/models.py index bca182c3..84d98710 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -33,18 +33,18 @@ class Transaction(Transaction): InvalidHash: if the hash of the transaction is wrong InvalidSignature: if the signature of the transaction is wrong """ - if len(self.fulfillments) == 0: - raise ValueError('Transaction contains no fulfillments') + if len(self.inputs) == 0: + raise ValueError('Transaction contains no inputs') input_conditions = [] - inputs_defined = all([ffill.tx_input for ffill in self.fulfillments]) + inputs_defined = all([inp.fulfills for inp in self.inputs]) if self.operation in (Transaction.CREATE, Transaction.GENESIS): # validate inputs if inputs_defined: raise ValueError('A CREATE operation has no inputs') # validate asset - amount = sum([condition.amount for condition in self.conditions]) + amount = sum([out.amount for out in self.outputs]) self.asset.validate_asset(amount=amount) elif self.operation == Transaction.TRANSFER: if not inputs_defined: @@ -54,9 +54,9 @@ class Transaction(Transaction): # store the inputs so that we can check if the asset ids match input_txs = [] input_amount = 0 - for ffill in self.fulfillments: - input_txid = ffill.tx_input.txid - input_cid = ffill.tx_input.cid + for input in self.inputs: + input_txid = input.fulfills.txid + input_idx = input.fulfills.idx input_tx, status = bigchain.\ get_transaction(input_txid, include_status=True) @@ -69,23 +69,22 @@ class Transaction(Transaction): 'input `{}` does not exist in a valid block'.format( input_txid)) - spent = bigchain.get_spent(input_txid, ffill.tx_input.cid) + spent = bigchain.get_spent(input_txid, input_idx) if spent and spent.id != self.id: raise DoubleSpend('input `{}` was already spent' .format(input_txid)) - input_conditions.append(input_tx.conditions[input_cid]) + input_conditions.append(input_tx.outputs[input_idx]) input_txs.append(input_tx) - if input_tx.conditions[input_cid].amount < 1: + if input_tx.outputs[input_idx].amount < 1: raise AmountError('`amount` needs to be greater than zero') - input_amount += input_tx.conditions[input_cid].amount + input_amount += input_tx.outputs[input_idx].amount # validate asset id asset_id = Asset.get_asset_id(input_txs) if asset_id != self.asset.data_id: - raise AssetIdMismatch(('The asset id of the input does not' - ' match the asset id of the' - ' transaction')) + raise AssetIdMismatch('The asset id of the input does not ' + 'match the asset id of the transaction') # get the asset creation to see if its divisible or not asset = bigchain.get_asset_by_id(asset_id) @@ -93,10 +92,10 @@ class Transaction(Transaction): asset.validate_asset(amount=input_amount) # validate the amounts output_amount = 0 - for condition in self.conditions: - if condition.amount < 1: + for output in self.outputs: + if output.amount < 1: raise AmountError('`amount` needs to be greater than zero') - output_amount += condition.amount + output_amount += output.amount if output_amount != input_amount: raise AmountError(('The amount used in the inputs `{}`' @@ -109,7 +108,7 @@ class Transaction(Transaction): raise TypeError('`operation`: `{}` must be either {}.' .format(self.operation, allowed_operations)) - if not self.fulfillments_valid(input_conditions): + if not self.inputs_valid(input_conditions): raise InvalidSignature() else: return self diff --git a/docs/server/generate_schema_documentation.py b/docs/server/generate_schema_documentation.py index 6b638d0a..b40b570e 100644 --- a/docs/server/generate_schema_documentation.py +++ b/docs/server/generate_schema_documentation.py @@ -57,9 +57,9 @@ Transaction Schema * `Transaction`_ -* Condition_ +* Input_ -* Fulfillment_ +* Output_ * Asset_ @@ -71,15 +71,15 @@ Transaction %(transaction)s -Condition +Input ---------- -%(condition)s +%(input)s -Fulfillment +Output ----------- -%(fulfillment)s +%(output)s Asset ----- @@ -99,8 +99,8 @@ def generate_transaction_docs(): doc = TPL_TRANSACTION % { 'transaction': render_section('Transaction', schema), - 'condition': render_section('Condition', defs['condition']), - 'fulfillment': render_section('Fulfillment', defs['fulfillment']), + 'output': render_section('Output', defs['output']), + 'input': render_section('Input', defs['input']), 'asset': render_section('Asset', defs['asset']), 'metadata': render_section('Metadata', defs['metadata']['anyOf'][0]), 'container': 'transaction-schema', diff --git a/tests/assets/test_divisible_assets.py b/tests/assets/test_divisible_assets.py index bcfd5d3e..9716ec5b 100644 --- a/tests/assets/test_divisible_assets.py +++ b/tests/assets/test_divisible_assets.py @@ -17,9 +17,9 @@ def test_single_in_single_own_single_out_single_own_create(b, user_pk): tx_signed = tx.sign([b.me_private]) assert tx_signed.validate(b) == tx_signed - assert len(tx_signed.conditions) == 1 - assert tx_signed.conditions[0].amount == 100 - assert len(tx_signed.fulfillments) == 1 + assert len(tx_signed.outputs) == 1 + assert tx_signed.outputs[0].amount == 100 + assert len(tx_signed.inputs) == 1 # CREATE divisible asset @@ -37,10 +37,10 @@ def test_single_in_single_own_multiple_out_single_own_create(b, user_pk): tx_signed = tx.sign([b.me_private]) assert tx_signed.validate(b) == tx_signed - assert len(tx_signed.conditions) == 2 - assert tx_signed.conditions[0].amount == 50 - assert tx_signed.conditions[1].amount == 50 - assert len(tx_signed.fulfillments) == 1 + assert len(tx_signed.outputs) == 2 + assert tx_signed.outputs[0].amount == 50 + assert tx_signed.outputs[1].amount == 50 + assert len(tx_signed.inputs) == 1 # CREATE divisible asset @@ -57,14 +57,14 @@ def test_single_in_single_own_single_out_multiple_own_create(b, user_pk): tx_signed = tx.sign([b.me_private]) assert tx_signed.validate(b) == tx_signed - assert len(tx_signed.conditions) == 1 - assert tx_signed.conditions[0].amount == 100 + assert len(tx_signed.outputs) == 1 + assert tx_signed.outputs[0].amount == 100 - condition = tx_signed.conditions[0].to_dict() - assert 'subfulfillments' in condition['condition']['details'] - assert len(condition['condition']['details']['subfulfillments']) == 2 + output = tx_signed.outputs[0].to_dict() + assert 'subfulfillments' in output['condition']['details'] + assert len(output['condition']['details']['subfulfillments']) == 2 - assert len(tx_signed.fulfillments) == 1 + assert len(tx_signed.inputs) == 1 # CREATE divisible asset @@ -84,15 +84,15 @@ def test_single_in_single_own_multiple_out_mix_own_create(b, user_pk): tx_signed = tx.sign([b.me_private]) assert tx_signed.validate(b) == tx_signed - assert len(tx_signed.conditions) == 2 - assert tx_signed.conditions[0].amount == 50 - assert tx_signed.conditions[1].amount == 50 + assert len(tx_signed.outputs) == 2 + assert tx_signed.outputs[0].amount == 50 + assert tx_signed.outputs[1].amount == 50 - condition_cid1 = tx_signed.conditions[1].to_dict() - assert 'subfulfillments' in condition_cid1['condition']['details'] - assert len(condition_cid1['condition']['details']['subfulfillments']) == 2 + output_cid1 = tx_signed.outputs[1].to_dict() + assert 'subfulfillments' in output_cid1['condition']['details'] + assert len(output_cid1['condition']['details']['subfulfillments']) == 2 - assert len(tx_signed.fulfillments) == 1 + assert len(tx_signed.inputs) == 1 # CREATE divisible asset @@ -108,11 +108,11 @@ def test_single_in_multiple_own_single_out_single_own_create(b, user_pk, tx = Transaction.create([b.me, user_pk], [([user_pk], 100)], asset=asset) tx_signed = tx.sign([b.me_private, user_sk]) assert tx_signed.validate(b) == tx_signed - assert len(tx_signed.conditions) == 1 - assert tx_signed.conditions[0].amount == 100 - assert len(tx_signed.fulfillments) == 1 + assert len(tx_signed.outputs) == 1 + assert tx_signed.outputs[0].amount == 100 + assert len(tx_signed.inputs) == 1 - ffill = tx_signed.fulfillments[0].fulfillment.to_dict() + ffill = tx_signed.inputs[0].fulfillment.to_dict() assert 'subfulfillments' in ffill assert len(ffill['subfulfillments']) == 2 @@ -150,9 +150,9 @@ def test_single_in_single_own_single_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 - assert len(tx_transfer_signed.fulfillments) == 1 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 + assert len(tx_transfer_signed.inputs) == 1 # TRANSFER divisible asset @@ -185,10 +185,10 @@ def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 2 - assert tx_transfer_signed.conditions[0].amount == 50 - assert tx_transfer_signed.conditions[1].amount == 50 - assert len(tx_transfer_signed.fulfillments) == 1 + assert len(tx_transfer_signed.outputs) == 2 + assert tx_transfer_signed.outputs[0].amount == 50 + assert tx_transfer_signed.outputs[1].amount == 50 + assert len(tx_transfer_signed.inputs) == 1 # TRANSFER divisible asset @@ -221,14 +221,14 @@ def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 - condition = tx_transfer_signed.conditions[0].to_dict() + condition = tx_transfer_signed.outputs[0].to_dict() assert 'subfulfillments' in condition['condition']['details'] assert len(condition['condition']['details']['subfulfillments']) == 2 - assert len(tx_transfer_signed.fulfillments) == 1 + assert len(tx_transfer_signed.inputs) == 1 # TRANSFER divisible asset @@ -262,15 +262,15 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 2 - assert tx_transfer_signed.conditions[0].amount == 50 - assert tx_transfer_signed.conditions[1].amount == 50 + assert len(tx_transfer_signed.outputs) == 2 + assert tx_transfer_signed.outputs[0].amount == 50 + assert tx_transfer_signed.outputs[1].amount == 50 - condition_cid1 = tx_transfer_signed.conditions[1].to_dict() - assert 'subfulfillments' in condition_cid1['condition']['details'] - assert len(condition_cid1['condition']['details']['subfulfillments']) == 2 + output_cid1 = tx_transfer_signed.outputs[1].to_dict() + assert 'subfulfillments' in output_cid1['condition']['details'] + assert len(output_cid1['condition']['details']['subfulfillments']) == 2 - assert len(tx_transfer_signed.fulfillments) == 1 + assert len(tx_transfer_signed.inputs) == 1 # TRANSFER divisible asset @@ -303,11 +303,11 @@ def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 - assert len(tx_transfer_signed.fulfillments) == 1 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 + assert len(tx_transfer_signed.inputs) == 1 - ffill = tx_transfer_signed.fulfillments[0].fulfillment.to_dict() + ffill = tx_transfer_signed.inputs[0].fulfillment.to_dict() assert 'subfulfillments' in ffill assert len(ffill['subfulfillments']) == 2 @@ -342,9 +342,9 @@ def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 - assert len(tx_transfer_signed.fulfillments) == 2 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 + assert len(tx_transfer_signed.inputs) == 2 # TRANSFER divisible asset @@ -379,12 +379,12 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk]) assert tx_transfer_signed.validate(b) - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 - assert len(tx_transfer_signed.fulfillments) == 2 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 + assert len(tx_transfer_signed.inputs) == 2 - ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict() - ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict() + ffill_fid0 = tx_transfer_signed.inputs[0].fulfillment.to_dict() + ffill_fid1 = tx_transfer_signed.inputs[1].fulfillment.to_dict() assert 'subfulfillments' in ffill_fid0 assert 'subfulfillments' in ffill_fid1 assert len(ffill_fid0['subfulfillments']) == 2 @@ -424,12 +424,12 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 100 - assert len(tx_transfer_signed.fulfillments) == 2 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 100 + assert len(tx_transfer_signed.inputs) == 2 - ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict() - ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict() + ffill_fid0 = tx_transfer_signed.inputs[0].fulfillment.to_dict() + ffill_fid1 = tx_transfer_signed.inputs[1].fulfillment.to_dict() assert 'subfulfillments' not in ffill_fid0 assert 'subfulfillments' in ffill_fid1 assert len(ffill_fid1['subfulfillments']) == 2 @@ -470,19 +470,19 @@ def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk, tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 2 - assert tx_transfer_signed.conditions[0].amount == 50 - assert tx_transfer_signed.conditions[1].amount == 50 - assert len(tx_transfer_signed.fulfillments) == 2 + assert len(tx_transfer_signed.outputs) == 2 + assert tx_transfer_signed.outputs[0].amount == 50 + assert tx_transfer_signed.outputs[1].amount == 50 + assert len(tx_transfer_signed.inputs) == 2 - cond_cid0 = tx_transfer_signed.conditions[0].to_dict() - cond_cid1 = tx_transfer_signed.conditions[1].to_dict() + cond_cid0 = tx_transfer_signed.outputs[0].to_dict() + cond_cid1 = tx_transfer_signed.outputs[1].to_dict() assert 'subfulfillments' not in cond_cid0['condition']['details'] assert 'subfulfillments' in cond_cid1['condition']['details'] assert len(cond_cid1['condition']['details']['subfulfillments']) == 2 - ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict() - ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict() + ffill_fid0 = tx_transfer_signed.inputs[0].fulfillment.to_dict() + ffill_fid1 = tx_transfer_signed.inputs[1].fulfillment.to_dict() assert 'subfulfillments' not in ffill_fid0 assert 'subfulfillments' in ffill_fid1 assert len(ffill_fid1['subfulfillments']) == 2 @@ -541,12 +541,12 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk): tx_transfer2_signed = tx_transfer2.sign([user_sk]) assert tx_transfer2_signed.validate(b) == tx_transfer2_signed - assert len(tx_transfer2_signed.conditions) == 1 - assert tx_transfer2_signed.conditions[0].amount == 100 - assert len(tx_transfer2_signed.fulfillments) == 2 + assert len(tx_transfer2_signed.outputs) == 1 + assert tx_transfer2_signed.outputs[0].amount == 100 + assert len(tx_transfer2_signed.inputs) == 2 - fid0_input = tx_transfer2_signed.fulfillments[0].to_dict()['input']['txid'] - fid1_input = tx_transfer2_signed.fulfillments[1].to_dict()['input']['txid'] + fid0_input = tx_transfer2_signed.inputs[0].fulfills.txid + fid1_input = tx_transfer2_signed.inputs[1].fulfills.txid assert fid0_input == tx_create.id assert fid1_input == tx_transfer1.id @@ -651,8 +651,8 @@ def test_sum_amount(b, user_pk, user_sk): tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 1 - assert tx_transfer_signed.conditions[0].amount == 3 + assert len(tx_transfer_signed.outputs) == 1 + assert tx_transfer_signed.outputs[0].amount == 3 @pytest.mark.usefixtures('inputs') @@ -681,9 +681,9 @@ def test_divide(b, user_pk, user_sk): tx_transfer_signed = tx_transfer.sign([user_sk]) assert tx_transfer_signed.validate(b) == tx_transfer_signed - assert len(tx_transfer_signed.conditions) == 3 - for condition in tx_transfer_signed.conditions: - assert condition.amount == 1 + assert len(tx_transfer_signed.outputs) == 3 + for output in tx_transfer_signed.outputs: + assert output.amount == 1 # Check that negative inputs are caught when creating a TRANSFER transaction @@ -737,7 +737,7 @@ def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk): tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 4), ([b.me], 1)], asset=tx_create.asset) - tx_transfer.conditions[1].amount = -1 + tx_transfer.outputs[1].amount = -1 tx_transfer_signed = tx_transfer.sign([user_sk]) with pytest.raises(AmountError): @@ -769,7 +769,7 @@ def test_non_positive_amounts_on_create_validate(b, user_pk): asset = Asset(divisible=True) tx_create = Transaction.create([b.me], [([user_pk], 3)], asset=asset) - tx_create.conditions[0].amount = -3 + tx_create.outputs[0].amount = -3 with patch.object(Asset, 'validate_asset', return_value=None): tx_create_signed = tx_create.sign([b.me_private]) diff --git a/tests/common/conftest.py b/tests/common/conftest.py index a54daf20..e906902a 100644 --- a/tests/common/conftest.py +++ b/tests/common/conftest.py @@ -86,39 +86,39 @@ def user2_Ed25519(user2_pub): @pytest.fixture -def user_ffill(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Fulfillment - return Fulfillment(user_Ed25519, [user_pub]) +def user_input(user_Ed25519, user_pub): + from bigchaindb.common.transaction import Input + return Input(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]) +def user2_input(user2_Ed25519, user2_pub): + from bigchaindb.common.transaction import Input + return Input(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]) +def user_user2_threshold_output(user_user2_threshold, user_pub, user2_pub): + from bigchaindb.common.transaction import Output + return Output(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]) +def user_user2_threshold_input(user_user2_threshold, user_pub, user2_pub): + from bigchaindb.common.transaction import Input + return Input(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]) +def user_output(user_Ed25519, user_pub): + from bigchaindb.common.transaction import Output + return Output(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]) +def user2_output(user2_Ed25519, user2_pub): + from bigchaindb.common.transaction import Output + return Output(user2_Ed25519, [user2_pub]) @pytest.fixture @@ -137,9 +137,10 @@ def uuid4(): @pytest.fixture -def utx(user_ffill, user_cond): +def utx(user_input, user_output): from bigchaindb.common.transaction import Transaction, Asset - return Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond]) + return Transaction(Transaction.CREATE, Asset(), [user_input], + [user_output]) @pytest.fixture @@ -148,14 +149,14 @@ def tx(utx, user_priv): @pytest.fixture -def transfer_utx(user_cond, user2_cond, utx): - from bigchaindb.common.transaction import (Fulfillment, TransactionLink, +def transfer_utx(user_output, user2_output, utx): + from bigchaindb.common.transaction import (Input, 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]) + user_output = user_output.to_dict() + input = Input(utx.outputs[0].fulfillment, + user_output['public_keys'], + TransactionLink(utx.id, 0)) + return Transaction('TRANSFER', Asset(), [input], [user2_output]) @pytest.fixture diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py index ebd2b8e7..7d095250 100644 --- a/tests/common/test_transaction.py +++ b/tests/common/test_transaction.py @@ -2,111 +2,110 @@ from pytest import raises from unittest.mock import patch -def test_fulfillment_serialization(ffill_uri, user_pub): - from bigchaindb.common.transaction import Fulfillment - from cryptoconditions import Fulfillment as CCFulfillment +def test_input_serialization(ffill_uri, user_pub): + from bigchaindb.common.transaction import Input + from cryptoconditions import Fulfillment expected = { 'owners_before': [user_pub], 'fulfillment': ffill_uri, - 'input': None, + 'fulfills': None, } - ffill = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) - assert ffill.to_dict() == expected + input = Input(Fulfillment.from_uri(ffill_uri), [user_pub]) + assert input.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 +def test_input_deserialization_with_uri(ffill_uri, user_pub): + from bigchaindb.common.transaction import Input + from cryptoconditions import Fulfillment - expected = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) + expected = Input(Fulfillment.from_uri(ffill_uri), [user_pub]) ffill = { 'owners_before': [user_pub], 'fulfillment': ffill_uri, - 'input': None, + 'fulfills': None, } - ffill = Fulfillment.from_dict(ffill) + input = Input.from_dict(ffill) - assert ffill == expected + assert input == expected -def test_fulfillment_deserialization_with_invalid_fulfillment(user_pub): - from bigchaindb.common.transaction import Fulfillment +def test_input_deserialization_with_invalid_fulfillment(user_pub): + from bigchaindb.common.transaction import Input ffill = { 'owners_before': [user_pub], 'fulfillment': None, - 'input': None, + 'fulfills': None, } with raises(TypeError): - Fulfillment.from_dict(ffill) + Input.from_dict(ffill) -def test_fulfillment_deserialization_with_invalid_fulfillment_uri(user_pub): +def test_input_deserialization_with_invalid_fulfillment_uri(user_pub): from bigchaindb.common.exceptions import InvalidSignature - from bigchaindb.common.transaction import Fulfillment + from bigchaindb.common.transaction import Input ffill = { 'owners_before': [user_pub], 'fulfillment': 'an invalid fulfillment', - 'input': None, + 'fulfills': None, } with raises(InvalidSignature): - Fulfillment.from_dict(ffill) + Input.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 +def test_input_deserialization_with_unsigned_fulfillment(ffill_uri, user_pub): + from bigchaindb.common.transaction import Input + from cryptoconditions import Fulfillment - expected = Fulfillment(CCFulfillment.from_uri(ffill_uri), [user_pub]) + expected = Input(Fulfillment.from_uri(ffill_uri), [user_pub]) ffill = { 'owners_before': [user_pub], - 'fulfillment': CCFulfillment.from_uri(ffill_uri), - 'input': None, + 'fulfillment': Fulfillment.from_uri(ffill_uri), + 'fulfills': None, } - ffill = Fulfillment.from_dict(ffill) + input = Input.from_dict(ffill) - assert ffill == expected + assert input == expected def test_condition_serialization(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output expected = { 'condition': { 'uri': user_Ed25519.condition_uri, 'details': user_Ed25519.to_dict(), }, - 'owners_after': [user_pub], + 'public_keys': [user_pub], 'amount': 1, } - cond = Condition(user_Ed25519, [user_pub], 1) + cond = Output(user_Ed25519, [user_pub], 1) assert cond.to_dict() == expected def test_condition_deserialization(user_Ed25519, user_pub): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output - expected = Condition(user_Ed25519, [user_pub], 1) + expected = Output(user_Ed25519, [user_pub], 1) cond = { 'condition': { 'uri': user_Ed25519.condition_uri, 'details': user_Ed25519.to_dict() }, - 'owners_after': [user_pub], + 'public_keys': [user_pub], 'amount': 1, } - cond = Condition.from_dict(cond) + cond = Output.from_dict(cond) assert cond == expected def test_condition_hashlock_serialization(): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output from cryptoconditions import PreimageSha256Fulfillment secret = b'wow much secret' @@ -116,44 +115,44 @@ def test_condition_hashlock_serialization(): 'condition': { 'uri': hashlock, }, - 'owners_after': None, + 'public_keys': None, 'amount': 1, } - cond = Condition(hashlock, amount=1) + cond = Output(hashlock, amount=1) assert cond.to_dict() == expected def test_condition_hashlock_deserialization(): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output from cryptoconditions import PreimageSha256Fulfillment secret = b'wow much secret' hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri - expected = Condition(hashlock, amount=1) + expected = Output(hashlock, amount=1) cond = { 'condition': { 'uri': hashlock }, - 'owners_after': None, + 'public_keys': None, 'amount': 1, } - cond = Condition.from_dict(cond) + cond = Output.from_dict(cond) assert cond == expected def test_invalid_condition_initialization(cond_uri, user_pub): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output with raises(TypeError): - Condition(cond_uri, user_pub) + Output(cond_uri, user_pub) def test_generate_conditions_split_half_recursive(user_pub, user2_pub, user3_pub): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) @@ -167,13 +166,13 @@ def test_generate_conditions_split_half_recursive(user_pub, user2_pub, expected_threshold.add_subfulfillment(expected_simple3) expected.add_subfulfillment(expected_threshold) - cond = Condition.generate([user_pub, [user2_pub, expected_simple3]], 1) + cond = Output.generate([user_pub, [user2_pub, expected_simple3]], 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 bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) @@ -187,12 +186,12 @@ def test_generate_conditions_split_half_single_owner(user_pub, user2_pub, expected.add_subfulfillment(expected_threshold) expected.add_subfulfillment(expected_simple1) - cond = Condition.generate([[expected_simple2, user3_pub], user_pub], 1) + cond = Output.generate([[expected_simple2, user3_pub], user_pub], 1) 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 bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment expected_simple1 = Ed25519Fulfillment(public_key=user_pub) @@ -204,42 +203,42 @@ def test_generate_conditions_flat_ownage(user_pub, user2_pub, user3_pub): expected.add_subfulfillment(expected_simple2) expected.add_subfulfillment(expected_simple3) - cond = Condition.generate([user_pub, user2_pub, expected_simple3], 1) + cond = Output.generate([user_pub, user2_pub, expected_simple3], 1) assert cond.fulfillment.to_dict() == expected.to_dict() def test_generate_conditions_single_owner(user_pub): - from bigchaindb.common.transaction import Condition + from bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Fulfillment expected = Ed25519Fulfillment(public_key=user_pub) - cond = Condition.generate([user_pub], 1) + cond = Output.generate([user_pub], 1) 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 bigchaindb.common.transaction import Output from cryptoconditions import Ed25519Fulfillment expected = Ed25519Fulfillment(public_key=user_pub) - cond = Condition.generate([expected], 1) + cond = Output.generate([expected], 1) 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 + from bigchaindb.common.transaction import Output with raises(ValueError): - Condition.generate([], 1) + Output.generate([], 1) with raises(TypeError): - Condition.generate('not a list', 1) + Output.generate('not a list', 1) with raises(ValueError): - Condition.generate([[user_pub, [user2_pub, [user3_pub]]]], 1) + Output.generate([[user_pub, [user2_pub, [user3_pub]]]], 1) with raises(ValueError): - Condition.generate([[user_pub]], 1) + Output.generate([[user_pub]], 1) def test_invalid_transaction_initialization(): @@ -253,21 +252,21 @@ def test_invalid_transaction_initialization(): Transaction( operation='CREATE', asset=Asset(), - conditions='invalid conditions' + outputs='invalid outputs' ) with raises(TypeError): Transaction( operation='CREATE', asset=Asset(), - conditions=[], - fulfillments='invalid fulfillments' + outputs=[], + inputs='invalid inputs' ) with raises(TypeError): Transaction( operation='CREATE', asset=Asset(), - conditions=[], - fulfillments=[], + outputs=[], + inputs=[], metadata='invalid metadata' ) @@ -291,7 +290,7 @@ def test_create_default_asset_on_tx_initialization(): validate_transaction_model(tx) -def test_transaction_serialization(user_ffill, user_cond, data, data_id): +def test_transaction_serialization(user_input, user_output, data, data_id): from bigchaindb.common.transaction import Transaction, Asset from bigchaindb.common.exceptions import ValidationError from .util import validate_transaction_model @@ -301,10 +300,10 @@ def test_transaction_serialization(user_ffill, user_cond, data, data_id): expected = { 'id': tx_id, 'version': Transaction.VERSION, - # NOTE: This test assumes that Fulfillments and Conditions can + # NOTE: This test assumes that Inputs and Outputs can # successfully be serialized - 'fulfillments': [user_ffill.to_dict()], - 'conditions': [user_cond.to_dict()], + 'inputs': [user_input.to_dict()], + 'outputs': [user_output.to_dict()], 'operation': Transaction.CREATE, 'metadata': None, 'asset': { @@ -316,8 +315,8 @@ def test_transaction_serialization(user_ffill, user_cond, data, data_id): } } - tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_ffill], - [user_cond]) + tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_input], + [user_output]) tx_dict = tx.to_dict() tx_dict['id'] = tx_id tx_dict['asset']['id'] = data_id @@ -329,21 +328,20 @@ def test_transaction_serialization(user_ffill, user_cond, data, data_id): validate_transaction_model(tx) -def test_transaction_deserialization(user_ffill, user_cond, data, uuid4): +def test_transaction_deserialization(user_input, user_output, data, uuid4): from bigchaindb.common.transaction import Transaction, Asset from .util import validate_transaction_model - expected_asset = Asset(data, uuid4) - expected = Transaction(Transaction.CREATE, expected_asset, [user_ffill], - [user_cond], None, Transaction.VERSION) + expected = Transaction(Transaction.CREATE, expected_asset, [user_input], + [user_output], None, Transaction.VERSION) tx = { 'version': Transaction.VERSION, - # NOTE: This test assumes that Fulfillments and Conditions can + # NOTE: This test assumes that Inputs and Outputs can # successfully be serialized - 'fulfillments': [user_ffill.to_dict()], - 'conditions': [user_cond.to_dict()], + 'inputs': [user_input.to_dict()], + 'outputs': [user_output.to_dict()], 'operation': Transaction.CREATE, 'metadata': None, 'asset': { @@ -374,13 +372,11 @@ def test_tx_serialization_with_incorrect_hash(utx): utx_dict.pop('id') -def test_invalid_fulfillment_initialization(user_ffill, user_pub): - from bigchaindb.common.transaction import Fulfillment +def test_invalid_fulfillment_initialization(user_input, user_pub): + from bigchaindb.common.transaction import Input with raises(TypeError): - Fulfillment(user_ffill, user_pub) - with raises(TypeError): - Fulfillment(user_ffill, [], tx_input='somethingthatiswrong') + Input(user_input, tx_input='somethingthatiswrong') def test_transaction_link_serialization(): @@ -389,7 +385,7 @@ def test_transaction_link_serialization(): tx_id = 'a transaction id' expected = { 'txid': tx_id, - 'cid': 0, + 'idx': 0, } tx_link = TransactionLink(tx_id, 0) @@ -412,7 +408,7 @@ def test_transaction_link_deserialization(): expected = TransactionLink(tx_id, 0) tx_link = { 'txid': tx_id, - 'cid': 0, + 'idx': 0, } tx_link = TransactionLink.from_dict(tx_link) @@ -517,45 +513,45 @@ def test_eq_asset_link(): assert AssetLink(asset_id_1) != AssetLink(asset_id_2) -def test_add_fulfillment_to_tx(user_ffill): +def test_add_input_to_tx(user_input): from bigchaindb.common.transaction import Transaction, Asset with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset(), [], []) - tx.add_fulfillment(user_ffill) + tx.add_input(user_input) - assert len(tx.fulfillments) == 1 + assert len(tx.inputs) == 1 -def test_add_fulfillment_to_tx_with_invalid_parameters(): +def test_add_input_to_tx_with_invalid_parameters(): from bigchaindb.common.transaction import Transaction, Asset with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset()) with raises(TypeError): - tx.add_fulfillment('somewronginput') + tx.add_input('somewronginput') -def test_add_condition_to_tx(user_cond): +def test_add_output_to_tx(user_output): from bigchaindb.common.transaction import Transaction, Asset from .util import validate_transaction_model with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset()) - tx.add_condition(user_cond) + tx.add_output(user_output) - assert len(tx.conditions) == 1 + assert len(tx.outputs) == 1 validate_transaction_model(tx) -def test_add_condition_to_tx_with_invalid_parameters(): +def test_add_output_to_tx_with_invalid_parameters(): from bigchaindb.common.transaction import Transaction, Asset with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset(), [], []) with raises(TypeError): - tx.add_condition('somewronginput') + tx.add_output('somewronginput') def test_sign_with_invalid_parameters(utx, user_priv): @@ -565,67 +561,60 @@ def test_sign_with_invalid_parameters(utx, user_priv): utx.sign(user_priv) -def test_validate_tx_simple_create_signature(user_ffill, user_cond, user_priv): +def test_validate_tx_simple_create_signature(user_input, user_output, user_priv): from copy import deepcopy from bigchaindb.common.crypto import PrivateKey from bigchaindb.common.transaction import Transaction, Asset from .util import validate_transaction_model - tx = Transaction(Transaction.CREATE, Asset(), [user_ffill], [user_cond]) - expected = deepcopy(user_cond) + tx = Transaction(Transaction.CREATE, Asset(), [user_input], [user_output]) + expected = deepcopy(user_output) expected.fulfillment.sign(str(tx).encode(), PrivateKey(user_priv)) tx.sign([user_priv]) - assert tx.fulfillments[0].to_dict()['fulfillment'] == \ + assert tx.inputs[0].to_dict()['fulfillment'] == \ expected.fulfillment.serialize_uri() - assert tx.fulfillments_valid() is True + assert tx.inputs_valid() is True validate_transaction_model(tx) def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, - user_ffill): + user_input): 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, + utx._sign_simple_signature_fulfillment(user_input, 0, 'somemessage', invalid_key_pair) -def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_ffill, +def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_input, user3_pub, user3_priv): from bigchaindb.common.exceptions import KeypairMismatchException with raises(KeypairMismatchException): - utx._sign_threshold_signature_fulfillment(user_user2_threshold_ffill, + utx._sign_threshold_signature_fulfillment(user_user2_threshold_input, 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] + input_conditions = [out.fulfillment.condition_uri for out in utx.outputs] 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], + assert utx._input_valid(utx.inputs[0], tx_serialized, input_conditions) is False -def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv): +def test_validate_multiple_inputs(user_input, user_output, user_priv): from copy import deepcopy from bigchaindb.common.crypto import PrivateKey @@ -633,33 +622,33 @@ def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv): from .util import validate_transaction_model tx = Transaction(Transaction.CREATE, Asset(divisible=True), - [user_ffill, deepcopy(user_ffill)], - [user_cond, deepcopy(user_cond)]) + [user_input, deepcopy(user_input)], + [user_output, deepcopy(user_output)]) expected_first = deepcopy(tx) expected_second = deepcopy(tx) - expected_first.fulfillments = [expected_first.fulfillments[0]] - expected_second.fulfillments = [expected_second.fulfillments[1]] + expected_first.inputs = [expected_first.inputs[0]] + expected_second.inputs = [expected_second.inputs[1]] expected_first_bytes = str(expected_first).encode() - expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes, + expected_first.inputs[0].fulfillment.sign(expected_first_bytes, PrivateKey(user_priv)) expected_second_bytes = str(expected_second).encode() - expected_second.fulfillments[0].fulfillment.sign(expected_second_bytes, - PrivateKey(user_priv)) + expected_second.inputs[0].fulfillment.sign(expected_second_bytes, + PrivateKey(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 + assert tx.inputs[0].to_dict()['fulfillment'] == \ + expected_first.inputs[0].fulfillment.serialize_uri() + assert tx.inputs[1].to_dict()['fulfillment'] == \ + expected_second.inputs[0].fulfillment.serialize_uri() + assert tx.inputs_valid() is True validate_transaction_model(tx) -def test_validate_tx_threshold_create_signature(user_user2_threshold_ffill, - user_user2_threshold_cond, +def test_validate_tx_threshold_create_signature(user_user2_threshold_input, + user_user2_threshold_output, user_pub, user2_pub, user_priv, @@ -670,83 +659,78 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_ffill, from bigchaindb.common.transaction import Transaction, Asset from .util import validate_transaction_model - tx = Transaction(Transaction.CREATE, Asset(), [user_user2_threshold_ffill], - [user_user2_threshold_cond]) - expected = deepcopy(user_user2_threshold_cond) + tx = Transaction(Transaction.CREATE, Asset(), [user_user2_threshold_input], + [user_user2_threshold_output]) + expected = deepcopy(user_user2_threshold_output) expected.fulfillment.subconditions[0]['body'].sign(str(tx).encode(), PrivateKey(user_priv)) expected.fulfillment.subconditions[1]['body'].sign(str(tx).encode(), PrivateKey(user2_priv)) tx.sign([user_priv, user2_priv]) - assert tx.fulfillments[0].to_dict()['fulfillment'] == \ + assert tx.inputs[0].to_dict()['fulfillment'] == \ expected.fulfillment.serialize_uri() - assert tx.fulfillments_valid() is True + assert tx.inputs_valid() is True validate_transaction_model(tx) -def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond, - user_priv, user2_pub, - user2_priv, user3_pub, - user3_priv): +def test_multiple_input_validation_of_transfer_tx(user_input, user_output, + user_priv, user2_pub, + user2_priv, user3_pub, + user3_priv): from copy import deepcopy from bigchaindb.common.transaction import (Transaction, TransactionLink, - Fulfillment, Condition, Asset) + Input, Output, Asset) from cryptoconditions import Ed25519Fulfillment from .util import validate_transaction_model tx = Transaction(Transaction.CREATE, Asset(divisible=True), - [user_ffill, deepcopy(user_ffill)], - [user_cond, deepcopy(user_cond)]) + [user_input, deepcopy(user_input)], + [user_output, deepcopy(user_output)]) 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) + inputs = [Input(cond.fulfillment, cond.public_keys, + TransactionLink(tx.id, index)) + for index, cond in enumerate(tx.outputs)] + outputs = [Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub]), + Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub])] + transfer_tx = Transaction('TRANSFER', tx.asset, inputs, outputs) transfer_tx = transfer_tx.sign([user_priv]) - assert transfer_tx.fulfillments_valid(tx.conditions) is True + assert transfer_tx.inputs_valid(tx.outputs) is True validate_transaction_model(tx) -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 +def test_validate_inputs_of_transfer_tx_with_invalid_params( + transfer_tx, cond_uri, utx, user2_pub, user_priv): + from bigchaindb.common.transaction import Output 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 + invalid_out = Output(Ed25519Fulfillment.from_uri('cf:0:'), ['invalid']) + assert transfer_tx.inputs_valid([invalid_out]) is False + invalid_out = utx.outputs[0] + invalid_out.public_key = 'invalid' + assert transfer_tx.inputs_valid([invalid_out]) is True with raises(TypeError): - assert transfer_tx.fulfillments_valid(None) is False + assert transfer_tx.inputs_valid(None) is False with raises(AttributeError): - transfer_tx.fulfillments_valid('not a list') + transfer_tx.inputs_valid('not a list') with raises(ValueError): - transfer_tx.fulfillments_valid([]) + transfer_tx.inputs_valid([]) with raises(TypeError): transfer_tx.operation = "Operation that doesn't exist" - transfer_tx.fulfillments_valid([utx.conditions[0]]) + transfer_tx.inputs_valid([utx.outputs[0]]) -def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4): +def test_create_create_transaction_single_io(user_output, user_pub, data, uuid4): from bigchaindb.common.transaction import Transaction, Asset from .util import validate_transaction_model expected = { - 'conditions': [user_cond.to_dict()], + 'outputs': [user_output.to_dict()], 'metadata': data, 'asset': { 'id': uuid4, @@ -755,13 +739,13 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4): 'refillable': False, 'data': data, }, - 'fulfillments': [ + 'inputs': [ { 'owners_before': [ user_pub ], 'fulfillment': None, - 'input': None + 'fulfills': None } ], 'operation': 'CREATE', @@ -771,7 +755,7 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4): asset = Asset(data, uuid4) tx = Transaction.create([user_pub], [([user_pub], 1)], data, asset) tx_dict = tx.to_dict() - tx_dict['fulfillments'][0]['fulfillment'] = None + tx_dict['inputs'][0]['fulfillment'] = None tx_dict.pop('id') assert tx_dict == expected @@ -784,23 +768,23 @@ def test_validate_single_io_create_transaction(user_pub, user_priv, data): tx = Transaction.create([user_pub], [([user_pub], 1)], data, Asset()) tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True + assert tx.inputs_valid() is True -def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub, +def test_create_create_transaction_multiple_io(user_output, user2_output, user_pub, user2_pub): - from bigchaindb.common.transaction import Transaction, Asset, Fulfillment + from bigchaindb.common.transaction import Transaction, Asset, Input # a fulfillment for a create transaction with multiple `owners_before` # is a fulfillment for an implicit threshold condition with # weight = len(owners_before) - ffill = Fulfillment.generate([user_pub, user2_pub]).to_dict() + input = Input.generate([user_pub, user2_pub]).to_dict() expected = { - 'conditions': [user_cond.to_dict(), user2_cond.to_dict()], + 'outputs': [user_output.to_dict(), user2_output.to_dict()], 'metadata': { 'message': 'hello' }, - 'fulfillments': [ffill], + 'inputs': [input], 'operation': 'CREATE', 'version': 1 } @@ -825,19 +809,19 @@ def test_validate_multiple_io_create_transaction(user_pub, user_priv, metadata={'message': 'hello'}, asset=Asset(divisible=True)) tx = tx.sign([user_priv, user2_priv]) - assert tx.fulfillments_valid() is True + assert tx.inputs_valid() is True validate_transaction_model(tx) def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, - user_user2_threshold_cond, - user_user2_threshold_ffill, data, + user_user2_threshold_output, + user_user2_threshold_input, data, uuid4): from bigchaindb.common.transaction import Transaction, Asset expected = { - 'conditions': [user_user2_threshold_cond.to_dict()], + 'outputs': [user_user2_threshold_output.to_dict()], 'metadata': data, 'asset': { 'id': uuid4, @@ -846,13 +830,13 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, 'refillable': False, 'data': data, }, - 'fulfillments': [ + 'inputs': [ { 'owners_before': [ - user_pub, + user_pub ], 'fulfillment': None, - 'input': None + 'fulfills': None }, ], 'operation': 'CREATE', @@ -863,7 +847,7 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, data, asset) tx_dict = tx.to_dict() tx_dict.pop('id') - tx_dict['fulfillments'][0]['fulfillment'] = None + tx_dict['inputs'][0]['fulfillment'] = None assert tx_dict == expected @@ -876,7 +860,7 @@ def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub, tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)], data, Asset()) tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True + assert tx.inputs_valid() is True validate_transaction_model(tx) @@ -898,18 +882,18 @@ def test_create_create_transaction_with_invalid_parameters(user_pub): Transaction.create([user_pub], [([user_pub],)]) -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_outputs_to_inputs(tx): + inputs = tx.to_inputs([0]) + assert len(inputs) == 1 + input = inputs.pop() + assert input.owners_before == tx.outputs[0].public_keys + assert input.fulfillment == tx.outputs[0].fulfillment + assert input.fulfills.txid == tx.id + assert input.fulfills.idx == 0 def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, - user2_cond, user_priv, uuid4): + user2_output, user_priv, uuid4): from copy import deepcopy from bigchaindb.common.crypto import PrivateKey from bigchaindb.common.transaction import Transaction, Asset @@ -917,20 +901,20 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, from .util import validate_transaction_model expected = { - 'conditions': [user2_cond.to_dict()], + 'outputs': [user2_output.to_dict()], 'metadata': None, 'asset': { 'id': uuid4, }, - 'fulfillments': [ + 'inputs': [ { 'owners_before': [ user_pub ], 'fulfillment': None, - 'input': { + 'fulfills': { 'txid': tx.id, - 'cid': 0 + 'idx': 0 } } ], @@ -948,19 +932,19 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub, expected_input.fulfillment.sign(serialize(expected).encode(), PrivateKey(user_priv)) expected_ffill = expected_input.fulfillment.serialize_uri() - transfer_ffill = transfer_tx['fulfillments'][0]['fulfillment'] + transfer_ffill = transfer_tx['inputs'][0]['fulfillment'] assert transfer_ffill == expected_ffill transfer_tx = Transaction.from_dict(transfer_tx) - assert transfer_tx.fulfillments_valid([tx.conditions[0]]) is True + assert transfer_tx.inputs_valid([tx.outputs[0]]) is True validate_transaction_model(transfer_tx) def test_create_transfer_transaction_multiple_io(user_pub, user_priv, user2_pub, user2_priv, - user3_pub, user2_cond): + user3_pub, user2_output): from bigchaindb.common.transaction import Transaction, Asset asset = Asset(divisible=True) @@ -969,26 +953,26 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv, tx = tx.sign([user_priv]) expected = { - 'conditions': [user2_cond.to_dict(), user2_cond.to_dict()], + 'outputs': [user2_output.to_dict(), user2_output.to_dict()], 'metadata': None, - 'fulfillments': [ + 'inputs': [ { 'owners_before': [ user_pub ], 'fulfillment': None, - 'input': { + 'fulfills': { 'txid': tx.id, - 'cid': 0 + 'idx': 0 } }, { 'owners_before': [ user2_pub ], 'fulfillment': None, - 'input': { + 'fulfills': { 'txid': tx.id, - 'cid': 1 + 'idx': 1 } } ], @@ -1001,14 +985,14 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv, asset=tx.asset) transfer_tx = transfer_tx.sign([user_priv, user2_priv]) - assert len(transfer_tx.fulfillments) == 2 - assert len(transfer_tx.conditions) == 2 + assert len(transfer_tx.inputs) == 2 + assert len(transfer_tx.outputs) == 2 - assert transfer_tx.fulfillments_valid(tx.conditions) is True + assert transfer_tx.inputs_valid(tx.outputs) is True transfer_tx = transfer_tx.to_dict() - transfer_tx['fulfillments'][0]['fulfillment'] = None - transfer_tx['fulfillments'][1]['fulfillment'] = None + transfer_tx['inputs'][0]['fulfillment'] = None + transfer_tx['inputs'][1]['fulfillment'] = None transfer_tx.pop('asset') transfer_tx.pop('id') @@ -1038,7 +1022,7 @@ def test_cant_add_empty_condition(): with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, None) with raises(TypeError): - tx.add_condition(None) + tx.add_output(None) def test_cant_add_empty_fulfillment(): @@ -1047,4 +1031,4 @@ def test_cant_add_empty_fulfillment(): with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, None) with raises(TypeError): - tx.add_fulfillment(None) + tx.add_input(None) diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index b66a5f87..a4c31133 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -276,7 +276,7 @@ class TestBigchainApi(object): assert len(block['block']['transactions']) == 1 assert block['block']['transactions'][0]['operation'] == 'GENESIS' - assert block['block']['transactions'][0]['fulfillments'][0]['input'] is None + assert block['block']['transactions'][0]['inputs'][0]['fulfills'] is None def test_create_genesis_block_fails_if_table_not_empty(self, b): from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError @@ -527,16 +527,16 @@ class TestBigchainApi(object): def test_non_create_input_not_found(self, b, user_pk): from cryptoconditions import Ed25519Fulfillment from bigchaindb.common.exceptions import TransactionDoesNotExist - from bigchaindb.common.transaction import (Fulfillment, Asset, + from bigchaindb.common.transaction import (Input, Asset, TransactionLink) from bigchaindb.models import Transaction from bigchaindb import Bigchain - # Create a fulfillment for a non existing transaction - fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_pk), - [user_pk], - TransactionLink('somethingsomething', 0)) - tx = Transaction.transfer([fulfillment], [([user_pk], 1)], Asset()) + # Create an input for a non existing transaction + input = Input(Ed25519Fulfillment(public_key=user_pk), + [user_pk], + TransactionLink('somethingsomething', 0)) + tx = Transaction.transfer([input], [([user_pk], 1)], Asset()) with pytest.raises(TransactionDoesNotExist): tx.validate(Bigchain()) @@ -557,16 +557,16 @@ class TestTransactionValidation(object): def test_create_operation_with_inputs(self, b, user_pk, create_tx): from bigchaindb.common.transaction import TransactionLink - # Manipulate fulfillment so that it has a `tx_input` defined even + # Manipulate input so that it has a `fulfills` defined even # though it shouldn't have one - create_tx.fulfillments[0].tx_input = TransactionLink('abc', 0) + create_tx.inputs[0].fulfills = TransactionLink('abc', 0) with pytest.raises(ValueError) as excinfo: b.validate_transaction(create_tx) assert excinfo.value.args[0] == 'A CREATE operation has no inputs' def test_transfer_operation_no_inputs(self, b, user_pk, signed_transfer_tx): - signed_transfer_tx.fulfillments[0].tx_input = None + signed_transfer_tx.inputs[0].fulfills = None with pytest.raises(ValueError) as excinfo: b.validate_transaction(signed_transfer_tx) @@ -577,7 +577,7 @@ class TestTransactionValidation(object): from bigchaindb.common.exceptions import TransactionDoesNotExist from bigchaindb.common.transaction import TransactionLink - signed_transfer_tx.fulfillments[0].tx_input = TransactionLink('c', 0) + signed_transfer_tx.inputs[0].fulfills = TransactionLink('c', 0) with pytest.raises(TransactionDoesNotExist): b.validate_transaction(signed_transfer_tx) @@ -593,7 +593,7 @@ class TestTransactionValidation(object): tx = Transaction.create([pk], [([user_pk], 1)]) tx.operation = 'TRANSFER' tx.asset = input_transaction.asset - tx.fulfillments[0].tx_input = input_tx + tx.inputs[0].fulfills = input_tx with pytest.raises(InvalidSignature): b.validate_transaction(tx) @@ -777,8 +777,8 @@ class TestMultipleInputs(object): # validate transaction assert b.is_valid_transaction(tx) == tx - assert len(tx.fulfillments) == 1 - assert len(tx.conditions) == 1 + assert len(tx.inputs) == 1 + assert len(tx.outputs) == 1 def test_single_owner_before_multiple_owners_after_single_input(self, b, user_sk, @@ -798,8 +798,8 @@ class TestMultipleInputs(object): tx = tx.sign([user_sk]) assert b.is_valid_transaction(tx) == tx - assert len(tx.fulfillments) == 1 - assert len(tx.conditions) == 1 + assert len(tx.inputs) == 1 + assert len(tx.outputs) == 1 @pytest.mark.usefixtures('inputs') def test_multiple_owners_before_single_owner_after_single_input(self, b, @@ -830,8 +830,8 @@ class TestMultipleInputs(object): # validate transaction assert b.is_valid_transaction(transfer_tx) == transfer_tx - assert len(transfer_tx.fulfillments) == 1 - assert len(transfer_tx.conditions) == 1 + assert len(transfer_tx.inputs) == 1 + assert len(transfer_tx.outputs) == 1 @pytest.mark.usefixtures('inputs') def test_multiple_owners_before_multiple_owners_after_single_input(self, b, @@ -862,8 +862,8 @@ class TestMultipleInputs(object): tx = tx.sign([user_sk, user2_sk]) assert b.is_valid_transaction(tx) == tx - assert len(tx.fulfillments) == 1 - assert len(tx.conditions) == 1 + assert len(tx.inputs) == 1 + assert len(tx.outputs) == 1 @pytest.mark.usefixtures('setup_database') def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk): @@ -1025,8 +1025,8 @@ class TestMultipleInputs(object): # check spents input_txid = owned_inputs_user1.txid - input_cid = owned_inputs_user1.cid - spent_inputs_user1 = b.get_spent(input_txid, input_cid) + input_idx = owned_inputs_user1.idx + spent_inputs_user1 = b.get_spent(input_txid, input_idx) assert spent_inputs_user1 is None # create a transaction and block @@ -1035,7 +1035,7 @@ class TestMultipleInputs(object): block = b.create_block([tx]) b.write_block(block) - spent_inputs_user1 = b.get_spent(input_txid, input_cid) + spent_inputs_user1 = b.get_spent(input_txid, input_idx) assert spent_inputs_user1 == tx @pytest.mark.usefixtures('setup_database') @@ -1061,8 +1061,8 @@ class TestMultipleInputs(object): # check spents input_txid = owned_inputs_user1.txid - input_cid = owned_inputs_user1.cid - spent_inputs_user1 = b.get_spent(input_txid, input_cid) + input_idx = owned_inputs_user1.idx + spent_inputs_user1 = b.get_spent(input_txid, input_idx) assert spent_inputs_user1 is None # create a transaction and block @@ -1076,7 +1076,7 @@ class TestMultipleInputs(object): b.write_vote(vote) # NOTE: I have no idea why this line is here b.get_transaction(tx.id) - spent_inputs_user1 = b.get_spent(input_txid, input_cid) + spent_inputs_user1 = b.get_spent(input_txid, input_idx) # Now there should be no spents (the block is invalid) assert spent_inputs_user1 is None @@ -1105,7 +1105,7 @@ class TestMultipleInputs(object): # check spents for input_tx in owned_inputs_user1: - assert b.get_spent(input_tx.txid, input_tx.cid) is None + assert b.get_spent(input_tx.txid, input_tx.idx) is None # transfer the first 2 inputs tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2], @@ -1117,12 +1117,12 @@ class TestMultipleInputs(object): # check that used inputs are marked as spent for ffill in tx_create.to_inputs()[:2]: - spent_tx = b.get_spent(ffill.tx_input.txid, ffill.tx_input.cid) + spent_tx = b.get_spent(ffill.fulfills.txid, ffill.fulfills.idx) assert spent_tx == tx_transfer_signed # check if remaining transaction that was unspent is also perceived # spendable by BigchainDB - assert b.get_spent(tx_create.to_inputs()[2].tx_input.txid, 2) is None + assert b.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None @pytest.mark.usefixtures('setup_database') def test_get_spent_multiple_owners(self, b, user_sk, user_pk): @@ -1147,7 +1147,7 @@ class TestMultipleInputs(object): # check spents for input_tx in owned_inputs_user1: - assert b.get_spent(input_tx.txid, input_tx.cid) is None + assert b.get_spent(input_tx.txid, input_tx.idx) is None # create a transaction tx = Transaction.transfer(transactions[0].to_inputs(), diff --git a/tests/pipelines/test_vote.py b/tests/pipelines/test_vote.py index ad45951a..9f1cf091 100644 --- a/tests/pipelines/test_vote.py +++ b/tests/pipelines/test_vote.py @@ -157,7 +157,7 @@ def test_vote_accumulates_transactions(b): validation = vote_obj.validate_tx(tx, 123, 1) assert validation == (True, 123, 1) - tx.fulfillments[0].fulfillment.signature = None + tx.inputs[0].fulfillment.signature = None validation = vote_obj.validate_tx(tx, 456, 10) assert validation == (False, 456, 10) diff --git a/tests/test_models.py b/tests/test_models.py index f462e3fe..554f3436 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -12,7 +12,7 @@ class TestTransactionModel(object): tx.validate(b) tx.operation = 'CREATE' - tx.fulfillments = [] + tx.inputs = [] with raises(ValueError): tx.validate(b) diff --git a/tests/web/test_transactions.py b/tests/web/test_transactions.py index 8664d0e1..e6f2022e 100644 --- a/tests/web/test_transactions.py +++ b/tests/web/test_transactions.py @@ -35,8 +35,8 @@ def test_post_create_transaction_endpoint(b, client): tx = tx.sign([user_priv]) res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict())) - assert res.json['fulfillments'][0]['owners_before'][0] == user_pub - assert res.json['conditions'][0]['owners_after'][0] == user_pub + assert res.json['inputs'][0]['owners_before'][0] == user_pub + assert res.json['outputs'][0]['public_keys'][0] == user_pub def test_post_create_transaction_with_invalid_id(b, client): @@ -63,7 +63,7 @@ def test_post_create_transaction_with_invalid_signature(b, client): tx = Transaction.create([user_pub], [([user_pub], 1)]) tx = tx.sign([user_priv]).to_dict() - tx['fulfillments'][0]['fulfillment'] = 'cf:0:0' + tx['inputs'][0]['fulfillment'] = 'cf:0:0' res = client.post(TX_ENDPOINT, data=json.dumps(tx)) assert res.status_code == 400 @@ -135,8 +135,8 @@ def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk): res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict())) - assert res.json['fulfillments'][0]['owners_before'][0] == user_pk - assert res.json['conditions'][0]['owners_after'][0] == user_pub + assert res.json['inputs'][0]['owners_before'][0] == user_pk + assert res.json['outputs'][0]['public_keys'][0] == user_pub @pytest.mark.usefixtures('inputs')