Merge branch 'master' into voting-class-integration

This commit is contained in:
Scott Sadler 2017-03-13 13:35:03 +01:00
parent e011f50bc7
commit dc58466de3
16 changed files with 226 additions and 239 deletions

View File

@ -15,7 +15,3 @@ class OperationError(BackendError):
class DuplicateKeyError(OperationError):
"""Exception raised when an insert fails because the key is not unique"""
class BigchainDBCritical(Exception):
"""Unhandleable error that requires attention"""

View File

@ -7,44 +7,6 @@ class ConfigurationError(BigchainDBError):
"""Raised when there is a problem with server configuration"""
class OperationError(BigchainDBError):
"""Raised when an operation cannot go through"""
class TransactionDoesNotExist(BigchainDBError):
"""Raised if the transaction is not in the database"""
class TransactionOwnerError(BigchainDBError):
"""Raised if a user tries to transfer a transaction they don't own"""
class DoubleSpend(BigchainDBError):
"""Raised if a double spend is found"""
class ValidationError(BigchainDBError):
"""Raised if there was an error in validation"""
class InvalidHash(ValidationError):
"""Raised if there was an error checking the hash for a particular
operation"""
class SchemaValidationError(ValidationError):
"""Raised if there was any error validating an object's schema"""
class InvalidSignature(BigchainDBError):
"""Raised if there was an error checking the signature for a particular
operation"""
class DuplicateTransaction(ValidationError):
"""Raised if a duplicated transaction is found"""
class DatabaseAlreadyExists(BigchainDBError):
"""Raised when trying to create the database but the db is already there"""
@ -53,15 +15,6 @@ class DatabaseDoesNotExist(BigchainDBError):
"""Raised when trying to delete the database but the db is not there"""
class KeypairNotFoundException(BigchainDBError):
"""Raised if operation cannot proceed because the keypair was not given"""
class KeypairMismatchException(BigchainDBError):
"""Raised if the private key(s) provided for signing don't match any of the
current owner(s)"""
class StartupError(BigchainDBError):
"""Raised when there is an error starting up the system"""
@ -74,14 +27,82 @@ class CyclicBlockchainError(BigchainDBError):
"""Raised when there is a cycle in the blockchain"""
class TransactionNotInValidBlock(BigchainDBError):
class KeypairNotFoundException(BigchainDBError):
"""Raised if operation cannot proceed because the keypair was not given"""
class KeypairMismatchException(BigchainDBError):
"""Raised if the private key(s) provided for signing don't match any of the
current owner(s)"""
class OperationError(BigchainDBError):
"""Raised when an operation cannot go through"""
################################################################################
# Validation errors
#
# All validation errors (which are handleable errors, not faults) should
# subclass ValidationError. However, where possible they should also have their
# own distinct type to differentiate them from other validation errors,
# especially for the purposes of testing.
class ValidationError(BigchainDBError):
"""Raised if there was an error in validation"""
class DoubleSpend(ValidationError):
"""Raised if a double spend is found"""
class InvalidHash(ValidationError):
"""Raised if there was an error checking the hash for a particular
operation"""
class SchemaValidationError(ValidationError):
"""Raised if there was any error validating an object's schema"""
class InvalidSignature(ValidationError):
"""Raised if there was an error checking the signature for a particular
operation"""
class ImproperVoteError(ValidationError):
"""Raised if a vote is not constructed correctly, or signed incorrectly"""
class MultipleVotesError(ValidationError):
"""Raised if a voter has voted more than once"""
class TransactionNotInValidBlock(ValidationError):
"""Raised when a transfer transaction is attempting to fulfill the
outputs of a transaction that is in an invalid or undecided block"""
class AssetIdMismatch(BigchainDBError):
class AssetIdMismatch(ValidationError):
"""Raised when multiple transaction inputs related to different assets"""
class AmountError(BigchainDBError):
class AmountError(ValidationError):
"""Raised when there is a problem with a transaction's output amounts"""
class InputDoesNotExist(ValidationError):
"""Raised if a transaction input does not exist"""
class TransactionOwnerError(ValidationError):
"""Raised if a user tries to transfer a transaction they don't own"""
class SybilError(ValidationError):
"""If a block or vote comes from an unidentifiable node"""
class DuplicateTransaction(ValidationError):
"""Raised if a duplicated transaction is found"""

