Merge pull request #680 from bigchaindb/rebase/feat/564/implement-digital-asset-template

Rebase/feat/564/implement digital asset template
This commit is contained in:
Rodolphe Marques 2016-10-13 13:55:49 +01:00 committed by GitHub
commit 23cec1bcb1
15 changed files with 368 additions and 78 deletions

View File

@ -6,11 +6,12 @@ from time import time
from itertools import compress
from bigchaindb_common import crypto, exceptions
from bigchaindb_common.util import gen_timestamp, serialize
from bigchaindb_common.transaction import TransactionLink
from bigchaindb_common.transaction import TransactionLink, Metadata
import rethinkdb as r
import bigchaindb
from bigchaindb.db.utils import Connection
from bigchaindb import config_utils, util
from bigchaindb.consensus import BaseConsensusRules
@ -326,32 +327,52 @@ class Bigchain(object):
else:
return None
def get_tx_by_payload_uuid(self, payload_uuid):
"""Retrieves transactions related to a digital asset.
def get_tx_by_metadata_id(self, metadata_id):
"""Retrieves transactions related to a metadata.
When creating a transaction one of the optional arguments is the `payload`. The payload is a generic
dict that contains information about the digital asset.
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 BigchainDB for that digital asset we create a UUID for the payload and
store it with the transaction. This makes it easy for developers to keep track of their digital
assets in bigchain.
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:
payload_uuid (str): the UUID for this particular payload.
metadata_id (str): the id for this particular metadata.
Returns:
A list of transactions containing that payload. If no transaction exists with that payload it
A list of transactions containing that metadata. If no transaction exists with that metadata it
returns an empty list `[]`
"""
cursor = self.connection.run(
r.table('bigchain', read_mode=self.read_mode)
.get_all(payload_uuid, index='payload_uuid')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['transaction']['data']['uuid'] == payload_uuid))
cursor = 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) \
.run(self.conn)
transactions = list(cursor)
return [Transaction.from_dict(tx) for tx in transactions]
def get_txs_by_asset_id(self, asset_id):
"""Retrieves transactions 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.
Args:
asset_id (str): the id for this particular metadata.
Returns:
A list of transactions containing related to the asset. If no transaction exists for that asset it
returns an empty list `[]`
"""
cursor = self.connection.run(
r.table('bigchain', read_mode=self.read_mode)
.get_all(asset_id, index='asset_id')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['transaction']['asset']['id'] == asset_id))
return [Transaction.from_dict(tx) for tx in cursor]
def get_spent(self, txid, cid):
"""Check if a `txid` was already used as an input.
@ -536,8 +557,9 @@ class Bigchain(object):
def prepare_genesis_block(self):
"""Prepare a genesis block."""
payload = {'message': 'Hello World from the BigchainDB'}
transaction = Transaction.create([self.me], [self.me], payload=payload)
metadata = {'message': 'Hello World from the BigchainDB'}
transaction = Transaction.create([self.me], [self.me],
metadata=metadata)
# NOTE: The transaction model doesn't expose an API to generate a
# GENESIS transaction, as this is literally the only usage.

View File

@ -105,9 +105,14 @@ def create_bigchain_secondary_index(conn, dbname):
.run(conn)
# secondary index for payload data by UUID
r.db(dbname).table('bigchain')\
.index_create('payload_uuid',
r.row['block']['transactions']['transaction']['data']['uuid'], multi=True)\
.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',
r.row['block']['transactions']['transaction']['asset']['id'], multi=True)\
.run(conn)
# wait for rethinkdb to finish creating secondary indexes
r.db(dbname).table('bigchain').index_wait().run(conn)

View File

