Merge remote-tracking branch 'origin/master' into add-bft-section-to-docs

This commit is contained in:
troymc 2016-06-29 09:20:19 +02:00
commit f23db12ad1
10 changed files with 175 additions and 47 deletions

View File

@ -1,9 +1,6 @@
import copy
from abc import ABCMeta, abstractmethod
import bigchaindb.exceptions as exceptions
from bigchaindb import util
from bigchaindb import crypto
from bigchaindb import crypto, exceptions, util
class AbstractConsensusRules(metaclass=ABCMeta):
@ -101,7 +98,7 @@ class AbstractConsensusRules(metaclass=ABCMeta):
bool: True if the votes's required signature data is present
and correct, False otherwise.
"""
raise NotImplementedError
class BaseConsensusRules(AbstractConsensusRules):
"""Base consensus rules for Bigchain.

View File

@ -6,18 +6,7 @@ import rethinkdb as r
import rapidjson
import bigchaindb
from bigchaindb import util
from bigchaindb import config_utils
from bigchaindb import exceptions
from bigchaindb import crypto
class GenesisBlockAlreadyExistsError(Exception):
pass
class ImproperVoteError(Exception):
pass
from bigchaindb import config_utils, crypto, exceptions, util
class Bigchain(object):
@ -446,17 +435,22 @@ class Bigchain(object):
block (dict): block to check.
Returns:
True if this block already has a valid vote from this node, False otherwise. If
there is already a vote, but the vote is invalid, raises an ImproperVoteError
bool: :const:`True` if this block already has a
valid vote from this node, :const:`False` otherwise.
Raises:
ImproperVoteError: If there is already a vote,
but the vote is invalid.
"""
if block['votes']:
for vote in block['votes']:
if vote['node_pubkey'] == self.me:
if util.verify_vote_signature(block, vote):
return True
else:
raise ImproperVoteError('Block {block_id} already has an incorrectly signed vote '
'from public key {me}').format(block_id=block['id'], me=self.me)
if not util.verify_vote_signature(block, vote):
raise exceptions.ImproperVoteError(
'Block {} already has an incorrectly signed vote from public key {}'
).format(block['id'], self.me)
return True
return False
def is_valid_block(self, block):
@ -509,7 +503,7 @@ class Bigchain(object):
blocks_count = r.table('bigchain').count().run(self.conn)
if blocks_count:
raise GenesisBlockAlreadyExistsError('Cannot create the Genesis block')
raise exceptions.GenesisBlockAlreadyExistsError('Cannot create the Genesis block')
payload = {'message': 'Hello World from the BigchainDB'}
transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload)

View File

@ -1,36 +1,54 @@
"""Custom exceptions used in the `bigchaindb` package.
"""
class OperationError(Exception):
"""Raised when an operation cannot go through"""
class TransactionDoesNotExist(Exception):
"""Raised if the transaction is not in the database"""
class TransactionOwnerError(Exception):
"""Raised if a user tries to transfer a transaction they don't own"""
class DoubleSpend(Exception):
"""Raised if a double spend is found"""
class InvalidHash(Exception):
"""Raised if there was an error checking the hash for a particular operation"""
class InvalidSignature(Exception):
"""Raised if there was an error checking the signature for a particular operation"""
class DatabaseAlreadyExists(Exception):
"""Raised when trying to create the database but the db is already there"""
class DatabaseDoesNotExist(Exception):
"""Raised when trying to delete the database but the db is not there"""
class KeypairNotFoundException(Exception):
"""Raised if operation cannot proceed because the keypair was not given"""
class KeypairMismatchException(Exception):
"""Raised if the private key(s) provided for signing don't match any of the curret owner(s)"""
class StartupError(Exception):
"""Raised when there is an error starting up the system"""
class ImproperVoteError(Exception):
"""Raised when an invalid vote is found"""
class GenesisBlockAlreadyExistsError(Exception):
"""Raised when trying to create the already existing genesis block"""

View File

