mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge pull request #856 from bigchaindb/remove-metadata-uuid
Remove metadata uuid
This commit is contained in:
commit
a30c79f3d5
@ -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'
|
||||
|
@ -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'])
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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({})
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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():
|
||||
|
Loading…
x
Reference in New Issue
Block a user