View File

@ -1,6 +1,7 @@
import random
from time import time
from bigchaindb import exceptions as core_exceptions
from bigchaindb.common import crypto, exceptions
from bigchaindb.common.utils import gen_timestamp, serialize
from bigchaindb.common.transaction import TransactionLink
@ -8,7 +9,6 @@ from bigchaindb.common.transaction import TransactionLink
import bigchaindb
from bigchaindb import backend, config_utils, utils
from bigchaindb.backend import exceptions as backend_exceptions
from bigchaindb.consensus import BaseConsensusRules
from bigchaindb.models import Block, Transaction
@ -110,7 +110,9 @@ class Bigchain(object):
dict: database response or None if no reassignment is possible
"""
other_nodes = self.federation.difference([transaction['assignee']])
other_nodes = tuple(
self.federation.difference([transaction['assignee']])
)
new_assignee = random.choice(other_nodes) if other_nodes else self.me
return backend.query.update_transaction(
@ -151,31 +153,6 @@ class Bigchain(object):
return self.consensus.validate_transaction(self, transaction)
def is_valid_transaction(self, transaction):
"""Check whether a transaction is valid or invalid.
Similar to :meth:`~bigchaindb.Bigchain.validate_transaction`
but never raises an exception. It returns :obj:`False` if
the transaction is invalid.
Args:
transaction (:Class:`~bigchaindb.models.Transaction`): transaction
to check.
Returns:
The :class:`~bigchaindb.models.Transaction` instance if valid,
otherwise :obj:`False`.
"""
try:
return self.validate_transaction(transaction)
except (ValueError, exceptions.OperationError,
exceptions.TransactionDoesNotExist,
exceptions.TransactionOwnerError, exceptions.DoubleSpend,
exceptions.InvalidHash, exceptions.InvalidSignature,
exceptions.TransactionNotInValidBlock, exceptions.AmountError):
return False
def is_new_transaction(self, txid, exclude_block_id=None):
"""
Return True if the transaction does not exist in any
@ -317,7 +294,7 @@ class Bigchain(object):
if list(validity.values()).count(Bigchain.BLOCK_VALID) > 1:
block_ids = str([block for block in validity
if validity[block] == Bigchain.BLOCK_VALID])
raise backend_exceptions.BigchainDBCritical(
raise core_exceptions.CriticalDoubleInclusion(
'Transaction {tx} is present in '
'multiple valid blocks: {block_ids}'
.format(tx=txid, block_ids=block_ids))
@ -370,10 +347,9 @@ class Bigchain(object):
if self.get_transaction(transaction['id']):
num_valid_transactions += 1
if num_valid_transactions > 1:
raise exceptions.DoubleSpend(('`{}` was spent more than'
' once. There is a problem'
' with the chain')
.format(txid))
raise core_exceptions.CriticalDoubleSpend(
'`{}` was spent more than once. There is a problem'
' with the chain'.format(txid))
if num_valid_transactions:
return Transaction.from_dict(transactions[0])

View File

@ -1,2 +1,10 @@
class BigchainDBError(Exception):
"""Base class for BigchainDB exceptions."""
class CriticalDoubleSpend(BigchainDBError):
"""Data integrity error that requires attention"""
class CriticalDoubleInclusion(BigchainDBError):
"""Data integrity error that requires attention"""

View File

@ -1,9 +1,9 @@
from bigchaindb.common.crypto import hash_data, PublicKey, PrivateKey
from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
OperationError, DoubleSpend,
TransactionDoesNotExist,
DoubleSpend, InputDoesNotExist,
TransactionNotInValidBlock,
AssetIdMismatch, AmountError,
SybilError, ValidationError,
DuplicateTransaction)
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.utils import gen_timestamp, serialize
@ -23,19 +23,10 @@ class Transaction(Transaction):
invalid.
Raises:
OperationError: if the transaction operation is not supported
TransactionDoesNotExist: if the input of the transaction is not
found
TransactionNotInValidBlock: if the input of the transaction is not
in a valid block
TransactionOwnerError: if the new transaction is using an input it
doesn't own
DoubleSpend: if the transaction is a double spend
InvalidHash: if the hash of the transaction is wrong
InvalidSignature: if the signature of the transaction is wrong
ValidationError: If the transaction is invalid
"""
if len(self.inputs) == 0:
raise ValueError('Transaction contains no inputs')
raise ValidationError('Transaction contains no inputs')
input_conditions = []
inputs_defined = all([input_.fulfills for input_ in self.inputs])
@ -47,20 +38,20 @@ class Transaction(Transaction):
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 TypeError(('`asset.data` must be a dict instance or '
'None for `CREATE` transactions'))
raise ValidationError(('`asset.data` must be a dict instance or '
'None for `CREATE` transactions'))
# validate inputs
if inputs_defined:
raise ValueError('A CREATE operation has no inputs')
raise ValidationError('A CREATE operation has no inputs')
elif self.operation == Transaction.TRANSFER:
# validate asset
if not isinstance(self.asset['id'], str):
raise ValueError(('`asset.id` must be a string for '
'`TRANSFER` transations'))
raise ValidationError('`asset.id` must be a string for '
'`TRANSFER` transations')
# check inputs
if not inputs_defined:
raise ValueError('Only `CREATE` transactions can have null '
'inputs')
raise ValidationError('Only `CREATE` transactions can have '
'null inputs')
# store the inputs so that we can check if the asset ids match
input_txs = []
@ -70,8 +61,8 @@ class Transaction(Transaction):
get_transaction(input_txid, include_status=True)
if input_tx is None:
raise TransactionDoesNotExist("input `{}` doesn't exist"
.format(input_txid))
raise InputDoesNotExist("input `{}` doesn't exist"
.format(input_txid))
if status != bigchain.TX_VALID:
raise TransactionNotInValidBlock(
@ -117,8 +108,8 @@ class Transaction(Transaction):
else:
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
raise TypeError('`operation`: `{}` must be either {}.'
.format(self.operation, allowed_operations))
raise ValidationError('`operation`: `{}` must be either {}.'
.format(self.operation, allowed_operations))
if not self.inputs_valid(input_conditions):
raise InvalidSignature('Transaction signature is invalid.')
@ -206,18 +197,8 @@ class Block(object):
raised.
Raises:
OperationError: If a non-federation node signed the Block.
InvalidSignature: If a Block's signature is invalid or if the
block contains a transaction with an invalid signature.
OperationError: if the transaction operation is not supported
TransactionDoesNotExist: if the input of the transaction is not
found
TransactionNotInValidBlock: if the input of the transaction is not
in a valid block
TransactionOwnerError: if the new transaction is using an input it
doesn't own
DoubleSpend: if the transaction is a double spend
InvalidHash: if the hash of the transaction is wrong
ValidationError: If the block or any transaction in the block does
not validate
"""
self._validate_block(bigchain)
@ -233,15 +214,14 @@ class Block(object):
object.
Raises:
OperationError: If a non-federation node signed the Block.
InvalidSignature: If a Block's signature is invalid.
ValidationError: If there is a problem with the block
"""
# Check if the block was created by a federation node
if self.node_pubkey not in bigchain.federation:
raise OperationError('Only federation nodes can create blocks')
raise SybilError('Only federation nodes can create blocks')
if set(self.voters) != bigchain.federation:
raise OperationError('Block voters differs from server keyring')
raise SybilError('Block voters differs from server keyring')
# Check that the signature is valid
if not self.is_signature_valid():
@ -254,17 +234,7 @@ class Block(object):
bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
Raises:
OperationError: if the transaction operation is not supported
TransactionDoesNotExist: if the input of the transaction is not
found
TransactionNotInValidBlock: if the input of the transaction is not
in a valid block
TransactionOwnerError: if the new transaction is using an input it
doesn't own
DoubleSpend: if the transaction is a double spend
InvalidHash: if the hash of the transaction is wrong
InvalidSignature: if the signature of the transaction is wrong
DuplicateTransaction: If the block contains a duplicated TX
ValidationError: If an invalid transaction is found
"""
txids = [tx.id for tx in self.transactions]
if len(txids) != len(set(txids)):
@ -349,10 +319,10 @@ class Block(object):
dict: The Block as a dict.
Raises:
OperationError: If the Block doesn't contain any transactions.
ValueError: If the Block doesn't contain any transactions.
"""
if len(self.transactions) == 0:
raise OperationError('Empty block creation is not allowed')
raise ValueError('Empty block creation is not allowed')
block = {
'timestamp': self.timestamp,

View File

@ -13,8 +13,7 @@ import bigchaindb
from bigchaindb import backend
from bigchaindb.backend.changefeed import ChangeFeed
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import (SchemaValidationError, InvalidHash,
InvalidSignature, AmountError)
from bigchaindb.common.exceptions import ValidationError
from bigchaindb import Bigchain
@ -63,8 +62,7 @@ class BlockPipeline:
"""
try:
tx = Transaction.from_dict(tx)
except (SchemaValidationError, InvalidHash, InvalidSignature,
AmountError):
except ValidationError:
return None
# If transaction is in any VALID or UNDECIDED block we
@ -74,12 +72,14 @@ class BlockPipeline:
return None
# If transaction is not valid it should not be included
if not self.bigchain.is_valid_transaction(tx):
try:
tx.validate(self.bigchain)
return tx
except ValidationError as e:
logger.warning('Invalid tx: %s', e)
self.bigchain.delete_transaction(tx.id)
return None
return tx
def create(self, tx, timeout=False):
"""Create a block.

View File

@ -60,7 +60,7 @@ class Vote:
return block['id'], [self.invalid_dummy_tx]
try:
block._validate_block(self.bigchain)
except (exceptions.OperationError, exceptions.InvalidSignature):
except exceptions.ValidationError:
# XXX: if a block is invalid we should skip the `validate_tx`
# step, but since we are in a pipeline we cannot just jump to
# another function. Hackish solution: generate an invalid
@ -104,7 +104,13 @@ class Vote:
if not new:
return False, block_id, num_tx
valid = bool(self.bigchain.is_valid_transaction(tx))
try:
tx.validate(self.bigchain)
valid = True
except exceptions.ValidationError as e:
logger.warning('Invalid tx: %s', e)
valid = False
return valid, block_id, num_tx
def vote(self, tx_validity, block_id, num_tx):

View File

@ -9,20 +9,7 @@ import logging
from flask import current_app, request
from flask_restful import Resource, reqparse
from bigchaindb.common.exceptions import (
AmountError,
DoubleSpend,
InvalidHash,
InvalidSignature,
SchemaValidationError,
OperationError,
TransactionDoesNotExist,
TransactionOwnerError,
TransactionNotInValidBlock,
ValidationError,
)
from bigchaindb.common.exceptions import SchemaValidationError, ValidationError
from bigchaindb.models import Transaction
from bigchaindb.web.views.base import make_error
from bigchaindb.web.views import parameters
@ -84,7 +71,7 @@ class TransactionListApi(Resource):
message='Invalid transaction schema: {}'.format(
e.__cause__.message)
)
except (ValidationError, InvalidSignature) as e:
except ValidationError as e:
return make_error(
400,
'Invalid transaction ({}): {}'.format(type(e).__name__, e)
@ -93,15 +80,7 @@ class TransactionListApi(Resource):
with pool() as bigchain:
try:
bigchain.validate_transaction(tx_obj)
except (ValueError,
OperationError,
TransactionDoesNotExist,
TransactionOwnerError,
DoubleSpend,
InvalidHash,
InvalidSignature,
TransactionNotInValidBlock,
AmountError) as e:
except ValidationError as e:
return make_error(
400,
'Invalid transaction ({}): {}'.format(type(e).__name__, e)

View File

@ -1,3 +1,4 @@
from bigchaindb.common.exceptions import ValidationError
import pytest
import random
@ -26,7 +27,7 @@ def test_validate_bad_asset_creation(b, user_pk):
tx.asset['data'] = 'a'
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
with pytest.raises(ValidationError):
b.validate_transaction(tx_signed)
@ -108,4 +109,4 @@ def test_create_valid_divisible_asset(b, user_pk, user_sk):
tx = Transaction.create([user_pk], [([user_pk], 2)])
tx_signed = tx.sign([user_sk])
assert b.is_valid_transaction(tx_signed)
tx_signed.validate(b)

View File

@ -3,6 +3,8 @@ from time import sleep
import pytest
from unittest.mock import patch
from bigchaindb.common.exceptions import ValidationError
pytestmark = pytest.mark.bdb
@ -91,7 +93,7 @@ class TestBigchainApi(object):
@pytest.mark.genesis
def test_get_spent_with_double_inclusion_detected(self, b, monkeypatch):
from bigchaindb.backend.exceptions import BigchainDBCritical
from bigchaindb.exceptions import CriticalDoubleInclusion
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([b.me], 1)])
@ -121,12 +123,47 @@ class TestBigchainApi(object):
vote = b.vote(block3.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
with pytest.raises(BigchainDBCritical):
with pytest.raises(CriticalDoubleInclusion):
b.get_spent(tx.id, 0)
@pytest.mark.genesis
def test_get_spent_with_double_spend_detected(self, b, monkeypatch):
from bigchaindb.exceptions import CriticalDoubleSpend
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
monkeypatch.setattr('time.time', lambda: 1000000000)
block1 = b.create_block([tx])
b.write_block(block1)
monkeypatch.setattr('time.time', lambda: 1000000020)
transfer_tx = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
asset_id=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: 1000000030)
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([b.me], 2)],
asset_id=tx.id)
transfer_tx2 = transfer_tx2.sign([b.me_private])
block3 = b.create_block([transfer_tx2])
b.write_block(block3)
# Vote both block2 and block3 valid
vote = b.vote(block2.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
vote = b.vote(block3.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
with pytest.raises(CriticalDoubleSpend):
b.get_spent(tx.id, 0)
@pytest.mark.genesis
def test_get_block_status_for_tx_with_double_inclusion(self, b, monkeypatch):
from bigchaindb.backend.exceptions import BigchainDBCritical
from bigchaindb.exceptions import CriticalDoubleInclusion
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([b.me], 1)])
@ -146,7 +183,7 @@ class TestBigchainApi(object):
vote = b.vote(block2.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
with pytest.raises(BigchainDBCritical):
with pytest.raises(CriticalDoubleInclusion):
b.get_blocks_status_containing_tx(tx.id)
@pytest.mark.genesis
@ -478,7 +515,7 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures('inputs')
def test_non_create_input_not_found(self, b, user_pk):
from cryptoconditions import Ed25519Fulfillment
from bigchaindb.common.exceptions import TransactionDoesNotExist
from bigchaindb.common.exceptions import InputDoesNotExist
from bigchaindb.common.transaction import Input, TransactionLink
from bigchaindb.models import Transaction
from bigchaindb import Bigchain
@ -490,7 +527,7 @@ class TestBigchainApi(object):
tx = Transaction.transfer([input], [([user_pk], 1)],
asset_id='mock_asset_link')
with pytest.raises(TransactionDoesNotExist):
with pytest.raises(InputDoesNotExist):
tx.validate(Bigchain())
def test_count_backlog(self, b, user_pk):
@ -513,24 +550,24 @@ class TestTransactionValidation(object):
# 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(ValueError) as excinfo:
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(ValueError) as excinfo:
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):
from bigchaindb.common.exceptions import TransactionDoesNotExist
from bigchaindb.common.exceptions import InputDoesNotExist
from bigchaindb.common.transaction import TransactionLink
signed_transfer_tx.inputs[0].fulfills = TransactionLink('c', 0)
with pytest.raises(TransactionDoesNotExist):
with pytest.raises(InputDoesNotExist):
b.validate_transaction(signed_transfer_tx)
@pytest.mark.usefixtures('inputs')
@ -689,7 +726,7 @@ class TestBlockValidation(object):
b.validate_block(block)
def test_invalid_node_pubkey(self, b):
from bigchaindb.common.exceptions import OperationError
from bigchaindb.common.exceptions import SybilError
from bigchaindb.common import crypto
# blocks can only be created by a federation node
@ -706,8 +743,8 @@ class TestBlockValidation(object):
# from a non federation node
block = block.sign(tmp_sk)
# check that validate_block raises an OperationError
with pytest.raises(OperationError):
# check that validate_block raises an SybilError
with pytest.raises(SybilError):
b.validate_block(block)
@ -726,7 +763,7 @@ class TestMultipleInputs(object):
tx = tx.sign([user_sk])
# validate transaction
assert b.is_valid_transaction(tx) == tx
tx.validate(b)
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@ -748,7 +785,7 @@ class TestMultipleInputs(object):
asset_id=input_tx.id)
tx = tx.sign([user_sk])
assert b.is_valid_transaction(tx) == tx
tx.validate(b)
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@ -780,7 +817,7 @@ class TestMultipleInputs(object):
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
# validate transaction
assert b.is_valid_transaction(transfer_tx) == transfer_tx
transfer_tx.validate(b)
assert len(transfer_tx.inputs) == 1
assert len(transfer_tx.outputs) == 1
@ -813,7 +850,7 @@ class TestMultipleInputs(object):
asset_id=tx_input.id)
tx = tx.sign([user_sk, user2_sk])
assert b.is_valid_transaction(tx) == tx
tx.validate(b)
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@ -1167,7 +1204,6 @@ def test_cant_spend_same_input_twice_in_tx(b, genesis_block):
tx_transfer = Transaction.transfer(dup_inputs, [([b.me], 200)],
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([b.me_private])
assert b.is_valid_transaction(tx_transfer_signed) is False
with pytest.raises(DoubleSpend):
tx_transfer_signed.validate(b)
@ -1225,3 +1261,10 @@ def test_is_new_transaction(b, genesis_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, 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)

View File

@ -46,28 +46,19 @@ def test_validate_transaction_handles_exceptions(b, signed_create_tx):
"""
from bigchaindb.pipelines.block import BlockPipeline
block_maker = BlockPipeline()
from bigchaindb.common.exceptions import ValidationError
# Test SchemaValidationError
tx_dict = signed_create_tx.to_dict()
tx_dict['invalid_key'] = 'schema validation gonna getcha!'
assert block_maker.validate_tx(tx_dict) is None
# Test InvalidHash
tx_dict = signed_create_tx.to_dict()
tx_dict['id'] = 'a' * 64
assert block_maker.validate_tx(tx_dict) is None
with patch('bigchaindb.models.Transaction.validate') as validate:
# Assert that validationerror gets caught
validate.side_effect = ValidationError()
assert block_maker.validate_tx(tx_dict) is None
# Test InvalidSignature when we pass a bad fulfillment
tx_dict = signed_create_tx.to_dict()
tx_dict['inputs'][0]['fulfillment'] = 'cf:0:aaaaaaaaaaaaaaaaaaaaaaaaa'
assert block_maker.validate_tx(tx_dict) is None
# Test AmountError
signed_create_tx.outputs[0].amount = 0
tx_dict = signed_create_tx.to_dict()
# set the correct value back so that we can continue using it
signed_create_tx.outputs[0].amount = 1
assert block_maker.validate_tx(tx_dict) is None
# Assert that another error doesnt
validate.side_effect = IOError()
with pytest.raises(IOError):
block_maker.validate_tx(tx_dict)
def test_create_block(b, user_pk):

