duplicate tx.id into tx.asset.id in CREATE transactions

This commit is contained in:
Scott Sadler 2017-01-16 15:03:54 +01:00
parent e08bf52fa7
commit 003519b0a8
4 changed files with 43 additions and 25 deletions

View File

@ -85,22 +85,6 @@ def get_blocks_status_from_transaction(conn, transaction_id):
@register_query(MongoDBConnection)
def get_txids_by_asset_id(conn, asset_id):
# get the txid of the create transaction for asset_id
cursor = conn.db['bigchain'].aggregate([
{'$match': {
'block.transactions.id': asset_id,
'block.transactions.operation': 'CREATE'
}},
{'$unwind': '$block.transactions'},
{'$match': {
'block.transactions.id': asset_id,
'block.transactions.operation': 'CREATE'
}},
{'$project': {'block.transactions.id': True}}
])
create_tx_txids = (elem['block']['transactions']['id'] for elem in cursor)
# get txids of transfer transaction with asset_id
cursor = conn.db['bigchain'].aggregate([
{'$match': {
'block.transactions.asset.id': asset_id
@ -111,9 +95,7 @@ def get_txids_by_asset_id(conn, asset_id):
}},
{'$project': {'block.transactions.id': True}}
])
transfer_tx_ids = (elem['block']['transactions']['id'] for elem in cursor)
return chain(create_tx_txids, transfer_tx_ids)
return (elem['block']['transactions']['id'] for elem in cursor)
@register_query(MongoDBConnection)

View File

@ -103,8 +103,8 @@ definitions:
description: |
Description of the asset being transacted. In the case of a ``TRANSFER``
transaction, this field contains only the ID of asset. In the case
of a ``CREATE`` transaction, this field contains only the user-defined
payload.
of a ``CREATE`` transaction, this field contains the user-defined
payload and may contain the asset ID (duplicated from the Transaction ID).
additionalProperties: false
properties:
id:

View File

@ -444,6 +444,7 @@ class Transaction(object):
asset is not None and not (isinstance(asset, dict) and 'data' in asset)):
raise TypeError(('`asset` must be None or a dict holding a `data` '
" property instance for '{}' Transactions".format(operation)))
asset.pop('id', None) # Remove duplicated asset ID if there is one
elif (operation == Transaction.TRANSFER and
not (isinstance(asset, dict) and 'id' in asset)):
raise TypeError(('`asset` must be a dict holding an `id` property '
@ -926,9 +927,11 @@ class Transaction(object):
tx_no_signatures = Transaction._remove_signatures(tx)
tx_serialized = Transaction._to_str(tx_no_signatures)
tx_id = Transaction._to_hash(tx_serialized)
tx['id'] = tx_id
tx['id'] = Transaction._to_hash(tx_serialized)
if self.operation == Transaction.CREATE:
# Duplicate asset into asset for consistency with TRANSFER
# transactions
tx['asset']['id'] = tx['id']
return tx
@staticmethod
@ -952,6 +955,9 @@ class Transaction(object):
# case could yield incorrect signatures. This is why we only
# set it to `None` if it's set in the dict.
input_['fulfillment'] = None
# Pop duplicated asset_id from CREATE tx
if tx_dict['operation'] == Transaction.CREATE:
tx_dict['asset'].pop('id', None)
return tx_dict
@staticmethod
@ -1031,6 +1037,10 @@ class Transaction(object):
"the hash of its body, i.e. it's not valid.")
raise InvalidHash(err_msg.format(proposed_tx_id))
if tx_body.get('operation') == Transaction.CREATE:
if proposed_tx_id != tx_body['asset'].get('id'):
raise InvalidHash("CREATE tx has wrong asset_id")
@classmethod
def from_dict(cls, tx):
"""Transforms a Python dictionary to a Transaction object.

View File

@ -300,6 +300,7 @@ def test_transaction_serialization(user_input, user_output, data):
'operation': Transaction.CREATE,
'metadata': None,
'asset': {
'id': tx_id,
'data': data,
}
}
@ -307,7 +308,7 @@ def test_transaction_serialization(user_input, user_output, data):
tx = Transaction(Transaction.CREATE, {'data': data}, [user_input],
[user_output])
tx_dict = tx.to_dict()
tx_dict['id'] = tx_id
tx_dict['id'] = tx_dict['asset']['id'] = tx_id
assert tx_dict == expected
@ -334,6 +335,7 @@ def test_transaction_deserialization(user_input, user_output, data):
}
tx_no_signatures = Transaction._remove_signatures(tx)
tx['id'] = Transaction._to_hash(Transaction._to_str(tx_no_signatures))
tx['asset']['id'] = tx['id']
tx = Transaction.from_dict(tx)
assert tx == expected
@ -680,6 +682,7 @@ def test_create_create_transaction_single_io(user_output, user_pub, data):
tx_dict = tx.to_dict()
tx_dict['inputs'][0]['fulfillment'] = None
tx_dict.pop('id')
tx_dict['asset'].pop('id')
assert tx_dict == expected
@ -763,6 +766,7 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
metadata=data, asset=data)
tx_dict = tx.to_dict()
tx_dict.pop('id')
tx_dict['asset'].pop('id')
tx_dict['inputs'][0]['fulfillment'] = None
assert tx_dict == expected
@ -975,3 +979,25 @@ def test_validate_version(utx):
utx.version = '1.0.0'
with raises(SchemaValidationError):
validate_transaction_model(utx)
def test_create_tx_has_asset_id(tx):
tx = tx.to_dict()
assert tx['id'] == tx['asset']['id']
def test_create_tx_validates_asset_id(tx):
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.exceptions import InvalidHash
tx = tx.to_dict()
# Test fails with wrong asset_id
tx['asset']['id'] = tx['asset']['id'][::-1]
with raises(InvalidHash):
Transaction.from_dict(tx)
# Test fails with no asset_id
tx['asset'].pop('id')
with raises(InvalidHash):
Transaction.from_dict(tx)