1
0
mirror of https://github.com/bigchaindb/bigchaindb.git synced 2024-06-28 00:27:45 +02:00

Tx ID as Asset ID (#926)

* Allow AssetLinks to be used in place of Assets in the Transaction Model and enforce `Transaction.transfer()` to only take an AssetLink

* Remove AssetLink's inheritance from Asset

* Remove id from the Asset model

* Fix get_txids_by_asset_id query for rethinkdb after removing asset's uuid

Because `CREATE` transactions don't have an asset that contains an id
anymore, one way to find all the transactions related to an asset is to
query the database twice: once for the `CREATE` transaction and another
for the `TRANSFER` transactions.

* Add TODO notice for vote test utils to be fixtures

* Update asset model documentation to reflect usage of transaction id

* Fix outdated asset description in transaction schema
This commit is contained in:
libscott 2016-12-20 17:28:15 +01:00 committed by Brett Sun
parent 1d7a8e3369
commit 7e33f2bd52
19 changed files with 316 additions and 259 deletions

View File

@ -111,12 +111,12 @@ def get_blocks_status_from_transaction(connection, transaction_id):
def get_txids_by_asset_id(connection, asset_id):
"""Retrieves transactions ids 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.
A digital asset in bigchaindb is identified by its ``CREATE``
transaction's ID. Knowing this ID allows us to query all the
transactions related to a particular digital asset.
Args:
asset_id (str): the id for this particular metadata.
asset_id (str): the ID of the asset.
Returns:
A list of transactions ids related to the asset. If no transaction

View File

@ -1,3 +1,4 @@
from itertools import chain
from time import time
import rethinkdb as r
@ -75,23 +76,32 @@ def get_blocks_status_from_transaction(connection, transaction_id):
def get_txids_by_asset_id(connection, asset_id):
# here we only want to return the transaction ids since later on when
# we are going to retrieve the transaction with status validation
return connection.run(
# First find the asset's CREATE transaction
create_tx_cursor = connection.run(
_get_asset_create_tx_query(asset_id).get_field('id'))
# Then find any TRANSFER transactions related to the asset
transfer_tx_cursor = connection.run(
r.table('bigchain')
.get_all(asset_id, index='asset_id')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['asset']['id'] == asset_id)
.get_field('id'))
return chain(create_tx_cursor, transfer_tx_cursor)
@register_query(RethinkDBConnection)
def get_asset_by_id(connection, asset_id):
return connection.run(
r.table('bigchain', read_mode=READ_MODE)
.get_all(asset_id, index='asset_id')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['asset']['id'] == asset_id)
.filter(lambda transaction: transaction['operation'] == 'CREATE')
.pluck('asset'))
return connection.run(_get_asset_create_tx_query(asset_id).pluck('asset'))
def _get_asset_create_tx_query(asset_id):
return r.table('bigchain', read_mode=READ_MODE) \
.get_all(asset_id, index='transaction_id') \
.concat_map(lambda block: block['block']['transactions']) \
.filter(lambda transaction: transaction['id'] == asset_id)
@register_query(RethinkDBConnection)

View File

@ -60,7 +60,7 @@ def create_bigchain_secondary_index(connection, dbname):
.table('bigchain')
.index_create('transaction_id', r.row['block']['transactions']['id'], multi=True))
# secondary index for asset uuid
# secondary index for asset links (in TRANSFER transactions)
connection.run(
r.db(dbname)
.table('bigchain')

View File

@ -105,13 +105,14 @@ 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 may contain properties:
of a ``CREATE`` transaction, this field contains only the user-defined
payload.
additionalProperties: false
required:
- id
properties:
id:
"$ref": "#/definitions/uuid4"
"$ref": "#/definitions/sha3_hexdigest"
description: |
ID of the transaction that created the asset.
data:
description: |
User provided metadata associated with the asset. May also be ``null``.

View File

@ -1,6 +1,5 @@
from copy import deepcopy
from functools import reduce
from uuid import uuid4
from cryptoconditions import (Fulfillment as CCFulfillment,
ThresholdSha256Fulfillment, Ed25519Fulfillment)
@ -384,14 +383,11 @@ class Asset(object):
Attributes:
data (dict): A dictionary of data that can be added to an Asset.
data_id (str): A unique identifier of `data`'s content.
"""
def __init__(self, data=None, data_id=None):
def __init__(self, data=None):
"""An Asset is not required to contain any extra data from outside."""
self.data = data
self.data_id = data_id if data_id is not None else self.to_hash()
self.validate_asset()
def __eq__(self, other):
@ -409,7 +405,6 @@ class Asset(object):
format.
"""
return {
'id': self.data_id,
'data': self.data,
}
@ -423,38 +418,37 @@ class Asset(object):
Returns:
:class:`~bigchaindb.common.transaction.Asset`
"""
return cls(asset.get('data'), asset['id'])
def to_hash(self):
"""Generates a unqiue uuid for an Asset"""
return str(uuid4())
return cls(asset.get('data'))
@staticmethod
def get_asset_id(transactions):
"""Get the asset id from a list of transaction ids.
"""Get the asset id from a list of :class:`~.Transactions`.
This is useful when we want to check if the multiple inputs of a
transaction are related to the same asset id.
Args:
transactions (:obj:`list` of :class:`~bigchaindb.common.
transaction.Transaction`): list of transaction usually inputs
that should have a matching asset_id
transaction.Transaction`): A list of Transactions.
Usually input Transactions that should have a matching
asset ID.
Returns:
str: uuid of the asset.
str: ID of the asset.
Raises:
AssetIdMismatch: If the inputs are related to different assets.
:exc:`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}
# create a set of the transactions' asset ids
asset_ids = {tx.id if tx.operation == Transaction.CREATE else tx.asset.id
for tx in transactions}
# check that all the transasctions have the same asset_id
# check that all the transasctions have the same asset id
if len(asset_ids) > 1:
raise AssetIdMismatch(('All inputs of all transactions passed'
' need to have the same asset id'))
@ -475,20 +469,20 @@ class Asset(object):
raise AmountError('`amount` must be greater than 0')
class AssetLink(Asset):
class AssetLink(object):
"""An object for unidirectional linking to a Asset.
"""
def __init__(self, data_id=None):
def __init__(self, asset_id=None):
"""Used to point to a specific Asset.
Args:
data_id (str): A Asset to link to.
asset_id (str): The ID of an asset to link to.
"""
self.data_id = data_id
self.id = asset_id
def __bool__(self):
return self.data_id is not None
return self.id is not None
def __eq__(self, other):
return isinstance(other, AssetLink) and \
@ -514,12 +508,14 @@ class AssetLink(Asset):
Returns:
(dict|None): The link as an alternative serialization format.
Returns None if the link is empty (i.e. is not linking to
an asset).
"""
if self.data_id is None:
if self.id is None:
return None
else:
return {
'id': self.data_id
'id': self.id
}
@ -537,6 +533,10 @@ class Transaction(object):
spend.
conditions (:obj:`list` of :class:`~bigchaindb.common.
transaction.Condition`, optional): Define the assets to lock.
asset (:class:`~.Asset`|:class:`~.AssetLink`): Asset or Asset link
associated with this Transaction. ``CREATE`` and ``GENESIS``
Transactions require an Asset while ``TRANSFER`` Transactions
require an AssetLink.
metadata (dict):
Metadata to be stored along with the Transaction.
version (int): Defines the version number of a Transaction.
@ -557,8 +557,8 @@ class Transaction(object):
Args:
operation (str): Defines the operation of the Transaction.
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
to be transferred or created in a Transaction.
asset (:class:`~.Asset`|:class:`~.AssetLink`): An Asset to be
created or an AssetLink linking an asset to be transferred.
fulfillments (:obj:`list` of :class:`~bigchaindb.common.
transaction.Fulfillment`, optional): Define the assets to
spend.
@ -575,10 +575,17 @@ class Transaction(object):
raise ValueError('`operation` must be one of {}'
.format(allowed_ops))
# Only assets for 'CREATE' operations can be un-defined.
if (asset and not isinstance(asset, Asset) or
not asset and operation != Transaction.CREATE):
raise TypeError('`asset` must be an Asset instance')
# Assets for 'CREATE' and 'GENESIS' operations must be None or of Asset
# type and Assets for 'TRANSFER' operations must be of AssetLink type.
if (operation in [Transaction.CREATE, Transaction.GENESIS] and
asset is not None and
not isinstance(asset, Asset)):
raise TypeError(("`asset` must be an Asset instance for "
"'{}' Transactions".format(operation)))
elif (operation == Transaction.TRANSFER and
not (asset and isinstance(asset, AssetLink))):
raise TypeError(("`asset` must be an valid AssetLink instance for "
"'TRANSFER' Transactions".format(operation)))
if conditions and not isinstance(conditions, list):
raise TypeError('`conditions` must be a list instance or None')
@ -591,9 +598,9 @@ class Transaction(object):
self.version = version if version is not None else self.VERSION
self.operation = operation
self.asset = asset if asset else Asset()
self.conditions = conditions if conditions else []
self.fulfillments = fulfillments if fulfillments else []
self.asset = asset or Asset()
self.conditions = conditions or []
self.fulfillments = fulfillments or []
self.metadata = metadata
# validate asset
@ -659,7 +666,7 @@ class Transaction(object):
return cls(cls.CREATE, asset, fulfillments, conditions, metadata)
@classmethod
def transfer(cls, inputs, owners_after, asset, metadata=None):
def transfer(cls, inputs, owners_after, asset_link, metadata=None):
"""A simple way to generate a `TRANSFER` transaction.
Note:
@ -689,8 +696,9 @@ class Transaction(object):
generate.
owners_after (:obj:`list` of :obj:`str`): A list of keys that
represent the receivers of this Transaction.
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
to be transferred in this Transaction.
asset_link (:class:`~bigchaindb.common.transaction.AssetLink`):
An AssetLink linking an asset to be transferred in this
Transaction.
metadata (dict): Python dictionary to be stored along with the
Transaction.
@ -716,7 +724,7 @@ class Transaction(object):
conditions.append(Condition.generate(pub_keys, amount))
inputs = deepcopy(inputs)
return cls(cls.TRANSFER, asset, inputs, conditions, metadata)
return cls(cls.TRANSFER, asset_link, inputs, conditions, metadata)
def __eq__(self, other):
try:
@ -1056,12 +1064,6 @@ class Transaction(object):
Returns:
dict: The Transaction as an alternative serialization format.
"""
if self.operation in (self.__class__.GENESIS, self.__class__.CREATE):
asset = self.asset.to_dict()
else:
# NOTE: An `asset` in a `TRANSFER` only contains the asset's id
asset = {'id': self.asset.data_id}
tx = {
'fulfillments': [fulfillment.to_dict() for fulfillment
in self.fulfillments],
@ -1069,7 +1071,7 @@ class Transaction(object):
in self.conditions],
'operation': str(self.operation),
'metadata': self.metadata,
'asset': asset,
'asset': self.asset.to_dict(),
'version': self.version,
}

View File

@ -82,7 +82,7 @@ class Transaction(Transaction):
# validate asset id
asset_id = Asset.get_asset_id(input_txs)
if asset_id != self.asset.data_id:
if asset_id != self.asset.id:
raise AssetIdMismatch(('The asset id of the input does not'
' match the asset id of the'
' transaction'))

View File

@ -62,7 +62,7 @@ def main():
""" Main function """
privkey = 'CfdqtD7sS7FgkMoGPXw55MVGGFwQLAoHYTcBhZDtF99Z'
pubkey = '4K9sWUMFwTgaDGPfdynrbxWqWS6sWmKbZoTjxLtVUibD'
asset = Asset(None, 'e6969f87-4fc9-4467-b62a-f0dfa1c85002')
asset = Asset(None)
tx = Transaction.create([pubkey], [([pubkey], 1)], asset=asset)
tx = tx.sign([privkey])
tx_json = json.dumps(tx.to_dict(), indent=2, sort_keys=True)

View File

@ -5,18 +5,17 @@ To avoid redundant data in transactions, the digital asset model is different fo
A digital asset's properties are defined in a `CREATE` transaction with the following model:
```json
{
"id": "<uuid>",
"data": "<json document>"
}
```
For `TRANSFER` transactions we only keep the asset id.
For `TRANSFER` transactions we only keep the asset ID:
```json
{
"id": "<uuid>"
"id": "<asset's CREATE transaction ID (sha3-256 hash)>"
}
```
- `id`: UUID version 4 (random) converted to a string of hex digits in standard form. Added server side.
- `id`: The ID of the `CREATE` transaction that created the asset.
- `data`: A user supplied JSON document with custom information about the asset. Defaults to null.

View File

@ -1,20 +1,22 @@
import pytest
import random
from unittest.mock import patch
@pytest.mark.usefixtures('inputs')
def test_asset_transfer(b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
tx_input = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_input.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
AssetLink(tx_create.id))
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
assert tx_transfer_signed.asset.id == tx_create.id
def test_validate_bad_asset_creation(b, user_pk):
@ -32,13 +34,14 @@ def test_validate_bad_asset_creation(b, user_pk):
@pytest.mark.usefixtures('inputs')
def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
from bigchaindb.common.exceptions import AssetIdMismatch
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_transfer.asset.data_id = 'aaa'
AssetLink(tx_create.id))
tx_transfer.asset.id = 'aaa'
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AssetIdMismatch):
tx_transfer_signed.validate(b)
@ -50,18 +53,19 @@ def test_get_asset_id_create_transaction(b, user_pk):
tx_create = Transaction.create([b.me], [([user_pk], 1)])
asset_id = Asset.get_asset_id(tx_create)
assert asset_id == tx_create.asset.data_id
assert asset_id == tx_create.id
@pytest.mark.usefixtures('inputs')
def test_get_asset_id_transfer_transaction(b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction, Asset
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
# create a block
block = b.create_block([tx_transfer_signed])
@ -71,15 +75,17 @@ def test_get_asset_id_transfer_transaction(b, user_pk, user_sk):
b.write_vote(vote)
asset_id = Asset.get_asset_id(tx_transfer)
assert asset_id == tx_transfer.asset.data_id
assert asset_id == tx_transfer.asset.id
def test_asset_id_mismatch(b, user_pk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.common.exceptions import AssetIdMismatch
tx1 = Transaction.create([b.me], [([user_pk], 1)])
tx2 = Transaction.create([b.me], [([user_pk], 1)])
tx1 = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx2 = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
with pytest.raises(AssetIdMismatch):
Asset.get_asset_id([tx1, tx2])
@ -87,20 +93,21 @@ def test_asset_id_mismatch(b, user_pk):
@pytest.mark.usefixtures('inputs')
def test_get_transactions_by_asset_id(b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
asset_id = tx_create.id
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 1
assert txs[0].id == tx_create.id
assert txs[0].asset.data_id == asset_id
assert txs[0].id == asset_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -114,26 +121,28 @@ def test_get_transactions_by_asset_id(b, user_pk, user_sk):
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
# FIXME: can I rely on the ordering here?
assert asset_id == txs[0].id
assert asset_id == txs[1].asset.id
@pytest.mark.usefixtures('inputs')
def test_get_transactions_by_asset_id_with_invalid_block(b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
asset_id = tx_create.id
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 1
assert txs[0].id == tx_create.id
assert txs[0].asset.data_id == asset_id
assert txs[0].id == asset_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -149,15 +158,15 @@ def test_get_transactions_by_asset_id_with_invalid_block(b, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_get_asset_by_id(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Asset, Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -166,6 +175,7 @@ def test_get_asset_by_id(b, user_pk, user_sk):
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
asset_id = Asset.get_asset_id([tx_create, tx_transfer])
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 2

View File

@ -130,7 +130,7 @@ def test_single_in_multiple_own_single_out_single_own_create(b, user_pk,
def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -146,7 +146,7 @@ def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b)
@ -164,7 +164,7 @@ def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -181,7 +181,7 @@ def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me], 50)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -200,7 +200,7 @@ def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -217,7 +217,7 @@ def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me, b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -241,7 +241,7 @@ def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -258,7 +258,7 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me, b.me], 50)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -282,7 +282,7 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -299,7 +299,7 @@ def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -321,7 +321,7 @@ def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -338,7 +338,7 @@ def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b)
@ -356,7 +356,7 @@ def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -375,7 +375,7 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b)
@ -401,7 +401,7 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -420,7 +420,7 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -446,7 +446,7 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -466,7 +466,7 @@ def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me, user_pk], 50)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -496,7 +496,7 @@ def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
@pytest.mark.usefixtures('inputs')
def test_multiple_in_different_transactions(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
# `b` creates a divisible asset and assigns 50 shares to `b` and
@ -521,7 +521,7 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
# split across two different transactions
tx_transfer1 = Transaction.transfer(tx_create.to_inputs([1]),
[([user_pk], 50)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer1_signed = tx_transfer1.sign([b.me_private])
# create block
block = b.create_block([tx_transfer1_signed])
@ -537,7 +537,7 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
tx_transfer2 = Transaction.transfer(tx_create.to_inputs([0]) +
tx_transfer1.to_inputs([0]),
[([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer2_signed = tx_transfer2.sign([user_sk])
assert tx_transfer2_signed.validate(b) == tx_transfer2_signed
@ -557,7 +557,7 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_amount_error_transfer(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset
@ -575,7 +575,7 @@ def test_amount_error_transfer(b, user_pk, user_sk):
# TRANSFER
# output amount less than input amount
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 50)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AmountError):
tx_transfer_signed.validate(b)
@ -583,7 +583,7 @@ def test_amount_error_transfer(b, user_pk, user_sk):
# TRANSFER
# output amount greater than input amount
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 101)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AmountError):
tx_transfer_signed.validate(b)
@ -600,7 +600,7 @@ def test_threshold_same_public_key(b, user_pk, user_sk):
# that does not mean that the code shouldn't work.
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset
asset = Asset()
@ -617,7 +617,7 @@ def test_threshold_same_public_key(b, user_pk, user_sk):
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -626,7 +626,7 @@ def test_threshold_same_public_key(b, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_sum_amount(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset with 3 outputs with amount 1
asset = Asset()
@ -647,7 +647,7 @@ def test_sum_amount(b, user_pk, user_sk):
# create a transfer transaction with one output and check if the amount
# is 3
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 3)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -658,7 +658,7 @@ def test_sum_amount(b, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_divide(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
# CREATE divisible asset with 1 output with amount 3
asset = Asset()
@ -677,7 +677,7 @@ def test_divide(b, user_pk, user_sk):
# of each output is 1
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 1), ([b.me], 1), ([b.me], 1)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -690,7 +690,7 @@ def test_divide(b, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
@ -709,14 +709,14 @@ def test_non_positive_amounts_on_transfer(b, user_pk):
with pytest.raises(AmountError):
Transaction.transfer(tx_create.to_inputs(),
[([b.me], 4), ([b.me], -1)],
asset=tx_create.asset)
AssetLink(tx_create.id))
# Check that negative inputs are caught when validating a TRANSFER transaction
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.transaction import Asset, AssetLink
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
@ -736,7 +736,7 @@ def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
# of each output is 1
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 4), ([b.me], 1)],
asset=tx_create.asset)
AssetLink(tx_create.id))
tx_transfer.conditions[1].amount = -1
tx_transfer_signed = tx_transfer.sign([user_sk])

View File

@ -17,9 +17,6 @@ CC_CONDITION_URI = 'cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0'
DATA = {
'msg': 'Hello BigchainDB!'
}
DATA_ID = '872fa6e6f46246cd44afdb2ee9cfae0e72885fb0910e2bcf9a5a2a4eadb417b8'
UUID4 = 'dc568f27-a113-46b4-9bd4-43015859e3e3'
@pytest.fixture
@ -126,16 +123,6 @@ def data():
return DATA
@pytest.fixture
def data_id():
return DATA_ID
@pytest.fixture
def uuid4():
return UUID4
@pytest.fixture
def utx(user_ffill, user_cond):
from bigchaindb.common.transaction import Transaction, Asset
@ -150,12 +137,12 @@ def tx(utx, user_priv):
@pytest.fixture
def transfer_utx(user_cond, user2_cond, utx):
from bigchaindb.common.transaction import (Fulfillment, TransactionLink,
Transaction, Asset)
Transaction, AssetLink)
user_cond = user_cond.to_dict()
ffill = Fulfillment(utx.conditions[0].fulfillment,
user_cond['owners_after'],
TransactionLink(utx.id, 0))
return Transaction('TRANSFER', Asset(), [ffill], [user2_cond])
return Transaction('TRANSFER', AssetLink(utx.id), [ffill], [user2_cond])
@pytest.fixture

View File

@ -6,7 +6,6 @@ def test_asset_default_values():
asset = Asset()
assert asset.data is None
assert asset.data_id
def test_asset_creation_with_data(data):
@ -24,32 +23,30 @@ def test_asset_invalid_asset_initialization():
Asset(data='some wrong type')
def test_invalid_asset_comparison(data, data_id):
def test_invalid_asset_comparison(data):
from bigchaindb.common.transaction import Asset
assert Asset(data, data_id) != 'invalid comparison'
assert Asset(data) != 'invalid comparison'
def test_asset_serialization(data, data_id):
def test_asset_serialization(data):
from bigchaindb.common.transaction import Asset
expected = {
'id': data_id,
'data': data,
}
asset = Asset(data, data_id)
asset = Asset(data)
assert asset.to_dict() == expected
def test_asset_deserialization(data, data_id):
def test_asset_deserialization(data):
from bigchaindb.common.transaction import Asset
asset_dict = {
'id': data_id,
'data': data,
}
asset = Asset.from_dict(asset_dict)
expected = Asset(data, data_id)
expected = Asset(data)
assert asset == expected

View File

@ -274,27 +274,17 @@ def test_invalid_transaction_initialization():
def test_create_default_asset_on_tx_initialization():
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.exceptions import ValidationError
from .util import validate_transaction_model
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, None)
expected = Asset()
asset = tx.asset
expected.data_id = None
asset.data_id = None
assert asset == expected
# Fails because no asset hash
with raises(ValidationError):
validate_transaction_model(tx)
def test_transaction_serialization(user_ffill, user_cond, data, data_id):
def test_transaction_serialization(user_ffill, user_cond, data):
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.exceptions import ValidationError
from .util import validate_transaction_model
tx_id = 'l0l'
@ -308,30 +298,23 @@ def test_transaction_serialization(user_ffill, user_cond, data, data_id):
'operation': Transaction.CREATE,
'metadata': None,
'asset': {
'id': data_id,
'data': data,
}
}
tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_ffill],
tx = Transaction(Transaction.CREATE, Asset(data), [user_ffill],
[user_cond])
tx_dict = tx.to_dict()
tx_dict['id'] = tx_id
tx_dict['asset']['id'] = data_id
assert tx_dict == expected
# Fails because asset id is not a uuid4
with raises(ValidationError):
validate_transaction_model(tx)
def test_transaction_deserialization(user_ffill, user_cond, data, uuid4):
def test_transaction_deserialization(user_ffill, user_cond, data):
from bigchaindb.common.transaction import Transaction, Asset
from .util import validate_transaction_model
expected_asset = Asset(data, uuid4)
expected_asset = Asset(data)
expected = Transaction(Transaction.CREATE, expected_asset, [user_ffill],
[user_cond], None, Transaction.VERSION)
@ -344,7 +327,6 @@ def test_transaction_deserialization(user_ffill, user_cond, data, uuid4):
'operation': Transaction.CREATE,
'metadata': None,
'asset': {
'id': uuid4,
'data': data,
}
}
@ -453,11 +435,11 @@ def test_cast_transaction_link_to_boolean():
def test_asset_link_serialization():
from bigchaindb.common.transaction import AssetLink
data_id = 'a asset id'
asset_id = 'a asset id'
expected = {
'id': data_id,
'id': asset_id,
}
asset_link = AssetLink(data_id)
asset_link = AssetLink(asset_id)
assert asset_link.to_dict() == expected
@ -474,10 +456,10 @@ def test_asset_link_serialization_with_empty_payload():
def test_asset_link_deserialization():
from bigchaindb.common.transaction import AssetLink
data_id = 'a asset id'
expected = AssetLink(data_id)
asset_id = 'a asset id'
expected = AssetLink(asset_id)
asset_link = {
'id': data_id
'id': asset_id
}
asset_link = AssetLink.from_dict(asset_link)
@ -686,7 +668,8 @@ def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond,
user3_priv):
from copy import deepcopy
from bigchaindb.common.transaction import (Transaction, TransactionLink,
Fulfillment, Condition, Asset)
Fulfillment, Condition, Asset,
AssetLink)
from cryptoconditions import Ed25519Fulfillment
from .util import validate_transaction_model
@ -702,7 +685,8 @@ def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond,
[user3_pub]),
Condition(Ed25519Fulfillment(public_key=user3_pub),
[user3_pub])]
transfer_tx = Transaction('TRANSFER', tx.asset, fulfillments, conditions)
transfer_tx = Transaction('TRANSFER', AssetLink(tx.id),
fulfillments, conditions)
transfer_tx = transfer_tx.sign([user_priv])
assert transfer_tx.fulfillments_valid(tx.conditions) is True
@ -735,7 +719,7 @@ def test_validate_fulfillments_of_transfer_tx_with_invalid_params(transfer_tx,
transfer_tx.fulfillments_valid([utx.conditions[0]])
def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4):
def test_create_create_transaction_single_io(user_cond, user_pub, data):
from bigchaindb.common.transaction import Transaction, Asset
from .util import validate_transaction_model
@ -743,7 +727,6 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4):
'conditions': [user_cond.to_dict()],
'metadata': data,
'asset': {
'id': uuid4,
'data': data,
},
'fulfillments': [
@ -759,7 +742,7 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data, uuid4):
'version': 1,
}
asset = Asset(data, uuid4)
asset = Asset(data)
tx = Transaction.create([user_pub], [([user_pub], 1)], data, asset)
tx_dict = tx.to_dict()
tx_dict['fulfillments'][0]['fulfillment'] = None
@ -823,15 +806,13 @@ def test_validate_multiple_io_create_transaction(user_pub, user_priv,
def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
user_user2_threshold_cond,
user_user2_threshold_ffill, data,
uuid4):
user_user2_threshold_ffill, data):
from bigchaindb.common.transaction import Transaction, Asset
expected = {
'conditions': [user_user2_threshold_cond.to_dict()],
'metadata': data,
'asset': {
'id': uuid4,
'data': data,
},
'fulfillments': [
@ -846,7 +827,7 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
'operation': 'CREATE',
'version': 1
}
asset = Asset(data, uuid4)
asset = Asset(data)
tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)],
data, asset)
tx_dict = tx.to_dict()
@ -870,7 +851,7 @@ def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub,
def test_create_create_transaction_with_invalid_parameters(user_pub):
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.transaction import Transaction, AssetLink
with raises(TypeError):
Transaction.create('not a list')
@ -884,6 +865,13 @@ def test_create_create_transaction_with_invalid_parameters(user_pub):
Transaction.create([user_pub], [user_pub])
with raises(ValueError):
Transaction.create([user_pub], [([user_pub],)])
with raises(TypeError):
Transaction.create([user_pub], [([user_pub], 1)], metadata=[])
with raises(TypeError):
Transaction.create([user_pub],
[([user_pub], 1)],
metadata=None,
asset=AssetLink('mock_asset_link'))
def test_conditions_to_inputs(tx):
@ -897,10 +885,10 @@ def test_conditions_to_inputs(tx):
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
user2_cond, user_priv, uuid4):
user2_cond, user_priv):
from copy import deepcopy
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction, AssetLink
from bigchaindb.common.util import serialize
from .util import validate_transaction_model
@ -908,7 +896,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
'conditions': [user2_cond.to_dict()],
'metadata': None,
'asset': {
'id': uuid4,
'id': tx.id,
},
'fulfillments': [
{
@ -926,8 +914,8 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
'version': 1
}
inputs = tx.to_inputs([0])
asset = Asset(None, uuid4)
transfer_tx = Transaction.transfer(inputs, [([user2_pub], 1)], asset=asset)
transfer_tx = Transaction.transfer(inputs, [([user2_pub], 1)],
asset_link=AssetLink(tx.id))
transfer_tx = transfer_tx.sign([user_priv])
transfer_tx = transfer_tx.to_dict()
@ -949,7 +937,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
user2_pub, user2_priv,
user3_pub, user2_cond):
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction, Asset, AssetLink
asset = Asset()
tx = Transaction.create([user_pub], [([user_pub], 1), ([user2_pub], 1)],
@ -986,7 +974,7 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
transfer_tx = Transaction.transfer(tx.to_inputs(),
[([user2_pub], 1), ([user2_pub], 1)],
asset=tx.asset)
asset_link=AssetLink(tx.id))
transfer_tx = transfer_tx.sign([user_priv, user2_priv])
assert len(transfer_tx.fulfillments) == 2
@ -1003,21 +991,27 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
assert expected == transfer_tx
def test_create_transfer_with_invalid_parameters(user_pub):
from bigchaindb.common.transaction import Transaction, Asset
def test_create_transfer_with_invalid_parameters(tx, user_pub):
from bigchaindb.common.transaction import Transaction, Asset, AssetLink
mock_asset_link = AssetLink(tx.id)
with raises(TypeError):
Transaction.transfer({}, [], Asset())
Transaction.transfer({}, [], mock_asset_link)
with raises(ValueError):
Transaction.transfer([], [], Asset())
Transaction.transfer([], [], mock_asset_link)
with raises(TypeError):
Transaction.transfer(['fulfillment'], {}, Asset())
Transaction.transfer(['fulfillment'], {}, mock_asset_link)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [], Asset())
Transaction.transfer(['fulfillment'], [], mock_asset_link)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [user_pub], Asset())
Transaction.transfer(['fulfillment'], [user_pub], mock_asset_link)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [([user_pub],)], Asset())
Transaction.transfer(['fulfillment'], [([user_pub],)], mock_asset_link)
with raises(TypeError):
Transaction.transfer(['fulfillment'], [([user_pub], 1)],
mock_asset_link, metadata=[])
with raises(TypeError):
Transaction.transfer(['fulfillment'], [([user_pub], 1)], Asset())
def test_cant_add_empty_condition():

View File

@ -8,6 +8,7 @@ Tasks:
import os
import copy
import random
import pytest
@ -123,9 +124,12 @@ def signed_create_tx(b, create_tx):
@pytest.fixture
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
inputs = signed_create_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], signed_create_tx.asset)
tx = Transaction.transfer(inputs,
[([user_pk], 1)],
AssetLink(signed_create_tx.id))
return tx.sign([user_sk])
@ -188,8 +192,10 @@ def inputs(user_pk, setup_database):
prev_block_id = g.id
for block in range(4):
transactions = [
Transaction.create([b.me], [([user_pk], 1)]).sign([b.me_private])
for i in range(10)
Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
.sign([b.me_private])
for _ in range(10)
]
block = b.create_block(transactions)
b.write_block(block)
@ -216,9 +222,10 @@ def inputs_shared(user_pk, user2_pk, setup_database):
prev_block_id = g.id
for block in range(4):
transactions = [
Transaction.create(
[b.me], [user_pk, user2_pk], payload={'i': i}).sign([b.me_private])
for i in range(10)
Transaction.create([b.me], [user_pk, user2_pk],
metadata={'msg': random.random()})
.sign([b.me_private])
for _ in range(10)
]
block = b.create_block(transactions)
b.write_block(block)

View File

@ -1,6 +1,7 @@
from time import sleep
import pytest
import random
@pytest.mark.skipif(reason='Some tests throw a ResourceWarning that might result in some weird '
@ -91,6 +92,7 @@ class TestBigchainApi(object):
def test_get_spent_with_double_spend(self, b, monkeypatch):
from bigchaindb.common.exceptions import DoubleSpend
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
b.create_genesis_block()
@ -104,14 +106,14 @@ class TestBigchainApi(object):
monkeypatch.setattr('time.time', lambda: 2)
transfer_tx = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
tx.asset)
AssetLink(tx.id))
transfer_tx = transfer_tx.sign([b.me_private])
block2 = b.create_block([transfer_tx])
b.write_block(block2)
monkeypatch.setattr('time.time', lambda: 3333333333)
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
tx.asset)
AssetLink(tx.id))
transfer_tx2 = transfer_tx2.sign([b.me_private])
block3 = b.create_block([transfer_tx2])
b.write_block(block3)
@ -157,13 +159,15 @@ class TestBigchainApi(object):
b.create_genesis_block()
monkeypatch.setattr('time.time', lambda: 1)
tx1 = Transaction.create([b.me], [([b.me], 1)])
tx1 = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx1 = tx1.sign([b.me_private])
block1 = b.create_block([tx1])
b.write_block(block1)
monkeypatch.setattr('time.time', lambda: 2222222222)
tx2 = Transaction.create([b.me], [([b.me], 1)])
tx2 = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx2 = tx2.sign([b.me_private])
block2 = b.create_block([tx2])
b.write_block(block2)
@ -181,12 +185,14 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_write_transaction(self, b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
response = b.write_transaction(tx)
@ -199,12 +205,14 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_read_transaction(self, b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -219,12 +227,14 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_read_transaction_invalid_block(self, b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
# There's no need to b.write_transaction(tx) to the backlog
@ -243,12 +253,14 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_read_transaction_invalid_block_and_backlog(self, b, user_pk, user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
# Make sure there's a copy of tx in the backlog
@ -482,12 +494,14 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_assign_transaction_one_node(self, b, user_pk, user_sk):
from bigchaindb.backend import query
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -501,6 +515,7 @@ class TestBigchainApi(object):
def test_assign_transaction_multiple_nodes(self, b, user_pk, user_sk):
from bigchaindb.backend import query
from bigchaindb.common.crypto import generate_key_pair
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
# create 5 federation nodes
@ -512,7 +527,9 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_link=AssetLink(input_tx.id),
metadata={'msg': random.random()})
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -527,7 +544,7 @@ class TestBigchainApi(object):
def test_non_create_input_not_found(self, b, user_pk):
from cryptoconditions import Ed25519Fulfillment
from bigchaindb.common.exceptions import TransactionDoesNotExist
from bigchaindb.common.transaction import (Fulfillment, Asset,
from bigchaindb.common.transaction import (AssetLink, Fulfillment,
TransactionLink)
from bigchaindb.models import Transaction
from bigchaindb import Bigchain
@ -536,7 +553,8 @@ class TestBigchainApi(object):
fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_pk),
[user_pk],
TransactionLink('somethingsomething', 0))
tx = Transaction.transfer([fulfillment], [([user_pk], 1)], Asset())
tx = Transaction.transfer([fulfillment], [([user_pk], 1)],
AssetLink('mock_asset_link'))
with pytest.raises(TransactionDoesNotExist):
tx.validate(Bigchain())
@ -546,8 +564,9 @@ class TestBigchainApi(object):
from bigchaindb.models import Transaction
for _ in range(4):
tx = Transaction.create([b.me],
[([user_pk], 1)]).sign([b.me_private])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()}) \
.sign([b.me_private])
b.write_transaction(tx)
assert query.count_backlog(b.connection) == 4
@ -585,6 +604,7 @@ class TestTransactionValidation(object):
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
from bigchaindb.common.crypto import generate_key_pair
from bigchaindb.common.exceptions import InvalidSignature
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
@ -592,7 +612,7 @@ class TestTransactionValidation(object):
sk, pk = generate_key_pair()
tx = Transaction.create([pk], [([user_pk], 1)])
tx.operation = 'TRANSFER'
tx.asset = input_transaction.asset
tx.asset = AssetLink(input_transaction.id)
tx.fulfillments[0].tx_input = input_tx
with pytest.raises(InvalidSignature):
@ -629,13 +649,14 @@ class TestTransactionValidation(object):
def test_valid_non_create_transaction_after_block_creation(self, b,
user_pk,
user_sk):
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
input_tx.asset)
AssetLink(input_tx.id))
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -653,6 +674,7 @@ class TestTransactionValidation(object):
def test_transaction_not_in_valid_block(self, b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import TransactionNotInValidBlock
from bigchaindb.common.transaction import AssetLink
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
@ -660,7 +682,7 @@ class TestTransactionValidation(object):
# create a transaction that's valid but not in a voted valid block
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
input_tx.asset)
AssetLink(input_tx.id))
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -672,7 +694,7 @@ class TestTransactionValidation(object):
# create transaction with the undecided input
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(),
[([user_pk], 1)],
transfer_tx.asset)
AssetLink(transfer_tx.asset.id))
tx_invalid = tx_invalid.sign([user_sk])
with pytest.raises(TransactionNotInValidBlock):
@ -766,13 +788,15 @@ class TestMultipleInputs(object):
def test_transfer_single_owner_single_input(self, b, inputs, user_pk,
user_sk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
tx_link = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(tx_link.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user2_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user2_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
# validate transaction
@ -785,6 +809,7 @@ class TestMultipleInputs(object):
user_pk,
inputs):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -794,7 +819,8 @@ class TestMultipleInputs(object):
tx_link = owned_inputs.pop()
input_tx = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(input_tx.to_inputs(),
[([user2_pk, user3_pk], 1)], input_tx.asset)
[([user2_pk, user3_pk], 1)],
AssetLink(input_tx.id))
tx = tx.sign([user_sk])
assert b.is_valid_transaction(tx) == tx
@ -806,6 +832,7 @@ class TestMultipleInputs(object):
user_sk,
user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -825,7 +852,7 @@ class TestMultipleInputs(object):
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [([user3_pk], 1)],
input_tx.asset)
AssetLink(input_tx.id))
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
# validate transaction
@ -838,6 +865,7 @@ class TestMultipleInputs(object):
user_sk,
user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -858,7 +886,8 @@ class TestMultipleInputs(object):
tx_input = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(tx_input.to_inputs(),
[([user3_pk, user4_pk], 1)], tx_input.asset)
[([user3_pk, user4_pk], 1)],
AssetLink(tx_input.id))
tx = tx.sign([user_sk, user2_sk])
assert b.is_valid_transaction(tx) == tx
@ -868,7 +897,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.common.transaction import AssetLink, TransactionLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -883,7 +912,8 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
AssetLink(tx.id))
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -898,7 +928,7 @@ class TestMultipleInputs(object):
user_sk,
user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.common.transaction import AssetLink, TransactionLink
from bigchaindb.models import Transaction
genesis = b.create_genesis_block()
@ -921,7 +951,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_pk], 1)],
tx.asset)
AssetLink(tx.id))
tx_invalid = tx_invalid.sign([user_sk])
block = b.create_block([tx_invalid])
b.write_block(block)
@ -941,7 +971,8 @@ class TestMultipleInputs(object):
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink, Asset
from bigchaindb.common.transaction import (TransactionLink, Asset,
AssetLink)
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -967,7 +998,7 @@ class TestMultipleInputs(object):
# transfer divisible asset divided in two outputs
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([user2_pk], 1), ([user2_pk], 1)],
asset=tx_create.asset)
asset_link=AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
@ -981,7 +1012,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.common.transaction import AssetLink, TransactionLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -999,7 +1030,8 @@ 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_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)],
AssetLink(tx.id))
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1012,6 +1044,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_single_output(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -1030,7 +1063,8 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
AssetLink(tx.id))
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1041,6 +1075,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_single_output_invalid_block(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
genesis = b.create_genesis_block()
@ -1066,7 +1101,8 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
AssetLink(tx.id))
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1084,8 +1120,8 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import Asset, AssetLink
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# create a new users
user2_sk, user2_pk = crypto.generate_key_pair()
@ -1110,7 +1146,7 @@ class TestMultipleInputs(object):
# transfer the first 2 inputs
tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2],
[([user2_pk], 1), ([user2_pk], 1)],
asset=tx_create.asset)
asset_link=AssetLink(tx_create.id))
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
@ -1126,8 +1162,8 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures('setup_database')
def test_get_spent_multiple_owners(self, b, user_sk, user_pk):
import random
from bigchaindb.common import crypto
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
@ -1151,7 +1187,8 @@ class TestMultipleInputs(object):
# create a transaction
tx = Transaction.transfer(transactions[0].to_inputs(),
[([user3_pk], 1)], transactions[0].asset)
[([user3_pk], 1)],
AssetLink(transactions[0].id))
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)

View File

@ -1,3 +1,4 @@
import random
import time
from unittest.mock import patch
@ -44,8 +45,9 @@ def test_create_block(b, user_pk):
block_maker = BlockPipeline()
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
block_maker.create(tx)
@ -63,8 +65,9 @@ def test_write_block(b, user_pk):
block_maker = BlockPipeline()
txs = []
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
txs.append(tx)
@ -83,8 +86,9 @@ def test_duplicate_transaction(b, user_pk):
block_maker = block.BlockPipeline()
txs = []
for i in range(10):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(10):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
txs.append(tx)
@ -114,7 +118,8 @@ def test_delete_tx(b, user_pk):
from bigchaindb.pipelines.block import BlockPipeline
block_maker = BlockPipeline()
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
block_maker.create(tx)
# make sure the tx appears in the backlog
@ -150,10 +155,8 @@ def test_start(create_pipeline):
@pytest.mark.usefixtures('setup_database')
def test_full_pipeline(b, user_pk):
import random
from bigchaindb.backend import query
from bigchaindb.models import Block, Transaction
from bigchaindb.pipelines.block import create_pipeline, get_changefeed
from bigchaindb.pipelines.block import create_pipeline
outpipe = Pipe()
@ -166,7 +169,7 @@ def test_full_pipeline(b, user_pk):
number_assigned_to_others = 0
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
{'msg': random.random()})
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
tx = tx.to_dict()

View File

@ -1,9 +1,10 @@
import os
import random
from bigchaindb import Bigchain
from bigchaindb.pipelines import stale
from multipipes import Pipe, Pipeline
from unittest.mock import patch
from bigchaindb import config_utils
import os
import pytest
@ -89,7 +90,8 @@ def test_full_pipeline(monkeypatch, user_pk):
monkeypatch.setattr('time.time', lambda: 1)
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
original_txc.append(tx.to_dict())

View File

@ -1,3 +1,4 @@
import random
import time
from unittest.mock import patch
@ -5,9 +6,11 @@ from multipipes import Pipe, Pipeline
import pytest
# TODO: dummy_tx and dummy_block could be fixtures
def dummy_tx(b):
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([b.me], 1)])
tx = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
return tx
@ -275,6 +278,7 @@ def test_valid_block_voting_with_create_transaction(b, monkeypatch):
def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.common.transaction import AssetLink
from bigchaindb.models import Transaction
from bigchaindb.pipelines import vote
@ -292,7 +296,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], 1)],
tx.asset)
AssetLink(tx.id))
tx2 = tx2.sign([test_user_priv])
monkeypatch.setattr('time.time', lambda: 2222222222)

View File

@ -124,13 +124,15 @@ def test_post_invalid_transaction(client, exc, msg, monkeypatch):
def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
sk, pk = crypto.generate_key_pair()
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import AssetLink
user_priv, user_pub = crypto.generate_key_pair()
input_valid = b.get_owned_ids(user_pk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
[([user_pub], 1)], create_tx.asset)
[([user_pub], 1)],
AssetLink(create_tx.id))
transfer_tx = transfer_tx.sign([user_sk])
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
@ -142,13 +144,15 @@ def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_post_invalid_transfer_transaction_returns_400(b, client, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import AssetLink
user_priv, user_pub = crypto.generate_key_pair()
input_valid = b.get_owned_ids(user_pk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
[([user_pub], 1)], create_tx.asset)
[([user_pub], 1)],
AssetLink(create_tx.id))
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
assert res.status_code == 400