diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py index f058c001..c53db530 100644 --- a/bigchaindb/common/transaction.py +++ b/bigchaindb/common/transaction.py @@ -413,7 +413,7 @@ class Asset(object): self.updatable = updatable self.refillable = refillable - self._validate_asset() + self.validate_asset() def __eq__(self, other): try: @@ -470,8 +470,9 @@ class Asset(object): transaction are related to the same asset id. Args: - transactions (list): list of transaction usually inputs that should - have a matching asset_id + transactions (:obj:`list` of :class:`~bigchaindb.common. + transaction.Transaction`): list of transaction usually inputs + that should have a matching asset_id Returns: str: uuid of the asset. @@ -488,11 +489,11 @@ class Asset(object): # check that all the transasctions have the same asset_id if len(asset_ids) > 1: - raise AssetIdMismatch(('All inputs of a transaction need' - ' to have the same asset id.')) + raise AssetIdMismatch(('All inputs of all transactions passed' + ' need to have the same asset id')) return asset_ids.pop() - def _validate_asset(self, amount=None): + def validate_asset(self, amount=None): """Validates the asset""" if self.data is not None and not isinstance(self.data, dict): raise TypeError('`data` must be a dict instance or None') @@ -670,11 +671,11 @@ class Transaction(object): if self.operation == self.CREATE: amount = sum([condition.amount for condition in self.conditions]) - self.asset._validate_asset(amount=amount) + self.asset.validate_asset(amount=amount) else: # In transactions other then `CREATE` we don't know if its a # divisible asset or not, so we cannot validate the amount here - self.asset._validate_asset() + self.asset.validate_asset() @classmethod def create(cls, owners_before, owners_after, metadata=None, asset=None): @@ -719,8 +720,9 @@ class Transaction(object): # 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 is a tuple' - ' of `([], )`')) + raise ValueError(('Each `owner_after` in the list must be a' + ' tuple of `([],' + ' )`')) pub_keys, amount = owner_after conds.append(Condition.generate(pub_keys, amount)) @@ -780,8 +782,9 @@ class Transaction(object): conds = [] 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 is a tuple' - ' of `([], )`')) + raise ValueError(('Each `owner_after` in the list must be a' + ' tuple of `([],' + ' )`')) pub_keys, amount = owner_after conds.append(Condition.generate(pub_keys, amount)) diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 3c4f5347..5d58b644 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -345,14 +345,14 @@ class Bigchain(object): return [Transaction.from_dict(tx) for tx in cursor] def get_asset_by_id(self, asset_id): - """Returns the asset associated with an asset_id + """Returns the asset associated with an asset_id. Args: - asset_id (str): The asset id + asset_id (str): The asset id. Returns: :class:`~bigchaindb.common.transaction.Asset` if the asset - exists else None + exists else None. """ cursor = self.backend.get_asset_by_id(asset_id) cursor = list(cursor) diff --git a/bigchaindb/db/backends/rethinkdb.py b/bigchaindb/db/backends/rethinkdb.py index 944d5e7c..468e12a5 100644 --- a/bigchaindb/db/backends/rethinkdb.py +++ b/bigchaindb/db/backends/rethinkdb.py @@ -182,13 +182,13 @@ class RethinkDBBackend: transaction['transaction']['asset']['id'] == asset_id)) def get_asset_by_id(self, asset_id): - """Returns the asset associated with an asset_id + """Returns the asset associated with an asset_id. Args: - asset_id (str): The asset id + asset_id (str): The asset id. Returns: - Returns a rethinkdb cursor + Returns a rethinkdb cursor. """ return self.connection.run( r.table('bigchain', read_mode=self.read_mode) diff --git a/bigchaindb/models.py b/bigchaindb/models.py index 7f993ed4..c525e959 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -42,7 +42,7 @@ class Transaction(Transaction): raise ValueError('A CREATE operation has no inputs') # validate asset amount = sum([condition.amount for condition in self.conditions]) - self.asset._validate_asset(amount=amount) + self.asset.validate_asset(amount=amount) elif self.operation == Transaction.TRANSFER: if not inputs_defined: raise ValueError('Only `CREATE` transactions can have null ' @@ -85,7 +85,7 @@ class Transaction(Transaction): # get the asset creation to see if its divisible or not asset = bigchain.get_asset_by_id(asset_id) # validate the asset - asset._validate_asset(amount=input_amount) + asset.validate_asset(amount=input_amount) # validate the amounts output_amount = sum([condition.amount for condition in self.conditions]) diff --git a/tests/assets/test_digital_assets.py b/tests/assets/test_digital_assets.py index 85d3a60a..56a08d53 100644 --- a/tests/assets/test_digital_assets.py +++ b/tests/assets/test_digital_assets.py @@ -25,7 +25,7 @@ def test_validate_bad_asset_creation(b, user_vk): # `divisible` needs to be a boolean tx = Transaction.create([b.me], [([user_vk], 1)]) tx.asset.divisible = 1 - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx_signed = tx.sign([b.me_private]) with pytest.raises(TypeError): tx_signed.validate(b) @@ -33,7 +33,7 @@ def test_validate_bad_asset_creation(b, user_vk): # `refillable` needs to be a boolean tx = Transaction.create([b.me], [([user_vk], 1)]) tx.asset.refillable = 1 - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx_signed = tx.sign([b.me_private]) with pytest.raises(TypeError): b.validate_transaction(tx_signed) @@ -41,7 +41,7 @@ def test_validate_bad_asset_creation(b, user_vk): # `updatable` needs to be a boolean tx = Transaction.create([b.me], [([user_vk], 1)]) tx.asset.updatable = 1 - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx_signed = tx.sign([b.me_private]) with pytest.raises(TypeError): b.validate_transaction(tx_signed) @@ -49,7 +49,7 @@ def test_validate_bad_asset_creation(b, user_vk): # `data` needs to be a dictionary tx = Transaction.create([b.me], [([user_vk], 1)]) tx.asset.data = 'a' - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx_signed = tx.sign([b.me_private]) with pytest.raises(TypeError): b.validate_transaction(tx_signed) @@ -189,7 +189,7 @@ def test_create_invalid_divisible_asset(b, user_vk, user_sk): # even if a transaction is badly constructed the server should raise the # exception asset = Asset(divisible=False) - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction.create([user_vk], [([user_vk], 2)], asset=asset) tx_signed = tx.sign([user_sk]) with pytest.raises(AmountError): @@ -197,7 +197,7 @@ def test_create_invalid_divisible_asset(b, user_vk, user_sk): assert b.is_valid_transaction(tx_signed) is False asset = Asset(divisible=True) - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction.create([user_vk], [([user_vk], 1)], asset=asset) tx_signed = tx.sign([user_sk]) with pytest.raises(AmountError): diff --git a/tests/assets/test_divisible_assets.py b/tests/assets/test_divisible_assets.py index d808008c..44181a4e 100644 --- a/tests/assets/test_divisible_assets.py +++ b/tests/assets/test_divisible_assets.py @@ -24,7 +24,7 @@ def test_single_in_single_own_single_out_single_own_create(b, user_vk): # CREATE divisible asset # Single input -# Single onwers_before +# Single owners_before # Multiple outputs # Single owners_after per output def test_single_in_single_own_multiple_out_single_own_create(b, user_vk): @@ -98,7 +98,7 @@ def test_single_in_single_own_multiple_out_mix_own_create(b, user_vk): # CREATE divisible asset # Single input # Multiple owners_before -# Ouput combinations already tested above +# Output combinations already tested above def test_single_in_multiple_own_single_out_single_own_create(b, user_vk, user_sk): from bigchaindb.models import Transaction @@ -519,7 +519,7 @@ def test_multiple_in_different_transactions(b, user_vk, user_sk): # `b` transfers its 50 shares to `user_vk` # after this transaction `user_vk` will have a total of 100 shares # split across two different transactions - tx_transfer1 = Transaction.transfer([tx_create.to_inputs()[1]], + tx_transfer1 = Transaction.transfer(tx_create.to_inputs([1]), [([user_vk], 50)], asset=tx_create.asset) tx_transfer1_signed = tx_transfer1.sign([b.me_private]) @@ -534,8 +534,8 @@ def test_multiple_in_different_transactions(b, user_vk, user_sk): # TRANSFER # `user_vk` combines two different transaction with 50 shares each and # transfers a total of 100 shares back to `b` - tx_transfer2 = Transaction.transfer([tx_create.to_inputs()[0], - tx_transfer1.to_inputs()[0]], + tx_transfer2 = Transaction.transfer(tx_create.to_inputs([0]) + + tx_transfer1.to_inputs([0]), [([b.me], 100)], asset=tx_create.asset) tx_transfer2_signed = tx_transfer2.sign([user_sk]) diff --git a/tests/common/test_asset.py b/tests/common/test_asset.py index cddaae64..f6a3f89d 100644 --- a/tests/common/test_asset.py +++ b/tests/common/test_asset.py @@ -81,12 +81,12 @@ def test_validate_asset(): # test amount errors asset = Asset(divisible=False) with raises(AmountError): - asset._validate_asset(amount=2) + asset.validate_asset(amount=2) asset = Asset(divisible=True) with raises(AmountError): - asset._validate_asset(amount=1) + asset.validate_asset(amount=1) asset = Asset() with raises(TypeError): - asset._validate_asset(amount='a') + asset.validate_asset(amount='a') diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py index e0094c61..67304038 100644 --- a/tests/common/test_transaction.py +++ b/tests/common/test_transaction.py @@ -228,31 +228,6 @@ def test_generate_conditions_single_owner_with_condition(user_pub): assert cond.fulfillment.to_dict() == expected.to_dict() -# TODO FOR CC: see skip reason -@mark.skip(reason='threshold(hashlock).to_dict() exposes secret') -def test_generate_threshold_condition_with_hashlock(user_pub, user2_pub, - user3_pub): - from bigchaindb.common.transaction import Condition - from cryptoconditions import (PreimageSha256Fulfillment, - Ed25519Fulfillment, - ThresholdSha256Fulfillment) - - secret = b'much secret, wow' - hashlock = PreimageSha256Fulfillment(preimage=secret) - - expected_simple1 = Ed25519Fulfillment(public_key=user_pub) - expected_simple3 = Ed25519Fulfillment(public_key=user3_pub) - - expected = ThresholdSha256Fulfillment(threshold=2) - expected_sub = ThresholdSha256Fulfillment(threshold=2) - expected_sub.add_subfulfillment(expected_simple1) - expected_sub.add_subfulfillment(hashlock) - expected.add_subfulfillment(expected_simple3) - - cond = Condition.generate([[user_pub, hashlock], expected_simple3], 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 @@ -300,7 +275,7 @@ def test_invalid_transaction_initialization(): def test_create_default_asset_on_tx_initialization(): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, None) expected = Asset() asset = tx.asset @@ -493,7 +468,7 @@ def test_cast_transaction_link_to_boolean(): def test_add_fulfillment_to_tx(user_ffill): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset(), [], []) tx.add_fulfillment(user_ffill) @@ -503,7 +478,7 @@ def test_add_fulfillment_to_tx(user_ffill): def test_add_fulfillment_to_tx_with_invalid_parameters(): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset()) with raises(TypeError): tx.add_fulfillment('somewronginput') @@ -512,7 +487,7 @@ def test_add_fulfillment_to_tx_with_invalid_parameters(): def test_add_condition_to_tx(user_cond): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset()) tx.add_condition(user_cond) @@ -522,7 +497,7 @@ def test_add_condition_to_tx(user_cond): def test_add_condition_to_tx_with_invalid_parameters(): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, Asset(), [], []) with raises(TypeError): tx.add_condition('somewronginput') @@ -605,9 +580,7 @@ def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv): expected_first = deepcopy(tx) expected_second = deepcopy(tx) expected_first.fulfillments = [expected_first.fulfillments[0]] - expected_first.conditions = expected_first.conditions expected_second.fulfillments = [expected_second.fulfillments[1]] - expected_second.conditions = expected_second.conditions expected_first_bytes = str(expected_first).encode() expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes, @@ -854,62 +827,6 @@ def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub, assert tx.fulfillments_valid() is True -@mark.skip(reason='Hashlocks are not implemented') -def test_create_create_transaction_hashlock(user_pub, data, data_id): - from cryptoconditions import PreimageSha256Fulfillment - from bigchaindb.common.transaction import Transaction, Condition, Asset - - secret = b'much secret, wow' - hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri - cond = Condition(hashlock) - - expected = { - 'transaction': { - 'conditions': [cond.to_dict(0)], - 'metadata': { - 'data': data, - }, - 'asset': { - 'id': data_id, - 'divisible': False, - 'updatable': False, - 'refillable': False, - 'data': data, - }, - 'fulfillments': [ - { - 'owners_before': [ - user_pub, - ], - 'fid': 0, - 'fulfillment': None, - 'input': None - }, - ], - 'operation': 'CREATE', - }, - 'version': 1 - } - - asset = Asset(data, data_id) - tx = Transaction.create([user_pub], [], data, asset, secret).to_dict() - tx.pop('id') - tx['transaction']['metadata'].pop('id') - tx['transaction'].pop('timestamp') - tx['transaction']['fulfillments'][0]['fulfillment'] = None - - assert tx == expected - - -@mark.skip(reson='Hashlocks are not implemented') -def test_validate_hashlock_create_transaction(user_pub, user_priv, data): - from bigchaindb.common.transaction import Transaction, Asset - - tx = Transaction.create([user_pub], [], data, Asset(), b'much secret, wow') - tx = tx.sign([user_priv]) - assert tx.fulfillments_valid() is True - - def test_create_create_transaction_with_invalid_parameters(user_pub): from bigchaindb.common.transaction import Transaction @@ -1071,7 +988,7 @@ def test_create_transfer_with_invalid_parameters(user_pub): def test_cant_add_empty_condition(): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, None) with raises(TypeError): tx.add_condition(None) @@ -1080,7 +997,7 @@ def test_cant_add_empty_condition(): def test_cant_add_empty_fulfillment(): from bigchaindb.common.transaction import Transaction, Asset - with patch.object(Asset, '_validate_asset', return_value=None): + with patch.object(Asset, 'validate_asset', return_value=None): tx = Transaction(Transaction.CREATE, None) with raises(TypeError): tx.add_fulfillment(None)