remove structural and schematic validations from Transaction.validate which is validating the spend

This commit is contained in:
Scott Sadler 2017-03-14 12:39:57 +01:00
parent b997057962
commit 7c461e47d7
6 changed files with 30 additions and 68 deletions

View File

@ -12,6 +12,17 @@ properties:
"$ref": "#/definitions/sha3_hexdigest" "$ref": "#/definitions/sha3_hexdigest"
description: | description: |
ID of the transaction that created the asset. ID of the transaction that created the asset.
inputs:
type: array
title: "Transaction inputs"
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "object"
definitions: definitions:
sha3_hexdigest: sha3_hexdigest:
pattern: "[0-9a-f]{64}" pattern: "[0-9a-f]{64}"

View File

@ -3,7 +3,7 @@ from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
DoubleSpend, InputDoesNotExist, DoubleSpend, InputDoesNotExist,
TransactionNotInValidBlock, TransactionNotInValidBlock,
AssetIdMismatch, AmountError, AssetIdMismatch, AmountError,
SybilError, ValidationError, SybilError,
DuplicateTransaction) DuplicateTransaction)
from bigchaindb.common.transaction import Transaction from bigchaindb.common.transaction import Transaction
from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.common.utils import gen_timestamp, serialize
@ -12,7 +12,7 @@ from bigchaindb.common.schema import validate_transaction_schema
class Transaction(Transaction): class Transaction(Transaction):
def validate(self, bigchain): def validate(self, bigchain):
"""Validate a transaction. """Validate transaction spend
Args: Args:
bigchain (Bigchain): an instantiated bigchaindb.Bigchain object. bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
@ -26,30 +26,8 @@ class Transaction(Transaction):
ValidationError: If the transaction is invalid ValidationError: If the transaction is invalid
""" """
input_conditions = [] input_conditions = []
inputs_defined = all([input_.fulfills for input_ in self.inputs])
# validate amounts
if any(output.amount < 1 for output in self.outputs):
raise AmountError('`amount` needs to be greater than zero')
if self.operation in (Transaction.CREATE, Transaction.GENESIS):
# validate asset
if self.asset['data'] is not None and not isinstance(self.asset['data'], dict):
raise ValidationError(('`asset.data` must be a dict instance or '
'None for `CREATE` transactions'))
# validate inputs
if inputs_defined:
raise ValidationError('A CREATE operation has no inputs')
elif self.operation == Transaction.TRANSFER:
# validate asset
if not isinstance(self.asset['id'], str):
raise ValidationError('`asset.id` must be a string for '
'`TRANSFER` transations')
# check inputs
if not inputs_defined:
raise ValidationError('Only `CREATE` transactions can have '
'null inputs')
if self.operation == Transaction.TRANSFER:
# store the inputs so that we can check if the asset ids match # store the inputs so that we can check if the asset ids match
input_txs = [] input_txs = []
for input_ in self.inputs: for input_ in self.inputs:
@ -74,8 +52,6 @@ class Transaction(Transaction):
output = input_tx.outputs[input_.fulfills.output] output = input_tx.outputs[input_.fulfills.output]
input_conditions.append(output) input_conditions.append(output)
input_txs.append(input_tx) input_txs.append(input_tx)
if output.amount < 1:
raise AmountError('`amount` needs to be greater than zero')
# Validate that all inputs are distinct # Validate that all inputs are distinct
links = [i.fulfills.to_uri() for i in self.inputs] links = [i.fulfills.to_uri() for i in self.inputs]
@ -89,11 +65,6 @@ class Transaction(Transaction):
' match the asset id of the' ' match the asset id of the'
' transaction')) ' transaction'))
# validate the amounts
for output in self.outputs:
if output.amount < 1:
raise AmountError('`amount` needs to be greater than zero')
input_amount = sum([input_condition.amount for input_condition in input_conditions]) input_amount = sum([input_condition.amount for input_condition in input_conditions])
output_amount = sum([output_condition.amount for output_condition in self.outputs]) output_amount = sum([output_condition.amount for output_condition in self.outputs])
@ -103,11 +74,6 @@ class Transaction(Transaction):
' in the outputs `{}`') ' in the outputs `{}`')
.format(input_amount, output_amount)) .format(input_amount, output_amount))
else:
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
raise ValidationError('`operation`: `{}` must be either {}.'
.format(self.operation, allowed_operations))
if not self.inputs_valid(input_conditions): if not self.inputs_valid(input_conditions):
raise InvalidSignature('Transaction signature is invalid.') raise InvalidSignature('Transaction signature is invalid.')

View File