@ -2,11 +2,42 @@ from bigchaindb_common.crypto import hash_data, VerifyingKey, SigningKey
from bigchaindb_common.exceptions import (InvalidHash, InvalidSignature,
OperationError, DoubleSpend,
TransactionDoesNotExist,
FulfillmentNotInValidBlock)
from bigchaindb_common.transaction import Transaction
FulfillmentNotInValidBlock,
AssetIdMismatch)
from bigchaindb_common.transaction import Transaction, Asset
from bigchaindb_common.util import gen_timestamp, serialize
class Asset(Asset):
@staticmethod
def get_asset_id(transactions):
"""Get the asset id from a list of transaction ids.
This is useful when we want to check if the multiple inputs of a transaction
are related to the same asset id.
Args:
transactions (list): list of transaction usually inputs that should have a matching asset_id
Returns:
str: uuid of the asset.
Raises:
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}
# check that all the transasctions have the same asset_id
if len(asset_ids) > 1:
raise AssetIdMismatch("All inputs of a transaction need to have the same asset id.")
return asset_ids.pop()
class Transaction(Transaction):
def validate(self, bigchain):
"""Validate a transaction.
@ -36,13 +67,18 @@ class Transaction(Transaction):
inputs_defined = all([ffill.tx_input for ffill in self.fulfillments])
if self.operation in (Transaction.CREATE, Transaction.GENESIS):
# validate inputs
if inputs_defined:
raise ValueError('A CREATE operation has no inputs')
# validate asset
self.asset._validate_asset()
elif self.operation == Transaction.TRANSFER:
if not inputs_defined:
raise ValueError('Only `CREATE` transactions can have null '
'inputs')
# check inputs
# store the inputs so that we can check if the asset ids match
input_txs = []
for ffill in self.fulfillments:
input_txid = ffill.tx_input.txid
input_cid = ffill.tx_input.cid
@ -64,6 +100,12 @@ class Transaction(Transaction):
.format(input_txid))
input_conditions.append(input_tx.conditions[input_cid])
input_txs.append(input_tx)
# validate asset id
asset_id = Asset.get_asset_id(input_txs)
if asset_id != self.asset.data_id:
raise AssetIdMismatch('The asset id of the input does not match the asset id of the transaction')
else:
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
raise TypeError('`operation`: `{}` must be either {}.'

View File

@ -1,20 +1,10 @@
import time
import contextlib
from copy import deepcopy
import threading
import queue
import multiprocessing as mp
import uuid
import rapidjson
from bigchaindb_common import crypto, exceptions
from bigchaindb_common import crypto
from bigchaindb_common.util import serialize
import cryptoconditions as cc
from cryptoconditions.exceptions import ParsingError
import bigchaindb
from bigchaindb.models import Transaction
class ProcessGroup(object):

View File

@ -103,7 +103,7 @@ setup(
'requests~=2.9',
'gunicorn~=19.0',
'multipipes~=0.1.0',
'bigchaindb-common>=0.0.2',
'bigchaindb-common>=0.0.4',
],
setup_requires=['pytest-runner'],
tests_require=tests_require,

0
tests/assets/__init__.py Normal file
View File

19
tests/assets/conftest.py Normal file
View File

@ -0,0 +1,19 @@
import pytest
from ..db import conftest
@pytest.fixture(autouse=True)
def restore_config(request, node_config):
from bigchaindb import config_utils
config_utils.set_config(node_config)
@pytest.fixture(scope='module', autouse=True)
def setup_database(request, node_config):
conftest.setup_database(request, node_config)
@pytest.fixture(scope='function', autouse=True)
def cleanup_tables(request, node_config):
conftest.cleanup_tables(request, node_config)

View File

@ -0,0 +1,163 @@
import pytest
from ..db.conftest import inputs
@pytest.mark.usefixtures('inputs')
def test_asset_transfer(b, user_vk, user_sk):
from bigchaindb.models import Transaction
tx_input = b.get_owned_ids(user_vk).pop()
tx_create = b.get_transaction(tx_input.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk],
tx_create.asset)
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
def test_validate_bad_asset_creation(b, user_vk):
from bigchaindb.models import Transaction
# `divisible` needs to be a boolean
tx = Transaction.create([b.me], [user_vk])
tx.asset.divisible = 1
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
tx_signed.validate(b)
# `refillable` needs to be a boolean
tx = Transaction.create([b.me], [user_vk])
tx.asset.refillable = 1
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
# `updatable` needs to be a boolean
tx = Transaction.create([b.me], [user_vk])
tx.asset.updatable = 1
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
# `data` needs to be a dictionary
tx = Transaction.create([b.me], [user_vk])
tx.asset.data = 'a'
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
# TODO: Check where to test for the amount
"""
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx['transaction']['conditions'][0]['amount'] = 'a'
tx['id'] = get_hash_data(tx['transaction'])
tx_signed = b.sign_transaction(tx, b.me_private)
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx['transaction']['conditions'][0]['amount'] = 2
tx['transaction']['asset'].update({'divisible': False})
tx['id'] = get_hash_data(tx['transaction'])
tx_signed = b.sign_transaction(tx, b.me_private)
with pytest.raises(AmountError):
b.validate_transaction(tx_signed)
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx['transaction']['conditions'][0]['amount'] = 0
tx['id'] = get_hash_data(tx['transaction'])
tx_signed = b.sign_transaction(tx, b.me_private)
with pytest.raises(AmountError):
b.validate_transaction(tx_signed)
"""
@pytest.mark.usefixtures('inputs')
def test_validate_transfer_asset_id_mismatch(b, user_vk, user_sk):
from bigchaindb_common.exceptions import AssetIdMismatch
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_vk).pop()
tx_create = b.get_transaction(tx_create.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk],
tx_create.asset)
tx_transfer.asset.data_id = 'aaa'
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AssetIdMismatch):
tx_transfer_signed.validate(b)
def test_get_asset_id_create_transaction(b, user_vk):
from bigchaindb.models import Transaction, Asset
tx_create = Transaction.create([b.me], [user_vk])
asset_id = Asset.get_asset_id(tx_create)
assert asset_id == tx_create.asset.data_id
@pytest.mark.usefixtures('inputs')
def test_get_asset_id_transfer_transaction(b, user_vk, user_sk):
from bigchaindb.models import Transaction, Asset
tx_create = b.get_owned_ids(user_vk).pop()
tx_create = b.get_transaction(tx_create.txid)
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk],
tx_create.asset)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create a block
block = b.create_block([tx_transfer_signed])
b.write_block(block, durability='hard')
# vote the block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
asset_id = Asset.get_asset_id(tx_transfer)
assert asset_id == tx_transfer.asset.data_id
def test_asset_id_mismatch(b, user_vk):
from bigchaindb.models import Transaction, Asset
from bigchaindb_common.exceptions import AssetIdMismatch
tx1 = Transaction.create([b.me], [user_vk])
tx2 = Transaction.create([b.me], [user_vk])
with pytest.raises(AssetIdMismatch):
Asset.get_asset_id([tx1, tx2])
@pytest.mark.usefixtures('inputs')
def test_get_txs_by_asset_id(b, user_vk, user_sk):
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_vk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
txs = b.get_txs_by_asset_id(asset_id)
assert len(txs) == 1
assert txs[0].id == tx_create.id
assert txs[0].asset.data_id == asset_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk],
tx_create.asset)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
b.write_block(block, durability='hard')
# vote the block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
txs = b.get_txs_by_asset_id(asset_id)
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

