Merge pull request #856 from bigchaindb/remove-metadata-uuid

Remove metadata uuid
This commit is contained in:
libscott 2016-11-30 06:20:32 +01:00 committed by GitHub
commit a30c79f3d5
11 changed files with 26 additions and 252 deletions

View File

@ -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'

View File

@ -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'])

View File

@ -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.

View File

@ -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.

View File

@ -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',

View File

@ -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 <https://tools.ietf.org/html/rfc4122.html>`_
of type 4 (random).
Metadata.data
^^^^^^^^^^^^^
**type:** object
User provided transaction metadata.
contain an non empty object with freeform metadata.

View File

@ -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

View File

@ -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({})

View File

@ -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

View File

@ -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)

View File

@ -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():