@ -220,15 +220,14 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
inputs = [inputs]
# handle payload
data = None
if isinstance(payload, (dict, type(None))):
data = {
'uuid': str(uuid.uuid4()),
'payload': payload
}
else:
if payload is not None and not isinstance(payload, dict):
raise TypeError('`payload` must be an dict instance or None')
data = {
'uuid': str(uuid.uuid4()),
'payload': payload
}
# handle inputs
fulfillments = []
@ -397,13 +396,15 @@ def fulfill_threshold_signature_fulfillment(fulfillment, parsed_fulfillment, ful
try:
subfulfillment = parsed_fulfillment_copy.get_subcondition_from_vk(current_owner)[0]
except IndexError:
exceptions.KeypairMismatchException('Public key {} cannot be found in the fulfillment'
.format(current_owner))
raise exceptions.KeypairMismatchException(
'Public key {} cannot be found in the fulfillment'.format(current_owner))
try:
subfulfillment.sign(serialize(fulfillment_message), key_pairs[current_owner])
private_key = key_pairs[current_owner]
except KeyError:
raise exceptions.KeypairMismatchException('Public key {} is not a pair to any of the private keys'
.format(current_owner))
raise exceptions.KeypairMismatchException(
'Public key {} is not a pair to any of the private keys'.format(current_owner))
subfulfillment.sign(serialize(fulfillment_message), private_key)
parsed_fulfillment.add_subfulfillment(subfulfillment)
return parsed_fulfillment

View File

@ -92,7 +92,7 @@ setup(
]
},
install_requires=[
'rethinkdb==2.3.0',
'rethinkdb~=2.3',
'pysha3==0.3',
'pytz==2015.7',
'cryptoconditions==0.4.1',

View File

@ -9,7 +9,6 @@ Tasks:
import pytest
import rethinkdb as r
import bigchaindb
from bigchaindb import Bigchain
from bigchaindb.db import get_conn
@ -73,6 +72,7 @@ def setup_database(request, node_config):
@pytest.fixture(scope='function', autouse=True)
def cleanup_tables(request, node_config):
db_name = node_config['database']['name']
def fin():
get_conn().repl()
try:
@ -87,11 +87,12 @@ def cleanup_tables(request, node_config):
@pytest.fixture
def inputs(user_vk, amount=1, b=None):
from bigchaindb.exceptions import GenesisBlockAlreadyExistsError
# 1. create the genesis block
b = b or Bigchain()
try:
b.create_genesis_block()
except bigchaindb.core.GenesisBlockAlreadyExistsError:
except GenesisBlockAlreadyExistsError:
pass
# 2. create block with transactions for `USER` to spend
@ -105,4 +106,3 @@ def inputs(user_vk, amount=1, b=None):
block = b.create_block(transactions)
b.write_block(block, durability='hard')
return block

View File

@ -8,9 +8,7 @@ import rethinkdb as r
import cryptoconditions as cc
import bigchaindb
from bigchaindb import util
from bigchaindb import exceptions
from bigchaindb import crypto
from bigchaindb import crypto, exceptions, util
from bigchaindb.voter import Voter
from bigchaindb.block import Block, BlockDeleteRevert
@ -178,7 +176,7 @@ class TestBigchainApi(object):
def test_create_genesis_block_fails_if_table_not_empty(self, b):
b.create_genesis_block()
with pytest.raises(bigchaindb.core.GenesisBlockAlreadyExistsError):
with pytest.raises(exceptions.GenesisBlockAlreadyExistsError):
b.create_genesis_block()
genesis_blocks = list(r.table('bigchain')

15
tests/test_consensus.py Normal file
View File

@ -0,0 +1,15 @@
import pytest
class TestBaseConsensusRules(object):
def test_validate_transaction(self):
from bigchaindb.consensus import BaseConsensusRules
transaction = {
'transaction': {
'operation': None,
'fulfillments': None,
},
}
with pytest.raises(ValueError):
BaseConsensusRules.validate_transaction(None, transaction)

View File

@ -1,3 +1,7 @@
from collections import namedtuple
from rethinkdb.ast import RqlQuery
import pytest
@ -58,3 +62,36 @@ def test_bigchain_class_initialization_with_parameters(config):
assert bigchain.nodes_except_me == init_kwargs['keyring']
assert bigchain.consensus == BaseConsensusRules
assert bigchain._conn is None
def test_get_blocks_status_containing_tx(monkeypatch):
from bigchaindb.core import Bigchain
blocks = [
{'id': 1}, {'id': 2}
]
monkeypatch.setattr(
Bigchain, 'search_block_election_on_index', lambda x, y, z: blocks)
monkeypatch.setattr(
Bigchain, 'block_election_status', lambda x, y: Bigchain.BLOCK_VALID)
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.util.verify_vote_signature', lambda block, vote: False)
bigchain = Bigchain(public_key='pubkey', private_key='privkey')
block = {'votes': ({'node_pubkey': 'pubkey'},)}
with pytest.raises(Exception):
bigchain.has_previous_vote(block)
@pytest.mark.parametrize('items,exists', (((0,), True), ((), False)))
def test_transaction_exists(monkeypatch, items, exists):
from bigchaindb.core import Bigchain
monkeypatch.setattr(
RqlQuery, 'run', lambda x, y: namedtuple('response', 'items')(items))
bigchain = Bigchain(public_key='pubkey', private_key='privkey')
assert bigchain.transaction_exists('txid') is exists

View File

@ -1,6 +1,9 @@
from unittest.mock import patch, call
import pytest
import queue
from unittest.mock import patch, call
import pytest
from cryptoconditions import ThresholdSha256Fulfillment
@pytest.fixture
@ -142,3 +145,68 @@ def test_process_group_instantiates_and_start_processes(mock_process):
for process in pg.processes:
process.start.assert_called_with()
def test_create_tx_with_empty_inputs():
from bigchaindb.util import create_tx
tx = create_tx(None, None, [], None)
assert 'id' in tx
assert 'transaction' in tx
assert 'version' in tx
assert 'fulfillments' in tx['transaction']
assert 'conditions' in tx['transaction']
assert 'operation' in tx['transaction']
assert 'timestamp' in tx['transaction']
assert 'data' in tx['transaction']
assert len(tx['transaction']['fulfillments']) == 1
assert tx['transaction']['fulfillments'][0] == {
'current_owners': [], 'input': None, 'fulfillment': None, 'fid': 0}
def test_fulfill_threshold_signature_fulfillment_pubkey_notfound(monkeypatch):
from bigchaindb.exceptions import KeypairMismatchException
from bigchaindb.util import fulfill_threshold_signature_fulfillment
monkeypatch.setattr(
ThresholdSha256Fulfillment,
'get_subcondition_from_vk',
lambda x, y: []
)
fulfillment = {'current_owners': (None,)}
parsed_fulfillment = ThresholdSha256Fulfillment()
with pytest.raises(KeypairMismatchException):
fulfill_threshold_signature_fulfillment(
fulfillment, parsed_fulfillment, None, None)
def test_fulfill_threshold_signature_fulfillment_wrong_privkeys(monkeypatch):
from bigchaindb.exceptions import KeypairMismatchException
from bigchaindb.util import fulfill_threshold_signature_fulfillment
monkeypatch.setattr(
ThresholdSha256Fulfillment,
'get_subcondition_from_vk',
lambda x, y: (None,)
)
fulfillment = {'current_owners': ('alice-pub-key',)}
parsed_fulfillment = ThresholdSha256Fulfillment()
with pytest.raises(KeypairMismatchException):
fulfill_threshold_signature_fulfillment(
fulfillment, parsed_fulfillment, None, {})
def test_check_hash_and_signature_invalid_hash(monkeypatch):
from bigchaindb.exceptions import InvalidHash
from bigchaindb.util import check_hash_and_signature
transaction = {'id': 'txid'}
monkeypatch.setattr('bigchaindb.util.get_hash_data', lambda tx: 'txhash')
with pytest.raises(InvalidHash):
check_hash_and_signature(transaction)
def test_check_hash_and_signature_invalid_signature(monkeypatch):
from bigchaindb.exceptions import InvalidSignature
from bigchaindb.util import check_hash_and_signature
transaction = {'id': 'txid'}
monkeypatch.setattr('bigchaindb.util.get_hash_data', lambda tx: 'txid')
monkeypatch.setattr(
'bigchaindb.util.validate_fulfillments', lambda tx: False)
with pytest.raises(InvalidSignature):
check_hash_and_signature(transaction)