View File

@ -36,7 +36,11 @@ def test_reassign_transactions(b, user_pk):
stm = stale.StaleTransactionMonitor(timeout=0.001,
backlog_reassign_delay=0.001)
stm.reassign_transactions(tx.to_dict())
# This worked previously because transaction['assignee'] was only used if
# bigchain.nodes_except_me was not empty.
tx_dict = tx.to_dict()
tx_dict['assignee'] = b.me
stm.reassign_transactions(tx_dict)
# test with federation
tx = Transaction.create([b.me], [([user_pk], 1)])
@ -58,7 +62,7 @@ def test_reassign_transactions(b, user_pk):
tx = tx.sign([b.me_private])
stm.bigchain.nodes_except_me = ['lol']
b.write_transaction(tx)
stm.bigchain.nodes_except_me = None
stm.bigchain.nodes_except_me = []
tx = list(query.get_stale_transactions(b.connection, 0))[0]
stm.reassign_transactions(tx)

View File

@ -128,17 +128,23 @@ def test_validate_block_with_invalid_signature(b):
@pytest.mark.genesis
def test_vote_validate_transaction(b):
from bigchaindb.pipelines import vote
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import ValidationError
tx = dummy_tx(b)
vote_obj = vote.Vote()
validation = vote_obj.validate_tx(tx, 123, 1)
assert validation == (True, 123, 1)
# NOTE: Submit unsigned transaction to `validate_tx` yields `False`.
tx = Transaction.create([b.me], [([b.me], 1)])
validation = vote_obj.validate_tx(tx, 456, 10)
assert validation == (False, 456, 10)
with patch('bigchaindb.models.Transaction.validate') as validate:
# Assert that validationerror gets caught
validate.side_effect = ValidationError()
validation = vote_obj.validate_tx(tx, 456, 10)
assert validation == (False, 456, 10)
# Assert that another error doesnt
validate.side_effect = IOError()
with pytest.raises(IOError):
validation = vote_obj.validate_tx(tx, 456, 10)
@pytest.mark.genesis

