diff --git a/bigchaindb/common/schema/transaction.yaml b/bigchaindb/common/schema/transaction.yaml index f1864cc3..03f93ac7 100644 --- a/bigchaindb/common/schema/transaction.yaml +++ b/bigchaindb/common/schema/transaction.yaml @@ -60,7 +60,7 @@ properties: "$ref": "#/definitions/metadata" description: | User provided transaction metadata. This field may be ``null`` or may - contain an id and an object with freeform metadata. + contain an object with freeform metadata. See: `Metadata`_. version: @@ -245,17 +245,7 @@ definitions: - type: object description: | User provided transaction metadata. This field may be ``null`` or may - contain an id and an object with freeform metadata. - additionalProperties: false - required: - - id - - data - properties: - id: - "$ref": "#/definitions/uuid4" - data: - type: object - description: | - User provided transaction metadata. - additionalProperties: true + contain an non empty object with freeform metadata. + additionalProperties: true + minProperties: 1 - type: 'null' diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py index 6a7ec629..6fa1aa2b 100644 --- a/bigchaindb/common/transaction.py +++ b/bigchaindb/common/transaction.py @@ -572,66 +572,6 @@ class AssetLink(Asset): } -class Metadata(object): - """Metadata is used to store a dictionary and its hash in a Transaction.""" - - def __init__(self, data=None, data_id=None): - """Metadata stores a payload `data` as well as data's hash, `data_id`. - - Note: - When no `data_id` is provided, one is being generated by - this method. - - Args: - data (dict): A dictionary to be held by Metadata. - data_id (str): A hash corresponding to the contents of - `data`. - """ - if data is not None and not isinstance(data, dict): - raise TypeError('`data` must be a dict instance or None') - - self.data_id = data_id if data_id is not None else self.to_hash() - self.data = data - - def __eq__(self, other): - # TODO: If `other !== Data` return `False` - return self.to_dict() == other.to_dict() - - @classmethod - def from_dict(cls, data): - """Transforms a Python dictionary to a Metadata object. - - Args: - data (dict): The dictionary to be serialized. - - Returns: - :class:`~bigchaindb.common.transaction.Metadata` - """ - try: - return cls(data['data'], data['id']) - except TypeError: - return cls() - - def to_dict(self): - """Transforms the object to a Python dictionary. - - Returns: - (dict|None): The Metadata object as an alternative - serialization format. - """ - if self.data is None: - return None - else: - return { - 'data': self.data, - 'id': self.data_id, - } - - def to_hash(self): - """A hash corresponding to the contents of `payload`.""" - return str(uuid4()) - - class Transaction(object): """A Transaction is used to create and transfer assets. @@ -646,7 +586,7 @@ class Transaction(object): spend. conditions (:obj:`list` of :class:`~bigchaindb.common. transaction.Condition`, optional): Define the assets to lock. - metadata (:class:`~bigchaindb.common.transaction.Metadata`): + metadata (dict): Metadata to be stored along with the Transaction. version (int): Defines the version number of a Transaction. """ @@ -674,7 +614,7 @@ class Transaction(object): conditions (:obj:`list` of :class:`~bigchaindb.common. transaction.Condition`, optional): Define the assets to lock. - metadata (:class:`~bigchaindb.common.transaction.Metadata`): + metadata (dict): Metadata to be stored along with the Transaction. version (int): Defines the version number of a Transaction. @@ -695,8 +635,8 @@ class Transaction(object): if fulfillments and not isinstance(fulfillments, list): raise TypeError('`fulfillments` must be a list instance or None') - if metadata is not None and not isinstance(metadata, Metadata): - raise TypeError('`metadata` must be a Metadata instance or None') + if metadata is not None and not isinstance(metadata, dict): + raise TypeError('`metadata` must be a dict or None') self.version = version if version is not None else self.VERSION self.operation = operation @@ -750,7 +690,6 @@ class Transaction(object): if len(owners_after) == 0: raise ValueError('`owners_after` list cannot be empty') - metadata = Metadata(metadata) fulfillments = [] conditions = [] @@ -825,7 +764,6 @@ class Transaction(object): pub_keys, amount = owner_after conditions.append(Condition.generate(pub_keys, amount)) - metadata = Metadata(metadata) inputs = deepcopy(inputs) return cls(cls.TRANSFER, asset, inputs, conditions, metadata) @@ -1166,12 +1104,6 @@ class Transaction(object): Returns: dict: The Transaction as an alternative serialization format. """ - try: - metadata = self.metadata.to_dict() - except AttributeError: - # NOTE: metadata can be None and that's OK - metadata = None - if self.operation in (self.__class__.GENESIS, self.__class__.CREATE): asset = self.asset.to_dict() else: @@ -1184,7 +1116,7 @@ class Transaction(object): 'conditions': [condition.to_dict(cid) for cid, condition in enumerate(self.conditions)], 'operation': str(self.operation), - 'metadata': metadata, + 'metadata': self.metadata, 'asset': asset, } tx = { @@ -1279,11 +1211,10 @@ class Transaction(object): in tx['fulfillments']] conditions = [Condition.from_dict(condition) for condition in tx['conditions']] - metadata = Metadata.from_dict(tx['metadata']) 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, - metadata, tx_body['version']) + tx['metadata'], tx_body['version']) diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 653b7ac3..6fd892e7 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -330,35 +330,6 @@ class Bigchain(object): else: return None - def get_transaction_by_metadata_id(self, metadata_id): - """Retrieves valid or undecided transactions related to a particular - metadata. - - 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 the bigchain for that particular metadata we - create a UUID for the metadata and store it with the transaction. - - Args: - metadata_id (str): the id for this particular metadata. - - Returns: - A list of valid or undecided transactions containing that metadata. - If no transaction exists with that metadata it returns an empty - list `[]` - """ - txids = self.backend.get_txids_by_metadata_id(metadata_id) - transactions = [] - for txid in txids: - tx = self.get_transaction(txid) - # if a valid or undecided transaction exists append it to the list - # of transactions - if tx: - transactions.append(tx) - return transactions - def get_transactions_by_asset_id(self, asset_id): """Retrieves valid or undecided transactions related to a particular asset. diff --git a/bigchaindb/db/backends/rethinkdb.py b/bigchaindb/db/backends/rethinkdb.py index f01bdb4a..87b09dd2 100644 --- a/bigchaindb/db/backends/rethinkdb.py +++ b/bigchaindb/db/backends/rethinkdb.py @@ -138,32 +138,6 @@ class RethinkDBBackend: .get_all(transaction_id, index='transaction_id') .pluck('votes', 'id', {'block': ['voters']})) - def get_txids_by_metadata_id(self, metadata_id): - """Retrieves transaction ids related to a particular metadata. - - 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 the bigchain for that particular metadata we - create a UUID for the metadata and store it with the transaction. - - Args: - metadata_id (str): the id for this particular metadata. - - Returns: - A list of transaction ids containing that metadata. If no - transaction exists with that metadata it returns an empty list `[]` - """ - return self.connection.run( - 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) - .get_field('id')) - def get_txids_by_asset_id(self, asset_id): """Retrieves transactions ids related to a particular asset. diff --git a/bigchaindb/db/utils.py b/bigchaindb/db/utils.py index 05932f77..cbcebc85 100644 --- a/bigchaindb/db/utils.py +++ b/bigchaindb/db/utils.py @@ -116,11 +116,6 @@ def create_bigchain_secondary_index(conn, dbname): .index_create('transaction_id', r.row['block']['transactions']['id'], multi=True)\ .run(conn) - # secondary index for payload data by UUID - r.db(dbname).table('bigchain')\ - .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', diff --git a/docs/server/source/schema/transaction.rst b/docs/server/source/schema/transaction.rst index 68abab0c..1d2b8f7d 100644 --- a/docs/server/source/schema/transaction.rst +++ b/docs/server/source/schema/transaction.rst @@ -136,7 +136,7 @@ Transaction.metadata **type:** object or null User provided transaction metadata. This field may be ``null`` or may -contain an id and an object with freeform metadata. +contain an object with freeform metadata. See: `Metadata`_. @@ -301,26 +301,7 @@ Metadata -------- User provided transaction metadata. This field may be ``null`` or may -contain an id and an object with freeform metadata. - - -Metadata.id -^^^^^^^^^^^ - -**type:** string - -A `UUID `_ -of type 4 (random). - - - -Metadata.data -^^^^^^^^^^^^^ - -**type:** object - -User provided transaction metadata. - +contain an non empty object with freeform metadata. diff --git a/tests/common/conftest.py b/tests/common/conftest.py index 1e5fe7d3..a54daf20 100644 --- a/tests/common/conftest.py +++ b/tests/common/conftest.py @@ -136,12 +136,6 @@ def uuid4(): return UUID4 -@pytest.fixture -def metadata(data, data_id): - from bigchaindb.common.transaction import Metadata - return Metadata(data, data_id) - - @pytest.fixture def utx(user_ffill, user_cond): from bigchaindb.common.transaction import Transaction, Asset diff --git a/tests/common/test_schema.py b/tests/common/test_schema.py index 5ded0272..1827d3cb 100644 --- a/tests/common/test_schema.py +++ b/tests/common/test_schema.py @@ -16,6 +16,16 @@ def test_validate_transaction_signed_transfer(signed_transfer_tx): validate_transaction_schema(signed_transfer_tx.to_dict()) +def test_validate_fails_metadata_empty_dict(create_tx): + create_tx.metadata = {'a': 1} + validate_transaction_schema(create_tx.to_dict()) + create_tx.metadata = None + validate_transaction_schema(create_tx.to_dict()) + create_tx.metadata = {} + with raises(SchemaValidationError): + validate_transaction_schema(create_tx.to_dict()) + + def test_validation_fails(): with raises(SchemaValidationError): validate_transaction_schema({}) diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py index e6a4ac5f..335fd0a2 100644 --- a/tests/common/test_transaction.py +++ b/tests/common/test_transaction.py @@ -387,34 +387,6 @@ def test_invalid_fulfillment_initialization(user_ffill, user_pub): Fulfillment(user_ffill, [], tx_input='somethingthatiswrong') -def test_invalid_metadata_initialization(): - from bigchaindb.common.transaction import Metadata - - with raises(TypeError): - Metadata([]) - - -def test_metadata_serialization(data, data_id): - from bigchaindb.common.transaction import Metadata - - expected = { - 'data': data, - 'id': data_id, - } - metadata = Metadata(data, data_id) - - assert metadata.to_dict() == expected - - -def test_metadata_deserialization(data, data_id): - from bigchaindb.common.transaction import Metadata - - expected = Metadata(data, data_id) - metadata = Metadata.from_dict({'data': data, 'id': data_id}) - - assert metadata == expected - - def test_transaction_link_serialization(): from bigchaindb.common.transaction import TransactionLink @@ -762,9 +734,7 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4): expected = { 'transaction': { 'conditions': [user_cond.to_dict(0)], - 'metadata': { - 'data': data, - }, + 'metadata': data, 'asset': { 'id': uuid4, 'divisible': False, @@ -790,7 +760,6 @@ 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['transaction']['metadata'].pop('id') tx_dict['transaction']['fulfillments'][0]['fulfillment'] = None tx_dict.pop('id') @@ -820,9 +789,7 @@ def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub, 'transaction': { 'conditions': [user_cond.to_dict(0), user2_cond.to_dict(1)], 'metadata': { - 'data': { - 'message': 'hello' - } + 'message': 'hello' }, 'fulfillments': [ffill], 'operation': 'CREATE', @@ -835,7 +802,6 @@ def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub, asset=asset, metadata={'message': 'hello'}).to_dict() tx.pop('id') - tx['transaction']['metadata'].pop('id') tx['transaction'].pop('asset') assert tx == expected @@ -865,9 +831,7 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub, expected = { 'transaction': { 'conditions': [user_user2_threshold_cond.to_dict(0)], - 'metadata': { - 'data': data, - }, + 'metadata': data, 'asset': { 'id': uuid4, 'divisible': False, @@ -894,7 +858,6 @@ 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['transaction']['metadata'].pop('id') tx_dict['transaction']['fulfillments'][0]['fulfillment'] = None assert tx_dict == expected diff --git a/tests/db/test_bigchain_api.py b/tests/db/test_bigchain_api.py index 6de68898..cd030249 100644 --- a/tests/db/test_bigchain_api.py +++ b/tests/db/test_bigchain_api.py @@ -178,39 +178,6 @@ class TestBigchainApi(object): assert b.get_transaction(tx1.id) is None assert b.get_transaction(tx2.id) == tx2 - def test_get_transactions_for_metadata(self, b, user_pk): - from bigchaindb.models import Transaction - - metadata = {'msg': 'Hello BigchainDB!'} - tx = Transaction.create([b.me], [([user_pk], 1)], metadata=metadata) - - block = b.create_block([tx]) - b.write_block(block, durability='hard') - - matches = b.get_transaction_by_metadata_id(tx.metadata.data_id) - assert len(matches) == 1 - assert matches[0].id == tx.id - - @pytest.mark.usefixtures('inputs') - def test_get_transactions_for_metadata_invalid_block(self, b, user_pk): - from bigchaindb.models import Transaction - - metadata = {'msg': 'Hello BigchainDB!'} - tx = Transaction.create([b.me], [([user_pk], 1)], metadata=metadata) - - block = b.create_block([tx]) - b.write_block(block, durability='hard') - # vote block invalid - vote = b.vote(block.id, b.get_last_voted_block().id, False) - b.write_vote(vote) - - matches = b.get_transaction_by_metadata_id(tx.metadata.data_id) - assert len(matches) == 0 - - def test_get_transactions_for_metadata_mismatch(self, b): - matches = b.get_transaction_by_metadata_id('missing') - assert not matches - @pytest.mark.usefixtures('inputs') def test_write_transaction(self, b, user_pk, user_sk): from bigchaindb.models import Transaction @@ -646,7 +613,7 @@ class TestTransactionValidation(object): sleep(1) - signed_transfer_tx.metadata.data = {'different': 1} + signed_transfer_tx.metadata = {'different': 1} # FIXME: https://github.com/bigchaindb/bigchaindb/issues/592 with pytest.raises(DoubleSpend): b.validate_transaction(signed_transfer_tx) diff --git a/tests/db/test_utils.py b/tests/db/test_utils.py index dd8262a5..853604e9 100644 --- a/tests/db/test_utils.py +++ b/tests/db/test_utils.py @@ -77,8 +77,6 @@ def test_create_bigchain_secondary_index(): 'block_timestamp').run(conn) is True assert r.db(dbname).table('bigchain').index_list().contains( 'transaction_id').run(conn) is True - assert r.db(dbname).table('bigchain').index_list().contains( - 'metadata_id').run(conn) is True def test_create_backlog_table():