View File

@ -84,5 +84,5 @@ def signed_create_tx(b, create_tx):
def signed_transfer_tx(signed_create_tx, user_vk, user_sk):
from bigchaindb.models import Transaction
inputs = signed_create_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], signed_create_tx.asset)
return tx.sign([user_sk])

View File

@ -51,8 +51,8 @@ def setup_database(request, node_config):
r.db(db_name).table('backlog').index_create('transaction_timestamp', r.row['transaction']['timestamp']).run()
# to query by payload uuid
r.db(db_name).table('bigchain').index_create(
'payload_uuid',
r.row['block']['transactions']['transaction']['data']['uuid'],
'metadata_id',
r.row['block']['transactions']['transaction']['metadata']['id'],
multi=True,
).run()
# compound index to read transactions from the backlog per assignee
@ -62,6 +62,11 @@ def setup_database(request, node_config):
# compound index to order votes by block id and node
r.db(db_name).table('votes').index_create('block_and_voter',
[r.row['vote']['voting_for_block'], r.row['node_pubkey']]).run()
# secondary index for asset uuid
r.db(db_name).table('bigchain')\
.index_create('asset_id',
r.row['block']['transactions']['transaction']['asset']['id'], multi=True)\
.run()
# order transactions by id
r.db(db_name).table('bigchain').index_create('transaction_id', r.row['block']['transactions']['id'],
multi=True).run()
@ -114,8 +119,7 @@ def inputs(user_vk):
prev_block_id = g.id
for block in range(4):
transactions = [
Transaction.create(
[b.me], [user_vk], payload={'i': i}).sign([b.me_private])
Transaction.create([b.me], [user_vk]).sign([b.me_private])
for i in range(10)
]
block = b.create_block(transactions)

