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

initial integration of asset

This commit is contained in:
Rodolphe Marques 2016-10-07 15:46:21 +02:00
parent 48c657e2f2
commit 0652348bf0
9 changed files with 74 additions and 116 deletions

View File

@ -1,6 +1,6 @@
import rethinkdb as r
from bigchaindb.exceptions import AssetIdMismatch, AmountError
from bigchaindb_common.exceptions import AssetIdMismatch, AmountError
def get_asset_id(transactions):
@ -31,38 +31,6 @@ def get_asset_id(transactions):
return asset_ids.pop()
def validate_asset_creation(asset_data, divisible, updatable, refillable, amount):
"""Validate digital asset
Args:
asset_data (dict or None): dictionary describing the digital asset (only used on a create transaction)
divisible (boolean): Whether the asset is divisible or not. Defaults to `False`.
updatable (boolean): Whether the data in the asset can be updated in the future or not.
Defaults to `False`.
refillable (boolean): Whether the amount of the asset can change after its creation.
Defaults to `False`.
amount (int): The amount of "shares". Only relevant if the asset is marked as divisible.
Defaults to `1`.
"""
if asset_data is not None and not isinstance(asset_data, dict):
raise TypeError('`data` must be a dict instance or None')
if not isinstance(divisible, bool):
raise TypeError('`divisible` must be a boolean')
if not isinstance(refillable, bool):
raise TypeError('`refillable` must be a boolean')
if not isinstance(updatable, bool):
raise TypeError('`updatable` must be a boolean')
if not isinstance(amount, int):
raise TypeError('`amount` must be an int')
if divisible is False and amount != 1:
raise AmountError('Non-divisible assets must have amount 1')
if amount < 1:
raise AmountError('The amount cannot be less then 1')
if divisible or updatable or refillable or amount != 1:
raise NotImplementedError("Divisible assets are not yet implemented!")
def get_transactions_by_asset_id(asset_id, bigchain, read_mode='majority'):
cursor = r.table('bigchain', read_mode=read_mode)\
.get_all(asset_id, index='asset_id')\

View File

@ -6,7 +6,7 @@ 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
@ -553,7 +553,7 @@ class Bigchain(object):
metadata = {'message': 'Hello World from the BigchainDB'}
# TODO: When updating the BDBC lib, change `payload` to `metadata`
transaction = Transaction.create([self.me], [self.me],
payload=metadata)
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

@ -2,40 +2,29 @@ import pytest
from ..db.conftest import inputs
def test_asset_creation(b, user_vk):
asset_data = {'msg': 'hello'}
tx = b.create_transaction(b.me, user_vk, None, 'CREATE', asset_data=asset_data)
tx_signed = b.sign_transaction(tx, b.me_private)
assert b.validate_transaction(tx_signed) == tx_signed
assert tx_signed['transaction']['asset']['data'] == asset_data
assert tx_signed['transaction']['asset']['refillable'] is False
assert tx_signed['transaction']['asset']['divisible'] is False
assert tx_signed['transaction']['asset']['updatable'] is False
assert tx_signed['transaction']['conditions'][0]['amount'] == 1
@pytest.mark.usefixtures('inputs')
def test_asset_transfer(b, user_vk, user_sk):
tx_input = b.get_owned_ids(user_vk).pop()
tx_create = b.get_transaction(tx_input['txid'])
tx_transfer = b.create_transaction(user_vk, user_vk, tx_input, 'TRANSFER')
tx_transfer_signed = b.sign_transaction(tx_transfer, user_sk)
from bigchaindb.models import Transaction
assert b.validate_transaction(tx_transfer_signed) == tx_transfer_signed
assert tx_transfer_signed['transaction']['asset']['id'] == tx_create['transaction']['asset']['id']
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.util import get_hash_data
from bigchaindb.exceptions import AmountError
from bigchaindb_common.exceptions import AmountError
from bigchaindb.models import Transaction
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx['transaction']['asset'].update({'divisible': 1})
tx['id'] = get_hash_data(tx['transaction'])
tx_signed = b.sign_transaction(tx, b.me_private)
tx = Transaction.create([b.me], [user_vk])
tx.asset.divisible = 1
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
tx_signed.validate(b)
tx = b.create_transaction(b.me, user_vk, None, 'CREATE')
tx['transaction']['asset'].update({'refillable': 1})
@ -84,7 +73,7 @@ def test_validate_bad_asset_creation(b, user_vk):
@pytest.mark.usefixtures('inputs')
def test_validate_transfer_asset_id_mismatch(b, user_vk, user_sk):
from bigchaindb.util import get_hash_data
from bigchaindb.exceptions import AssetIdMismatch
from bigchaindb_common.exceptions import AssetIdMismatch
tx_input = b.get_owned_ids(user_vk).pop()
tx = b.create_transaction(user_vk, user_vk, tx_input, 'TRANFER')
@ -96,7 +85,7 @@ def test_validate_transfer_asset_id_mismatch(b, user_vk, user_sk):
def test_validate_asset_arguments(b):
from bigchaindb.exceptions import AmountError
from bigchaindb_common.exceptions import AmountError
with pytest.raises(TypeError):
b.create_transaction(b.me, b.me, None, 'CREATE', divisible=1)
@ -147,7 +136,7 @@ def test_get_asset_id_transfer_transaction(b, user_vk, user_sk):
@pytest.mark.usefixtures('inputs')
def test_asset_id_mismatch(b, user_vk):
from bigchaindb.assets import get_asset_id
from bigchaindb.exceptions import AssetIdMismatch
from bigchaindb_common.exceptions import AssetIdMismatch
tx_input1, tx_input2 = b.get_owned_ids(user_vk)[:2]
tx1 = b.get_transaction(tx_input1['txid'])
@ -158,7 +147,7 @@ def test_asset_id_mismatch(b, user_vk):
def test_get_asset_id_transaction_does_not_exist(b, user_vk):
from bigchaindb.exceptions import TransactionDoesNotExist
from bigchaindb_common.exceptions import TransactionDoesNotExist
with pytest.raises(TransactionDoesNotExist):
b.create_transaction(user_vk, user_vk, {'txid': 'bored', 'cid': '0'}, 'TRANSFER')

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