View File

@ -80,13 +80,3 @@ def test_get_blocks_status_containing_tx(monkeypatch):
bigchain = Bigchain(public_key='pubkey', private_key='privkey')
with pytest.raises(Exception):
bigchain.get_blocks_status_containing_tx('txid')
def test_has_previous_vote(monkeypatch):
from bigchaindb.core import Bigchain
monkeypatch.setattr(
'bigchaindb.utils.verify_vote_signature', lambda voters, vote: False)
bigchain = Bigchain(public_key='pubkey', private_key='privkey')
block = {'votes': ({'node_pubkey': 'pubkey'},)}
with pytest.raises(Exception):
bigchain.has_previous_vote(block)

View File

@ -1,4 +1,5 @@
from pytest import raises
from bigchaindb.common.exceptions import ValidationError
class TestTransactionModel(object):
@ -8,12 +9,12 @@ class TestTransactionModel(object):
tx = Transaction.create([b.me], [([b.me], 1)])
tx.operation = 'something invalid'
with raises(TypeError):
with raises(ValidationError):
tx.validate(b)
tx.operation = 'CREATE'
tx.inputs = []
with raises(ValueError):
with raises(ValidationError):
tx.validate(b)
@ -61,11 +62,10 @@ class TestBlockModel(object):
assert block.to_dict() == expected
def test_block_invalid_serializaton(self):
from bigchaindb.common.exceptions import OperationError
from bigchaindb.models import Block
block = Block([])
with raises(OperationError):
with raises(ValueError):
block.to_dict()
def test_block_deserialization(self, b):

View File

@ -1,4 +1,3 @@
import builtins
import json
from unittest.mock import patch
@ -113,18 +112,15 @@ def test_post_create_transaction_with_invalid_schema(client, caplog):
('DoubleSpend', 'Nope! It is gone now!'),
('InvalidHash', 'Do not smoke that!'),
('InvalidSignature', 'Falsche Unterschrift!'),
('OperationError', 'Create and transfer!'),
('TransactionDoesNotExist', 'Hallucinations?'),
('ValidationError', 'Create and transfer!'),
('InputDoesNotExist', 'Hallucinations?'),
('TransactionOwnerError', 'Not yours!'),
('TransactionNotInValidBlock', 'Wait, maybe?'),
('ValueError', '?'),
('ValidationError', '?'),
))
def test_post_invalid_transaction(client, exc, msg, monkeypatch, caplog):
from bigchaindb.common import exceptions
try:
exc_cls = getattr(exceptions, exc)
except AttributeError:
exc_cls = getattr(builtins, 'ValueError')
exc_cls = getattr(exceptions, exc)
def mock_validation(self_, tx):
raise exc_cls(msg)