View File

@ -88,6 +88,11 @@ class TestBigchainApi(object):
assert b.has_previous_vote(block.id, block.voters) is True
def test_get_transactions_for_metadata_mismatch(self, b):
matches = b.get_tx_by_metadata_id('missing')
assert not matches
def test_get_spent_with_double_spend(self, b, monkeypatch):
from bigchaindb_common.exceptions import DoubleSpend
from bigchaindb.models import Transaction
@ -102,13 +107,13 @@ class TestBigchainApi(object):
b.write_block(block1, durability='hard')
monkeypatch.setattr('time.time', lambda: 2)
transfer_tx = Transaction.transfer(tx.to_inputs(), [b.me])
transfer_tx = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset)
transfer_tx = transfer_tx.sign([b.me_private])
block2 = b.create_block([transfer_tx])
b.write_block(block2, durability='hard')
monkeypatch.setattr('time.time', lambda: 3)
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [b.me])
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset)
transfer_tx2 = transfer_tx2.sign([b.me_private])
block3 = b.create_block([transfer_tx2])
b.write_block(block3, durability='hard')
@ -173,24 +178,24 @@ class TestBigchainApi(object):
vote = b.vote(block2.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
assert b.get_transaction(tx1.id) == None
assert b.get_transaction(tx1.id) is None
assert b.get_transaction(tx2.id) == tx2
def test_get_transactions_for_payload(self, b, user_vk):
def test_get_transactions_for_metadata(self, b, user_vk):
from bigchaindb.models import Transaction
payload = {'msg': 'Hello BigchainDB!'}
tx = Transaction.create([b.me], [user_vk], payload=payload)
metadata = {'msg': 'Hello BigchainDB!'}
tx = Transaction.create([b.me], [user_vk], metadata=metadata)
block = b.create_block([tx])
b.write_block(block, durability='hard')
matches = b.get_tx_by_payload_uuid(tx.data.payload_id)
matches = b.get_tx_by_payload_uuid(tx.metadata.data_id)
assert len(matches) == 1
assert matches[0].id == tx.id
def test_get_transactions_for_payload_mismatch(self, b, user_vk):
matches = b.get_tx_by_payload_uuid('missing')
def test_get_transactions_for_metadata(self, b, user_vk):
matches = b.get_tx_by_metadata_id('missing')
assert not matches
@pytest.mark.usefixtures('inputs')
@ -200,7 +205,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
tx = tx.sign([user_sk])
response = b.write_transaction(tx)
@ -218,7 +223,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -238,7 +243,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -480,7 +485,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -505,7 +510,7 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user_vk])
tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -516,6 +521,25 @@ class TestBigchainApi(object):
assert response['assignee'] in b.nodes_except_me
@pytest.mark.usefixtures('inputs')
def test_non_create_input_not_found(self, b, user_vk):
from cryptoconditions import Ed25519Fulfillment
from bigchaindb_common.exceptions import TransactionDoesNotExist
from bigchaindb_common.transaction import (Fulfillment, Asset,
TransactionLink)
from bigchaindb.models import Transaction
from bigchaindb import Bigchain
# Create a fulfillment for a non existing transaction
fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_vk),
[user_vk],
TransactionLink('somethingsomething', 0))
tx = Transaction.transfer([fulfillment], [user_vk], Asset())
with pytest.raises(TransactionDoesNotExist) as excinfo:
tx.validate(Bigchain())
class TestTransactionValidation(object):
def test_create_operation_with_inputs(self, b, user_vk, create_tx):
from bigchaindb_common.transaction import TransactionLink
@ -550,9 +574,11 @@ class TestTransactionValidation(object):
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_vk).pop()
input_transaction = b.get_transaction(input_tx.txid)
sk, vk = generate_key_pair()
tx = Transaction.create([vk], [user_vk])
tx.operation = 'TRANSFER'
tx.asset = input_transaction.asset
tx.fulfillments[0].tx_input = input_tx
with pytest.raises(InvalidSignature):
@ -594,7 +620,7 @@ class TestTransactionValidation(object):
input_tx = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [user_vk])
transfer_tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -618,7 +644,7 @@ class TestTransactionValidation(object):
inputs = input_tx.to_inputs()
# create a transaction that's valid but not in a voted valid block
transfer_tx = Transaction.transfer(inputs, [user_vk])
transfer_tx = Transaction.transfer(inputs, [user_vk], input_tx.asset)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -628,7 +654,8 @@ class TestTransactionValidation(object):
b.write_block(block, durability='hard')
# create transaction with the undecided input
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(), [user_vk])
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(), [user_vk],
transfer_tx.asset)
tx_invalid = tx_invalid.sign([user_sk])
with pytest.raises(FulfillmentNotInValidBlock):
@ -726,7 +753,7 @@ class TestMultipleInputs(object):
tx_link = b.get_owned_ids(user_vk).pop()
input_tx = b.get_transaction(tx_link.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [user2_vk])
tx = Transaction.transfer(inputs, [user2_vk], input_tx.asset)
tx = tx.sign([user_sk])
# validate transaction
@ -734,6 +761,9 @@ class TestMultipleInputs(object):
assert len(tx.fulfillments) == 1
assert len(tx.conditions) == 1
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
@pytest.mark.usefixtures('inputs')
def test_transfer_single_owners_multiple_inputs(self, b, user_sk, user_vk):
from bigchaindb_common import crypto
@ -752,6 +782,9 @@ class TestMultipleInputs(object):
assert len(tx.fulfillments) == len(inputs)
assert len(tx.conditions) == len(inputs)
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
@pytest.mark.usefixtures('inputs')
def test_transfer_single_owners_single_input_from_multiple_outputs(self, b,
user_sk,
@ -779,7 +812,7 @@ class TestMultipleInputs(object):
# get inputs from user2
owned_inputs = b.get_owned_ids(user2_vk)
assert len(owned_inputs) == len(inputs)
assert len(owned_inputs) == len(inputs)
# create a transaction with a single input from a multiple output transaction
tx_link = owned_inputs.pop()
@ -803,14 +836,17 @@ class TestMultipleInputs(object):
owned_inputs = b.get_owned_ids(user_vk)
tx_link = owned_inputs.pop()
inputs = b.get_transaction(tx_link.txid).to_inputs()
tx = Transaction.transfer(inputs, [[user2_vk, user3_vk]])
input_tx = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(input_tx.to_inputs(), [[user2_vk, user3_vk]], input_tx.asset)
tx = tx.sign([user_sk])
assert b.is_valid_transaction(tx) == tx
assert len(tx.fulfillments) == 1
assert len(tx.conditions) == 1
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
@pytest.mark.usefixtures('inputs')
def test_single_owner_before_multiple_owners_after_multiple_inputs(self, b,
user_sk,
@ -865,7 +901,7 @@ class TestMultipleInputs(object):
input_tx = b.get_transaction(owned_input.txid)
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [user3_vk])
transfer_tx = Transaction.transfer(inputs, [user3_vk], input_tx.asset)
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
# validate transaction
@ -873,6 +909,9 @@ class TestMultipleInputs(object):
assert len(transfer_tx.fulfillments) == 1
assert len(transfer_tx.conditions) == 1
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
@pytest.mark.usefixtures('inputs_shared')
def test_multiple_owners_before_single_owner_after_multiple_inputs(self, b,
user_sk, user_vk, user2_vk, user2_sk):
@ -915,15 +954,18 @@ class TestMultipleInputs(object):
# get input
tx_link = b.get_owned_ids(user_vk).pop()
tx_input = b.get_transaction(tx_link.txid).to_inputs()
tx_input = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(tx_input, [[user3_vk, user4_vk]])
tx = Transaction.transfer(tx_input.to_inputs(), [[user3_vk, user4_vk]], tx_input.asset)
tx = tx.sign([user_sk, user2_sk])
assert b.is_valid_transaction(tx) == tx
assert len(tx.fulfillments) == 1
assert len(tx.conditions) == 1
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
@pytest.mark.usefixtures('inputs_shared')
def test_multiple_owners_before_multiple_owners_after_multiple_inputs(self, b,
user_sk, user_vk,
@ -944,7 +986,7 @@ class TestMultipleInputs(object):
assert b.is_valid_transaction(tx) == tx
assert len(tx.fulfillments) == len(inputs)
assert len(tx.conditions) == len(inputs)
assert len(tx.conditions) == len(inputs)
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_vk):
from bigchaindb_common import crypto
@ -963,7 +1005,7 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
tx = Transaction.transfer(tx.to_inputs(), [user2_vk])
tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block, durability='hard')
@ -999,7 +1041,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_vk])
tx_invalid = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset)
tx_invalid = tx_invalid.sign([user_sk])
block = b.create_block([tx_invalid])
b.write_block(block, durability='hard')
@ -1015,6 +1057,9 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
user_vk):
import random
@ -1074,7 +1119,7 @@ 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_vk])
tx = Transaction.transfer(tx.to_inputs(), [user3_vk], tx.asset)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block, durability='hard')
@ -1104,7 +1149,7 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [user2_vk])
tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block, durability='hard')
@ -1139,7 +1184,7 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [user2_vk])
tx = Transaction.transfer(tx.to_inputs(), [user2_vk], tx.asset)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block, durability='hard')
@ -1154,6 +1199,9 @@ class TestMultipleInputs(object):
# Now there should be no spents (the block is invalid)
assert spent_inputs_user1 is None
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
'same asset. Remove this after implementing ',
'multiple assets'))
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_vk):
import random
from bigchaindb_common import crypto
@ -1218,7 +1266,7 @@ class TestMultipleInputs(object):
assert b.get_spent(input_tx.txid, input_tx.cid) is None
# create a transaction
tx = Transaction.transfer(transactions[0].to_inputs(), [user3_vk])
tx = Transaction.transfer(transactions[0].to_inputs(), [user3_vk], transactions[0].asset)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block, durability='hard')