@ -28,7 +28,7 @@ def test_validate_bad_asset_creation(b, user_pk):
tx_signed = tx.sign([b.me_private]) tx_signed = tx.sign([b.me_private])
with pytest.raises(ValidationError): with pytest.raises(ValidationError):
b.validate_transaction(tx_signed) Transaction.from_dict(tx_signed.to_dict())
@pytest.mark.bdb @pytest.mark.bdb
@ -93,15 +93,15 @@ def test_asset_id_mismatch(b, user_pk):
def test_create_invalid_divisible_asset(b, user_pk, user_sk): def test_create_invalid_divisible_asset(b, user_pk, user_sk):
from bigchaindb.models import Transaction from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import AmountError from bigchaindb.common.exceptions import ValidationError
# Asset amount must be more than 0 # Asset amount must be more than 0
tx = Transaction.create([user_pk], [([user_pk], 1)]) tx = Transaction.create([user_pk], [([user_pk], 1)])
tx.outputs[0].amount = 0 tx.outputs[0].amount = 0
tx.sign([user_sk]) tx.sign([user_sk])
with pytest.raises(AmountError): with pytest.raises(ValidationError):
b.validate_transaction(tx) Transaction.from_dict(tx.to_dict())
def test_create_valid_divisible_asset(b, user_pk, user_sk): def test_create_valid_divisible_asset(b, user_pk, user_sk):

View File

@ -638,6 +638,7 @@ def test_divide(b, user_pk, user_sk):
# Check that negative inputs are caught when creating a TRANSFER transaction # Check that negative inputs are caught when creating a TRANSFER transaction
@pytest.mark.skip(reason='part of tx structural tests')
@pytest.mark.bdb @pytest.mark.bdb
@pytest.mark.usefixtures('inputs') @pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer(b, user_pk): def test_non_positive_amounts_on_transfer(b, user_pk):
@ -662,6 +663,7 @@ def test_non_positive_amounts_on_transfer(b, user_pk):
# Check that negative inputs are caught when validating a TRANSFER transaction # Check that negative inputs are caught when validating a TRANSFER transaction
@pytest.mark.skip(reason='part of tx structural tests')
@pytest.mark.bdb @pytest.mark.bdb
@pytest.mark.usefixtures('inputs') @pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk): def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
@ -704,6 +706,7 @@ def test_non_positive_amounts_on_create(b, user_pk):
# Check that negative inputs are caught when validating a CREATE transaction # Check that negative inputs are caught when validating a CREATE transaction
@pytest.mark.skip(reason='part of tx structural tests')
@pytest.mark.bdb @pytest.mark.bdb
@pytest.mark.usefixtures('inputs') @pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_create_validate(b, user_pk): def test_non_positive_amounts_on_create_validate(b, user_pk):

View File

@ -972,3 +972,12 @@ def test_create_tx_no_asset_id(b, utx):
utx.asset['id'] = 'b' * 64 utx.asset['id'] = 'b' * 64
with raises(SchemaValidationError): with raises(SchemaValidationError):
validate_transaction_model(utx) validate_transaction_model(utx)
def test_transfer_tx_asset_schema(transfer_utx):
from bigchaindb.common.exceptions import SchemaValidationError
from .utils import validate_transaction_model
tx = transfer_utx
tx.asset['data'] = {}
with raises(SchemaValidationError):
validate_transaction_model(tx)

View File

@ -3,8 +3,6 @@ from time import sleep
import pytest import pytest
from unittest.mock import patch from unittest.mock import patch
from bigchaindb.common.exceptions import ValidationError
pytestmark = pytest.mark.bdb pytestmark = pytest.mark.bdb
@ -596,24 +594,6 @@ class TestBigchainApi(object):
class TestTransactionValidation(object): class TestTransactionValidation(object):
def test_create_operation_with_inputs(self, b, user_pk, create_tx):
from bigchaindb.common.transaction import TransactionLink
# Manipulate input so that it has a `fulfills` defined even
# though it shouldn't have one
create_tx.inputs[0].fulfills = TransactionLink('abc', 0)
with pytest.raises(ValidationError) as excinfo:
b.validate_transaction(create_tx)
assert excinfo.value.args[0] == 'A CREATE operation has no inputs'
def test_transfer_operation_no_inputs(self, b, user_pk,
signed_transfer_tx):
signed_transfer_tx.inputs[0].fulfills = None
with pytest.raises(ValidationError) as excinfo:
b.validate_transaction(signed_transfer_tx)
assert excinfo.value.args[0] == 'Only `CREATE` transactions can have null inputs'
def test_non_create_input_not_found(self, b, user_pk, signed_transfer_tx): def test_non_create_input_not_found(self, b, user_pk, signed_transfer_tx):
from bigchaindb.common.exceptions import InputDoesNotExist from bigchaindb.common.exceptions import InputDoesNotExist
from bigchaindb.common.transaction import TransactionLink from bigchaindb.common.transaction import TransactionLink
@ -1313,10 +1293,3 @@ def test_is_new_transaction(b, genesis_block):
# Tx is new because it's only found in an invalid block # Tx is new because it's only found in an invalid block
assert b.is_new_transaction(tx.id) assert b.is_new_transaction(tx.id)
assert b.is_new_transaction(tx.id, exclude_block_id=block.id) assert b.is_new_transaction(tx.id, exclude_block_id=block.id)
def test_validate_asset_id_string(signed_transfer_tx):
from bigchaindb.common.exceptions import ValidationError
signed_transfer_tx.asset['id'] = 1
with pytest.raises(ValidationError):
signed_transfer_tx.validate(None)