mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-06-28 00:27:45 +02:00
Added validation for amounts
Added a new db call to return an asset instance given the id Created tests
This commit is contained in:
parent
db55aa8153
commit
a212aba35b
|
@ -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, Metadata
|
||||
from bigchaindb.common.transaction import TransactionLink, Metadata, Asset
|
||||
|
||||
import rethinkdb as r
|
||||
|
||||
|
@ -366,6 +366,32 @@ class Bigchain(object):
|
|||
|
||||
return [Transaction.from_dict(tx) for tx in cursor]
|
||||
|
||||
def get_asset_by_id(self, asset_id):
|
||||
"""Returns the asset associated with an asset_id
|
||||
|
||||
Args:
|
||||
asset_id (str): The asset id
|
||||
|
||||
Returns:
|
||||
:class:`~bigchaindb.common.transaction.Asset` if the asset
|
||||
exists else None
|
||||
"""
|
||||
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)
|
||||
.filter(lambda transaction:
|
||||
transaction['transaction']['operation'] == 'CREATE')
|
||||
.pluck({'transaction': 'asset'}))
|
||||
cursor = list(cursor)
|
||||
|
||||
if cursor:
|
||||
return Asset.from_dict(cursor[0]['transaction']['asset'])
|
||||
|
||||
return cursor
|
||||
|
||||
def get_spent(self, txid, cid):
|
||||
"""Check if a `txid` was already used as an input.
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
|
|||
OperationError, DoubleSpend,
|
||||
TransactionDoesNotExist,
|
||||
FulfillmentNotInValidBlock,
|
||||
AssetIdMismatch)
|
||||
AssetIdMismatch, AmountError)
|
||||
from bigchaindb.common.transaction import Transaction, Asset
|
||||
from bigchaindb.common.util import gen_timestamp, serialize
|
||||
|
||||
|
@ -41,7 +41,8 @@ class Transaction(Transaction):
|
|||
if inputs_defined:
|
||||
raise ValueError('A CREATE operation has no inputs')
|
||||
# validate asset
|
||||
self.asset._validate_asset()
|
||||
amount = sum([condition.amount for condition in self.conditions])
|
||||
self.asset._validate_asset(amount=amount)
|
||||
elif self.operation == Transaction.TRANSFER:
|
||||
if not inputs_defined:
|
||||
raise ValueError('Only `CREATE` transactions can have null '
|
||||
|
@ -49,6 +50,7 @@ class Transaction(Transaction):
|
|||
# check inputs
|
||||
# store the inputs so that we can check if the asset ids match
|
||||
input_txs = []
|
||||
input_amount = 0
|
||||
for ffill in self.fulfillments:
|
||||
input_txid = ffill.tx_input.txid
|
||||
input_cid = ffill.tx_input.cid
|
||||
|
@ -71,11 +73,28 @@ class Transaction(Transaction):
|
|||
|
||||
input_conditions.append(input_tx.conditions[input_cid])
|
||||
input_txs.append(input_tx)
|
||||
input_amount += input_tx.conditions[input_cid].amount
|
||||
|
||||
# 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')
|
||||
raise AssetIdMismatch(('The asset id of the input does not'
|
||||
' match the asset id of the'
|
||||
' transaction'))
|
||||
|
||||
# get the asset creation to see if its divisible or not
|
||||
asset = bigchain.get_asset_by_id(asset_id)
|
||||
# validate the asset
|
||||
asset._validate_asset(amount=input_amount)
|
||||
# validate the amounts
|
||||
output_amount = sum([condition.amount for
|
||||
condition in self.conditions])
|
||||
if output_amount != input_amount:
|
||||
raise AmountError(('The amout used in the inputs `{}`'
|
||||
' needs to be same as the amount used'
|
||||
' in the outputs `{}`')
|
||||
.format(input_amount, output_amount))
|
||||
|
||||
else:
|
||||
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
|
||||
raise TypeError('`operation`: `{}` must be either {}.'
|
||||
|
|
|
@ -146,7 +146,7 @@ def test_get_txs_by_asset_id(b, user_vk, user_sk):
|
|||
assert txs[0].asset.data_id == asset_id
|
||||
|
||||
# create a transfer transaction
|
||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_vk],
|
||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_vk], 1)],
|
||||
tx_create.asset)
|
||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||
# create the block
|
||||
|
@ -165,6 +165,31 @@ def test_get_txs_by_asset_id(b, user_vk, user_sk):
|
|||
assert asset_id == txs[1].asset.data_id
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_get_asset_by_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
|
||||
|
||||
# create a transfer transaction
|
||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_vk], 1)],
|
||||
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
|
||||
|
||||
asset = b.get_asset_by_id(asset_id)
|
||||
assert asset == tx_create.asset
|
||||
|
||||
def test_create_invalid_divisible_asset(b, user_vk, user_sk):
|
||||
from bigchaindb.models import Transaction, Asset
|
||||
from bigchaindb.common.exceptions import AmountError
|
||||
|
|
|
@ -546,6 +546,45 @@ def test_multiple_in_different_transactions(b, user_vk, user_sk):
|
|||
assert fid1_input == tx_transfer1.id
|
||||
|
||||
|
||||
# In a TRANSFER transaction of a divisible asset the amount being spent in the
|
||||
# inputs needs to match the amount being sent in the outputs.
|
||||
# In other words `amount_in_inputs - amount_in_outputs == 0`
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_amount_error_transfer(b, user_vk, user_sk):
|
||||
from bigchaindb.models import Transaction
|
||||
from bigchaindb.common.transaction import Asset
|
||||
from bigchaindb.common.exceptions import AmountError
|
||||
|
||||
# CREATE divisible asset
|
||||
asset = Asset(divisible=True)
|
||||
tx_create = Transaction.create([b.me], [([user_vk], 100)], asset=asset)
|
||||
tx_create_signed = tx_create.sign([b.me_private])
|
||||
# create block
|
||||
block = b.create_block([tx_create_signed])
|
||||
assert block.validate(b) == block
|
||||
b.write_block(block, durability='hard')
|
||||
# vote
|
||||
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||
b.write_vote(vote)
|
||||
|
||||
# TRANSFER
|
||||
# output amount less than input amount
|
||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 50)],
|
||||
asset=tx_create.asset)
|
||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||
with pytest.raises(AmountError):
|
||||
tx_transfer_signed.validate(b)
|
||||
|
||||
# TRANSFER
|
||||
# output amount greater than input amount
|
||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 101)],
|
||||
asset=tx_create.asset)
|
||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||
with pytest.raises(AmountError):
|
||||
tx_transfer_signed.validate(b)
|
||||
|
||||
|
||||
@pytest.mark.skip
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_transaction_unfulfilled_fulfillments(b, user_vk,
|
||||
user_sk):
|
||||
|
@ -576,8 +615,6 @@ def test_transaction_unfulfilled_fulfillments(b, user_vk,
|
|||
# invalid. Somehow the validation passes
|
||||
assert b.is_valid_transaction(tx_transfer_signed) == False
|
||||
|
||||
#test input output amount mismatch. Both when output is less and greater then input
|
||||
|
||||
|
||||
@pytest.mark.skip(reason=('get_subcondition_from_vk does not always work'
|
||||
' as expected'))
|
||||
|
|
Loading…
Reference in New Issue
Block a user