View File

@ -79,7 +79,7 @@ def test_create_bigchain_secondary_index():
assert r.db(dbname).table('bigchain').index_list().contains(
'transaction_id').run(conn) is True
assert r.db(dbname).table('bigchain').index_list().contains(
'payload_uuid').run(conn) is True
'metadata_id').run(conn) is True
def test_create_backlog_table():

View File

@ -83,10 +83,7 @@ def test_full_pipeline(user_vk):
original_txc = []
for i in range(100):
# FIXME Notice the payload. This is only to make sure that the
# transactions hashes are unique. See
# https://github.com/bigchaindb/bigchaindb-common/issues/21
tx = Transaction.create([b.me], [user_vk], payload={'i': i})
tx = Transaction.create([b.me], [user_vk])
tx = tx.sign([b.me_private])
original_txc.append(tx.to_dict())

View File

@ -274,7 +274,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])
tx2 = Transaction.transfer(tx.to_inputs(), [test_user2_pub], tx.asset)
tx2 = tx2.sign([test_user_priv])
monkeypatch.setattr('time.time', lambda: 2)

View File

@ -77,7 +77,7 @@ def test_post_transfer_transaction_endpoint(b, client, user_vk, user_sk):
input_valid = b.get_owned_ids(user_vk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub])
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset)
transfer_tx = transfer_tx.sign([user_sk])
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
@ -94,7 +94,7 @@ def test_post_invalid_transfer_transaction_returns_400(b, client, user_vk, user_
input_valid = b.get_owned_ids(user_vk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub])
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset)
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
assert res.status_code == 400