@ -114,10 +114,10 @@ def inputs(user_vk):
pass
# 2. create block with transactions for `USER` to spend
for block in range(4):
for block in range(4):
transactions = [
Transaction.create(
[b.me], [user_vk], payload={'i': i}).sign([b.me_private])
[b.me], [user_vk], metadata={'i': i}).sign([b.me_private])
for i in range(10)
]
block = b.create_block(transactions)

View File

@ -83,13 +83,11 @@ class TestBigchainApi(object):
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
assert b.has_previous_vote(block.id, block.voters) is True
matches = b.get_tx_by_metadata_id(metadata_id)
assert len(matches) == 1
assert matches[0]['id'] == tx['id']
def test_get_transactions_for_metadata_mismatch(self):
def test_get_transactions_for_metadata_mismatch(self, b):
matches = b.get_tx_by_metadata_id('missing')
assert not matches
@ -107,13 +105,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')
@ -181,21 +179,21 @@ class TestBigchainApi(object):
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')
@ -205,7 +203,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)
@ -223,7 +221,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)
@ -243,7 +241,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)
@ -485,7 +483,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)
@ -510,7 +508,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)
@ -524,19 +522,25 @@ class TestBigchainApi(object):
# TODO: Make this test work
@pytest.mark.usefixtures('inputs')
def test_non_create_input_not_found(self, b, user_vk):
with pytest.raises(exceptions.TransactionDoesNotExist) as excinfo:
b.create_transaction(user_vk, user_vk, {'txid': 'c', 'cid': 0}, 'TRANSFER')
from bigchaindb_common.exceptions import TransactionDoesNotExist
from bigchaindb_common.transaction import Fulfillment, Asset
from bigchaindb.models import Transaction
from bigchaindb import Bigchain
assert excinfo.value.args[0] == 'input with txid `c` does not exist in the bigchain'
# Create a fulfillment for a non existing transaction
fulfillment_dict = {'fulfillment': {'bitmask': 32,
'public_key': user_vk,
'signature': None,
'type': 'fulfillment',
'type_id': 4},
'input': {'cid': 0,
'txid': 'somethingsomething'},
'owners_before': [user_vk]}
fulfillment = Fulfillment.from_dict(fulfillment_dict)
tx = Transaction.transfer([fulfillment], [user_vk], Asset())
# Create transaction does not let you create a malformed transaction.
# Create a custom malformed transaction and check if validate catches the error
tx_input = b.get_owned_ids(user_vk).pop()
tx = b.create_transaction(user_vk, user_vk, tx_input, 'TRANSFER')
tx['transaction']['fulfillments'][0]['input'] = {'txid': 'c', 'cid': 0}
with pytest.raises(exceptions.TransactionDoesNotExist) as excinfo:
b.validate_transaction(tx)
with pytest.raises(TransactionDoesNotExist) as excinfo:
tx.validate(Bigchain())
class TestTransactionValidation(object):
@ -606,7 +610,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)
@ -712,7 +716,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
@ -810,8 +814,8 @@ 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
@ -873,7 +877,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
@ -928,9 +932,9 @@ class TestMultipleInputs(object):
b.write_block(block, durability='hard')
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
@ -987,7 +991,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')
@ -1023,7 +1027,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')
@ -1101,7 +1105,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')
@ -1131,7 +1135,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')
@ -1166,7 +1170,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')
@ -1248,7 +1252,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

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

@ -272,7 +272,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