diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 7e395614..805a6d16 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -6,11 +6,12 @@ from time import time from itertools import compress from bigchaindb_common import crypto, exceptions from bigchaindb_common.util import gen_timestamp, serialize -from bigchaindb_common.transaction import TransactionLink +from bigchaindb_common.transaction import TransactionLink, Metadata import rethinkdb as r import bigchaindb + from bigchaindb.db.utils import Connection from bigchaindb import config_utils, util from bigchaindb.consensus import BaseConsensusRules @@ -326,32 +327,52 @@ class Bigchain(object): else: return None - def get_tx_by_payload_uuid(self, payload_uuid): - """Retrieves transactions related to a digital asset. + def get_tx_by_metadata_id(self, metadata_id): + """Retrieves transactions related to a metadata. - When creating a transaction one of the optional arguments is the `payload`. The payload is a generic - dict that contains information about the digital asset. + When creating a transaction one of the optional arguments is the `metadata`. The metadata is a generic + dict that contains extra information that can be appended to the transaction. - To make it easy to query BigchainDB for that digital asset we create a UUID for the payload and - store it with the transaction. This makes it easy for developers to keep track of their digital - assets in bigchain. + To make it easy to query the bigchain for that particular metadata we create a UUID for the metadata and + store it with the transaction. Args: - payload_uuid (str): the UUID for this particular payload. + metadata_id (str): the id for this particular metadata. Returns: - A list of transactions containing that payload. If no transaction exists with that payload it + A list of transactions containing that metadata. If no transaction exists with that metadata it returns an empty list `[]` """ - cursor = self.connection.run( - r.table('bigchain', read_mode=self.read_mode) - .get_all(payload_uuid, index='payload_uuid') - .concat_map(lambda block: block['block']['transactions']) - .filter(lambda transaction: transaction['transaction']['data']['uuid'] == payload_uuid)) + cursor = r.table('bigchain', read_mode=self.read_mode) \ + .get_all(metadata_id, index='metadata_id') \ + .concat_map(lambda block: block['block']['transactions']) \ + .filter(lambda transaction: transaction['transaction']['metadata']['id'] == metadata_id) \ + .run(self.conn) transactions = list(cursor) return [Transaction.from_dict(tx) for tx in transactions] + def get_txs_by_asset_id(self, asset_id): + """Retrieves transactions related to a particular asset. + + A digital asset in bigchaindb is identified by an uuid. This allows us to query all the transactions + related to a particular digital asset, knowing the id. + + Args: + asset_id (str): the id for this particular metadata. + + Returns: + A list of transactions containing related to the asset. If no transaction exists for that asset it + returns an empty list `[]` + """ + cursor = self.connection.run( + r.table('bigchain', read_mode=self.read_mode) + .get_all(asset_id, index='asset_id') + .concat_map(lambda block: block['block']['transactions']) + .filter(lambda transaction: transaction['transaction']['asset']['id'] == asset_id)) + + return [Transaction.from_dict(tx) for tx in cursor] + def get_spent(self, txid, cid): """Check if a `txid` was already used as an input. @@ -536,8 +557,9 @@ class Bigchain(object): def prepare_genesis_block(self): """Prepare a genesis block.""" - payload = {'message': 'Hello World from the BigchainDB'} - transaction = Transaction.create([self.me], [self.me], payload=payload) + metadata = {'message': 'Hello World from the BigchainDB'} + transaction = Transaction.create([self.me], [self.me], + metadata=metadata) # NOTE: The transaction model doesn't expose an API to generate a # GENESIS transaction, as this is literally the only usage. diff --git a/bigchaindb/db/utils.py b/bigchaindb/db/utils.py index 0ed74471..0009b2c3 100644 --- a/bigchaindb/db/utils.py +++ b/bigchaindb/db/utils.py @@ -105,9 +105,14 @@ def create_bigchain_secondary_index(conn, dbname): .run(conn) # secondary index for payload data by UUID r.db(dbname).table('bigchain')\ - .index_create('payload_uuid', - r.row['block']['transactions']['transaction']['data']['uuid'], multi=True)\ + .index_create('metadata_id', + r.row['block']['transactions']['transaction']['metadata']['id'], multi=True)\ .run(conn) + # secondary index for asset uuid + r.db(dbname).table('bigchain')\ + .index_create('asset_id', + r.row['block']['transactions']['transaction']['asset']['id'], multi=True)\ + .run(conn) # wait for rethinkdb to finish creating secondary indexes r.db(dbname).table('bigchain').index_wait().run(conn) diff --git a/bigchaindb/models.py b/bigchaindb/models.py index e31d1a5d..342d6443 100644 --- a/bigchaindb/models.py +++ b/bigchaindb/models.py @@ -2,11 +2,42 @@ from bigchaindb_common.crypto import hash_data, VerifyingKey, SigningKey from bigchaindb_common.exceptions import (InvalidHash, InvalidSignature, OperationError, DoubleSpend, TransactionDoesNotExist, - FulfillmentNotInValidBlock) -from bigchaindb_common.transaction import Transaction + FulfillmentNotInValidBlock, + AssetIdMismatch) +from bigchaindb_common.transaction import Transaction, Asset from bigchaindb_common.util import gen_timestamp, serialize +class Asset(Asset): + @staticmethod + def get_asset_id(transactions): + """Get the asset id from a list of transaction ids. + + This is useful when we want to check if the multiple inputs of a transaction + are related to the same asset id. + + Args: + transactions (list): list of transaction usually inputs that should have a matching asset_id + + Returns: + str: uuid of the asset. + + Raises: + AssetIdMismatch: If the inputs are related to different assets. + """ + + if not isinstance(transactions, list): + transactions = [transactions] + + # create a set of asset_ids + asset_ids = {tx.asset.data_id for tx in transactions} + + # 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.") + return asset_ids.pop() + + class Transaction(Transaction): def validate(self, bigchain): """Validate a transaction. @@ -36,13 +67,18 @@ class Transaction(Transaction): inputs_defined = all([ffill.tx_input for ffill in self.fulfillments]) if self.operation in (Transaction.CREATE, Transaction.GENESIS): + # validate inputs if inputs_defined: raise ValueError('A CREATE operation has no inputs') + # validate asset + self.asset._validate_asset() elif self.operation == Transaction.TRANSFER: if not inputs_defined: raise ValueError('Only `CREATE` transactions can have null ' 'inputs') - + # check inputs + # store the inputs so that we can check if the asset ids match + input_txs = [] for ffill in self.fulfillments: input_txid = ffill.tx_input.txid input_cid = ffill.tx_input.cid @@ -64,6 +100,12 @@ class Transaction(Transaction): .format(input_txid)) input_conditions.append(input_tx.conditions[input_cid]) + input_txs.append(input_tx) + + # 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') else: allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS) raise TypeError('`operation`: `{}` must be either {}.' diff --git a/bigchaindb/util.py b/bigchaindb/util.py index 3e84b4ba..007d8f7e 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -1,20 +1,10 @@ -import time import contextlib -from copy import deepcopy import threading import queue import multiprocessing as mp -import uuid -import rapidjson - -from bigchaindb_common import crypto, exceptions +from bigchaindb_common import crypto from bigchaindb_common.util import serialize -import cryptoconditions as cc -from cryptoconditions.exceptions import ParsingError - -import bigchaindb -from bigchaindb.models import Transaction class ProcessGroup(object): diff --git a/setup.py b/setup.py index b003abbe..16258344 100644 --- a/setup.py +++ b/setup.py @@ -103,7 +103,7 @@ setup( 'requests~=2.9', 'gunicorn~=19.0', 'multipipes~=0.1.0', - 'bigchaindb-common>=0.0.2', + 'bigchaindb-common>=0.0.4', ], setup_requires=['pytest-runner'], tests_require=tests_require, diff --git a/tests/assets/__init__.py b/tests/assets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/assets/conftest.py b/tests/assets/conftest.py new file mode 100644 index 00000000..1a3a77e2 --- /dev/null +++ b/tests/assets/conftest.py @@ -0,0 +1,19 @@ +import pytest +from ..db import conftest + + +@pytest.fixture(autouse=True) +def restore_config(request, node_config): + from bigchaindb import config_utils + config_utils.set_config(node_config) + + +@pytest.fixture(scope='module', autouse=True) +def setup_database(request, node_config): + conftest.setup_database(request, node_config) + + +@pytest.fixture(scope='function', autouse=True) +def cleanup_tables(request, node_config): + conftest.cleanup_tables(request, node_config) + diff --git a/tests/assets/test_digital_assets.py b/tests/assets/test_digital_assets.py new file mode 100644 index 00000000..64da4c11 --- /dev/null +++ b/tests/assets/test_digital_assets.py @@ -0,0 +1,163 @@ +import pytest +from ..db.conftest import inputs + + +@pytest.mark.usefixtures('inputs') +def test_asset_transfer(b, user_vk, user_sk): + from bigchaindb.models import Transaction + + tx_input = b.get_owned_ids(user_vk).pop() + tx_create = b.get_transaction(tx_input.txid) + + tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk], + tx_create.asset) + tx_transfer_signed = tx_transfer.sign([user_sk]) + + assert tx_transfer_signed.validate(b) == tx_transfer_signed + assert tx_transfer_signed.asset.data_id == tx_create.asset.data_id + + +def test_validate_bad_asset_creation(b, user_vk): + from bigchaindb.models import Transaction + + # `divisible` needs to be a boolean + tx = Transaction.create([b.me], [user_vk]) + tx.asset.divisible = 1 + tx_signed = tx.sign([b.me_private]) + with pytest.raises(TypeError): + tx_signed.validate(b) + + # `refillable` needs to be a boolean + tx = Transaction.create([b.me], [user_vk]) + tx.asset.refillable = 1 + tx_signed = tx.sign([b.me_private]) + with pytest.raises(TypeError): + b.validate_transaction(tx_signed) + + # `updatable` needs to be a boolean + tx = Transaction.create([b.me], [user_vk]) + tx.asset.updatable = 1 + tx_signed = tx.sign([b.me_private]) + with pytest.raises(TypeError): + b.validate_transaction(tx_signed) + + # `data` needs to be a dictionary + tx = Transaction.create([b.me], [user_vk]) + tx.asset.data = 'a' + tx_signed = tx.sign([b.me_private]) + with pytest.raises(TypeError): + b.validate_transaction(tx_signed) + + # TODO: Check where to test for the amount + """ + tx = b.create_transaction(b.me, user_vk, None, 'CREATE') + tx['transaction']['conditions'][0]['amount'] = 'a' + tx['id'] = get_hash_data(tx['transaction']) + tx_signed = b.sign_transaction(tx, b.me_private) + with pytest.raises(TypeError): + b.validate_transaction(tx_signed) + + tx = b.create_transaction(b.me, user_vk, None, 'CREATE') + tx['transaction']['conditions'][0]['amount'] = 2 + tx['transaction']['asset'].update({'divisible': False}) + tx['id'] = get_hash_data(tx['transaction']) + tx_signed = b.sign_transaction(tx, b.me_private) + with pytest.raises(AmountError): + b.validate_transaction(tx_signed) + + tx = b.create_transaction(b.me, user_vk, None, 'CREATE') + tx['transaction']['conditions'][0]['amount'] = 0 + tx['id'] = get_hash_data(tx['transaction']) + tx_signed = b.sign_transaction(tx, b.me_private) + with pytest.raises(AmountError): + b.validate_transaction(tx_signed) + """ + + +@pytest.mark.usefixtures('inputs') +def test_validate_transfer_asset_id_mismatch(b, user_vk, user_sk): + from bigchaindb_common.exceptions import AssetIdMismatch + from bigchaindb.models import Transaction + + tx_create = b.get_owned_ids(user_vk).pop() + tx_create = b.get_transaction(tx_create.txid) + tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk], + tx_create.asset) + tx_transfer.asset.data_id = 'aaa' + tx_transfer_signed = tx_transfer.sign([user_sk]) + with pytest.raises(AssetIdMismatch): + tx_transfer_signed.validate(b) + + +def test_get_asset_id_create_transaction(b, user_vk): + from bigchaindb.models import Transaction, Asset + + tx_create = Transaction.create([b.me], [user_vk]) + asset_id = Asset.get_asset_id(tx_create) + + assert asset_id == tx_create.asset.data_id + + +@pytest.mark.usefixtures('inputs') +def test_get_asset_id_transfer_transaction(b, user_vk, user_sk): + from bigchaindb.models import Transaction, Asset + + tx_create = b.get_owned_ids(user_vk).pop() + tx_create = b.get_transaction(tx_create.txid) + # create a transfer transaction + tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk], + tx_create.asset) + tx_transfer_signed = tx_transfer.sign([user_sk]) + # create a block + block = b.create_block([tx_transfer_signed]) + b.write_block(block, durability='hard') + # vote the block valid + vote = b.vote(block.id, b.get_last_voted_block().id, True) + b.write_vote(vote) + asset_id = Asset.get_asset_id(tx_transfer) + + assert asset_id == tx_transfer.asset.data_id + + +def test_asset_id_mismatch(b, user_vk): + from bigchaindb.models import Transaction, Asset + from bigchaindb_common.exceptions import AssetIdMismatch + + tx1 = Transaction.create([b.me], [user_vk]) + tx2 = Transaction.create([b.me], [user_vk]) + + with pytest.raises(AssetIdMismatch): + Asset.get_asset_id([tx1, tx2]) + + +@pytest.mark.usefixtures('inputs') +def test_get_txs_by_asset_id(b, user_vk, user_sk): + from bigchaindb.models import Transaction + + tx_create = b.get_owned_ids(user_vk).pop() + tx_create = b.get_transaction(tx_create.txid) + asset_id = tx_create.asset.data_id + txs = b.get_txs_by_asset_id(asset_id) + + assert len(txs) == 1 + assert txs[0].id == tx_create.id + assert txs[0].asset.data_id == asset_id + + # create a transfer transaction + tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk], + tx_create.asset) + tx_transfer_signed = tx_transfer.sign([user_sk]) + # create the block + block = b.create_block([tx_transfer_signed]) + b.write_block(block, durability='hard') + # vote the block valid + vote = b.vote(block.id, b.get_last_voted_block().id, True) + b.write_vote(vote) + + txs = b.get_txs_by_asset_id(asset_id) + + assert len(txs) == 2 + assert tx_create.id in [t.id for t in txs] + assert tx_transfer.id in [t.id for t in txs] + assert asset_id == txs[0].asset.data_id + assert asset_id == txs[1].asset.data_id diff --git a/tests/conftest.py b/tests/conftest.py index 73bd025f..58178b7f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -84,5 +84,5 @@ def signed_create_tx(b, create_tx): def signed_transfer_tx(signed_create_tx, user_vk, user_sk): from bigchaindb.models import Transaction inputs = signed_create_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], signed_create_tx.asset) return tx.sign([user_sk]) diff --git a/tests/db/conftest.py b/tests/db/conftest.py index 3cd9ae49..ec4afc9b 100644 --- a/tests/db/conftest.py +++ b/tests/db/conftest.py @@ -51,8 +51,8 @@ def setup_database(request, node_config): r.db(db_name).table('backlog').index_create('transaction_timestamp', r.row['transaction']['timestamp']).run() # to query by payload uuid r.db(db_name).table('bigchain').index_create( - 'payload_uuid', - r.row['block']['transactions']['transaction']['data']['uuid'], + 'metadata_id', + r.row['block']['transactions']['transaction']['metadata']['id'], multi=True, ).run() # compound index to read transactions from the backlog per assignee @@ -62,6 +62,11 @@ def setup_database(request, node_config): # compound index to order votes by block id and node r.db(db_name).table('votes').index_create('block_and_voter', [r.row['vote']['voting_for_block'], r.row['node_pubkey']]).run() + # secondary index for asset uuid + r.db(db_name).table('bigchain')\ + .index_create('asset_id', + r.row['block']['transactions']['transaction']['asset']['id'], multi=True)\ + .run() # order transactions by id r.db(db_name).table('bigchain').index_create('transaction_id', r.row['block']['transactions']['id'], multi=True).run() @@ -114,8 +119,7 @@ def inputs(user_vk): prev_block_id = g.id for block in range(4): transactions = [ - Transaction.create( - [b.me], [user_vk], payload={'i': i}).sign([b.me_private]) + Transaction.create([b.me], [user_vk]).sign([b.me_private]) for i in range(10) ] block = b.create_block(transactions) diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 7b57049c..d056cbfe 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -88,6 +88,11 @@ class TestBigchainApi(object): assert b.has_previous_vote(block.id, block.voters) is True + + def test_get_transactions_for_metadata_mismatch(self, b): + matches = b.get_tx_by_metadata_id('missing') + assert not matches + def test_get_spent_with_double_spend(self, b, monkeypatch): from bigchaindb_common.exceptions import DoubleSpend from bigchaindb.models import Transaction @@ -102,13 +107,13 @@ class TestBigchainApi(object): b.write_block(block1, durability='hard') monkeypatch.setattr('time.time', lambda: 2) - transfer_tx = Transaction.transfer(tx.to_inputs(), [b.me]) + transfer_tx = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset) transfer_tx = transfer_tx.sign([b.me_private]) block2 = b.create_block([transfer_tx]) b.write_block(block2, durability='hard') monkeypatch.setattr('time.time', lambda: 3) - transfer_tx2 = Transaction.transfer(tx.to_inputs(), [b.me]) + transfer_tx2 = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset) transfer_tx2 = transfer_tx2.sign([b.me_private]) block3 = b.create_block([transfer_tx2]) b.write_block(block3, durability='hard') @@ -173,24 +178,24 @@ class TestBigchainApi(object): vote = b.vote(block2.id, b.get_last_voted_block().id, True) b.write_vote(vote) - assert b.get_transaction(tx1.id) == None + assert b.get_transaction(tx1.id) is None assert b.get_transaction(tx2.id) == tx2 - def test_get_transactions_for_payload(self, b, user_vk): + def test_get_transactions_for_metadata(self, b, user_vk): from bigchaindb.models import Transaction - payload = {'msg': 'Hello BigchainDB!'} - tx = Transaction.create([b.me], [user_vk], payload=payload) + metadata = {'msg': 'Hello BigchainDB!'} + tx = Transaction.create([b.me], [user_vk], metadata=metadata) block = b.create_block([tx]) b.write_block(block, durability='hard') - matches = b.get_tx_by_payload_uuid(tx.data.payload_id) + matches = b.get_tx_by_payload_uuid(tx.metadata.data_id) assert len(matches) == 1 assert matches[0].id == tx.id - def test_get_transactions_for_payload_mismatch(self, b, user_vk): - matches = b.get_tx_by_payload_uuid('missing') + def test_get_transactions_for_metadata(self, b, user_vk): + matches = b.get_tx_by_metadata_id('missing') assert not matches @pytest.mark.usefixtures('inputs') @@ -200,7 +205,7 @@ class TestBigchainApi(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) tx = tx.sign([user_sk]) response = b.write_transaction(tx) @@ -218,7 +223,7 @@ class TestBigchainApi(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) tx = tx.sign([user_sk]) b.write_transaction(tx) @@ -238,7 +243,7 @@ class TestBigchainApi(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) tx = tx.sign([user_sk]) b.write_transaction(tx) @@ -480,7 +485,7 @@ class TestBigchainApi(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) tx = tx.sign([user_sk]) b.write_transaction(tx) @@ -505,7 +510,7 @@ class TestBigchainApi(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user_vk]) + tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) tx = tx.sign([user_sk]) b.write_transaction(tx) @@ -516,6 +521,25 @@ class TestBigchainApi(object): assert response['assignee'] in b.nodes_except_me + @pytest.mark.usefixtures('inputs') + def test_non_create_input_not_found(self, b, user_vk): + from cryptoconditions import Ed25519Fulfillment + from bigchaindb_common.exceptions import TransactionDoesNotExist + from bigchaindb_common.transaction import (Fulfillment, Asset, + TransactionLink) + from bigchaindb.models import Transaction + from bigchaindb import Bigchain + + # Create a fulfillment for a non existing transaction + fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_vk), + [user_vk], + TransactionLink('somethingsomething', 0)) + tx = Transaction.transfer([fulfillment], [user_vk], Asset()) + + with pytest.raises(TransactionDoesNotExist) as excinfo: + tx.validate(Bigchain()) + + class TestTransactionValidation(object): def test_create_operation_with_inputs(self, b, user_vk, create_tx): from bigchaindb_common.transaction import TransactionLink @@ -550,9 +574,11 @@ class TestTransactionValidation(object): from bigchaindb.models import Transaction input_tx = b.get_owned_ids(user_vk).pop() + input_transaction = b.get_transaction(input_tx.txid) sk, vk = generate_key_pair() tx = Transaction.create([vk], [user_vk]) tx.operation = 'TRANSFER' + tx.asset = input_transaction.asset tx.fulfillments[0].tx_input = input_tx with pytest.raises(InvalidSignature): @@ -594,7 +620,7 @@ class TestTransactionValidation(object): input_tx = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(input_tx.txid) inputs = input_tx.to_inputs() - transfer_tx = Transaction.transfer(inputs, [user_vk]) + transfer_tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) transfer_tx = transfer_tx.sign([user_sk]) assert transfer_tx == b.validate_transaction(transfer_tx) @@ -618,7 +644,7 @@ class TestTransactionValidation(object): inputs = input_tx.to_inputs() # create a transaction that's valid but not in a voted valid block - transfer_tx = Transaction.transfer(inputs, [user_vk]) + transfer_tx = Transaction.transfer(inputs, [user_vk], input_tx.asset) transfer_tx = transfer_tx.sign([user_sk]) assert transfer_tx == b.validate_transaction(transfer_tx) @@ -628,7 +654,8 @@ class TestTransactionValidation(object): b.write_block(block, durability='hard') # create transaction with the undecided input - tx_invalid = Transaction.transfer(transfer_tx.to_inputs(), [user_vk]) + tx_invalid = Transaction.transfer(transfer_tx.to_inputs(), [user_vk], + transfer_tx.asset) tx_invalid = tx_invalid.sign([user_sk]) with pytest.raises(FulfillmentNotInValidBlock): @@ -726,7 +753,7 @@ class TestMultipleInputs(object): tx_link = b.get_owned_ids(user_vk).pop() input_tx = b.get_transaction(tx_link.txid) inputs = input_tx.to_inputs() - tx = Transaction.transfer(inputs, [user2_vk]) + tx = Transaction.transfer(inputs, [user2_vk], input_tx.asset) tx = tx.sign([user_sk]) # validate transaction @@ -734,6 +761,9 @@ class TestMultipleInputs(object): assert len(tx.fulfillments) == 1 assert len(tx.conditions) == 1 + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) @pytest.mark.usefixtures('inputs') def test_transfer_single_owners_multiple_inputs(self, b, user_sk, user_vk): from bigchaindb_common import crypto @@ -752,6 +782,9 @@ class TestMultipleInputs(object): assert len(tx.fulfillments) == len(inputs) assert len(tx.conditions) == len(inputs) + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) @pytest.mark.usefixtures('inputs') def test_transfer_single_owners_single_input_from_multiple_outputs(self, b, user_sk, @@ -779,7 +812,7 @@ class TestMultipleInputs(object): # get inputs from user2 owned_inputs = b.get_owned_ids(user2_vk) - assert len(owned_inputs) == len(inputs) + assert len(owned_inputs) == len(inputs) # create a transaction with a single input from a multiple output transaction tx_link = owned_inputs.pop() @@ -803,14 +836,17 @@ class TestMultipleInputs(object): owned_inputs = b.get_owned_ids(user_vk) tx_link = owned_inputs.pop() - inputs = b.get_transaction(tx_link.txid).to_inputs() - tx = Transaction.transfer(inputs, [[user2_vk, user3_vk]]) + input_tx = b.get_transaction(tx_link.txid) + tx = Transaction.transfer(input_tx.to_inputs(), [[user2_vk, user3_vk]], input_tx.asset) tx = tx.sign([user_sk]) assert b.is_valid_transaction(tx) == tx assert len(tx.fulfillments) == 1 assert len(tx.conditions) == 1 + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) @pytest.mark.usefixtures('inputs') def test_single_owner_before_multiple_owners_after_multiple_inputs(self, b, user_sk, @@ -865,7 +901,7 @@ class TestMultipleInputs(object): input_tx = b.get_transaction(owned_input.txid) inputs = input_tx.to_inputs() - transfer_tx = Transaction.transfer(inputs, [user3_vk]) + transfer_tx = Transaction.transfer(inputs, [user3_vk], input_tx.asset) transfer_tx = transfer_tx.sign([user_sk, user2_sk]) # validate transaction @@ -873,6 +909,9 @@ class TestMultipleInputs(object): assert len(transfer_tx.fulfillments) == 1 assert len(transfer_tx.conditions) == 1 + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) @pytest.mark.usefixtures('inputs_shared') def test_multiple_owners_before_single_owner_after_multiple_inputs(self, b, user_sk, user_vk, user2_vk, user2_sk): @@ -915,15 +954,18 @@ class TestMultipleInputs(object): # get input tx_link = b.get_owned_ids(user_vk).pop() - tx_input = b.get_transaction(tx_link.txid).to_inputs() + tx_input = b.get_transaction(tx_link.txid) - tx = Transaction.transfer(tx_input, [[user3_vk, user4_vk]]) + tx = Transaction.transfer(tx_input.to_inputs(), [[user3_vk, user4_vk]], tx_input.asset) tx = tx.sign([user_sk, user2_sk]) assert b.is_valid_transaction(tx) == tx assert len(tx.fulfillments) == 1 assert len(tx.conditions) == 1 + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) @pytest.mark.usefixtures('inputs_shared') def test_multiple_owners_before_multiple_owners_after_multiple_inputs(self, b, user_sk, user_vk, @@ -944,7 +986,7 @@ class TestMultipleInputs(object): assert b.is_valid_transaction(tx) == tx assert len(tx.fulfillments) == len(inputs) - assert len(tx.conditions) == len(inputs) + assert len(tx.conditions) == len(inputs) def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_vk): from bigchaindb_common import crypto @@ -963,7 +1005,7 @@ class TestMultipleInputs(object): assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == [] - tx = Transaction.transfer(tx.to_inputs(), [user2_vk]) + tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset) tx = tx.sign([user_sk]) block = b.create_block([tx]) b.write_block(block, durability='hard') @@ -999,7 +1041,7 @@ class TestMultipleInputs(object): # NOTE: The transaction itself is valid, still will mark the block # as invalid to mock the behavior. - tx_invalid = Transaction.transfer(tx.to_inputs(), [user2_vk]) + tx_invalid = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset) tx_invalid = tx_invalid.sign([user_sk]) block = b.create_block([tx_invalid]) b.write_block(block, durability='hard') @@ -1015,6 +1057,9 @@ class TestMultipleInputs(object): assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == [] + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_vk): import random @@ -1074,7 +1119,7 @@ class TestMultipleInputs(object): assert owned_inputs_user1 == owned_inputs_user2 assert owned_inputs_user1 == expected_owned_inputs_user1 - tx = Transaction.transfer(tx.to_inputs(), [user3_vk]) + tx = Transaction.transfer(tx.to_inputs(), [user3_vk], tx.asset) tx = tx.sign([user_sk, user2_sk]) block = b.create_block([tx]) b.write_block(block, durability='hard') @@ -1104,7 +1149,7 @@ class TestMultipleInputs(object): assert spent_inputs_user1 is None # create a transaction and block - tx = Transaction.transfer(tx.to_inputs(), [user2_vk]) + tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset) tx = tx.sign([user_sk]) block = b.create_block([tx]) b.write_block(block, durability='hard') @@ -1139,7 +1184,7 @@ class TestMultipleInputs(object): assert spent_inputs_user1 is None # create a transaction and block - tx = Transaction.transfer(tx.to_inputs(), [user2_vk]) + tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset) tx = tx.sign([user_sk]) block = b.create_block([tx]) b.write_block(block, durability='hard') @@ -1154,6 +1199,9 @@ class TestMultipleInputs(object): # Now there should be no spents (the block is invalid) assert spent_inputs_user1 is None + @pytest.mark.skipif(reason=('Multiple inputs are only allowed for the ' + 'same asset. Remove this after implementing ', + 'multiple assets')) def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_vk): import random from bigchaindb_common import crypto @@ -1218,7 +1266,7 @@ class TestMultipleInputs(object): assert b.get_spent(input_tx.txid, input_tx.cid) is None # create a transaction - tx = Transaction.transfer(transactions[0].to_inputs(), [user3_vk]) + tx = Transaction.transfer(transactions[0].to_inputs(), [user3_vk], transactions[0].asset) tx = tx.sign([user_sk, user2_sk]) block = b.create_block([tx]) b.write_block(block, durability='hard') diff --git a/tests/db/test_utils.py b/tests/db/test_utils.py index f22bcd04..56f09ac5 100644 --- a/tests/db/test_utils.py +++ b/tests/db/test_utils.py @@ -79,7 +79,7 @@ def test_create_bigchain_secondary_index(): assert r.db(dbname).table('bigchain').index_list().contains( 'transaction_id').run(conn) is True assert r.db(dbname).table('bigchain').index_list().contains( - 'payload_uuid').run(conn) is True + 'metadata_id').run(conn) is True def test_create_backlog_table(): diff --git a/tests/pipelines/test_stale_monitor.py b/tests/pipelines/test_stale_monitor.py index 511b0492..5045344b 100644 --- a/tests/pipelines/test_stale_monitor.py +++ b/tests/pipelines/test_stale_monitor.py @@ -83,10 +83,7 @@ def test_full_pipeline(user_vk): original_txc = [] for i in range(100): - # FIXME Notice the payload. This is only to make sure that the - # transactions hashes are unique. See - # https://github.com/bigchaindb/bigchaindb-common/issues/21 - tx = Transaction.create([b.me], [user_vk], payload={'i': i}) + tx = Transaction.create([b.me], [user_vk]) tx = tx.sign([b.me_private]) original_txc.append(tx.to_dict()) diff --git a/tests/pipelines/test_vote.py b/tests/pipelines/test_vote.py index 3ac431b7..09811b0d 100644 --- a/tests/pipelines/test_vote.py +++ b/tests/pipelines/test_vote.py @@ -274,7 +274,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b): # create a `TRANSFER` transaction test_user2_priv, test_user2_pub = crypto.generate_key_pair() - tx2 = Transaction.transfer(tx.to_inputs(), [test_user2_pub]) + tx2 = Transaction.transfer(tx.to_inputs(), [test_user2_pub], tx.asset) tx2 = tx2.sign([test_user_priv]) monkeypatch.setattr('time.time', lambda: 2) diff --git a/tests/web/test_basic_views.py b/tests/web/test_basic_views.py index 829374cd..b941011a 100644 --- a/tests/web/test_basic_views.py +++ b/tests/web/test_basic_views.py @@ -77,7 +77,7 @@ def test_post_transfer_transaction_endpoint(b, client, user_vk, user_sk): input_valid = b.get_owned_ids(user_vk).pop() create_tx = b.get_transaction(input_valid.txid) - transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub]) + transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset) transfer_tx = transfer_tx.sign([user_sk]) res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict())) @@ -94,7 +94,7 @@ def test_post_invalid_transfer_transaction_returns_400(b, client, user_vk, user_ input_valid = b.get_owned_ids(user_vk).pop() create_tx = b.get_transaction(input_valid.txid) - transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub]) + transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset) res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict())) assert res.status_code == 400