mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge remote-tracking branch 'origin/master' into add-bft-section-to-docs
This commit is contained in:
commit
f23db12ad1
@ -1,9 +1,6 @@
|
|||||||
import copy
|
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import bigchaindb.exceptions as exceptions
|
from bigchaindb import crypto, exceptions, util
|
||||||
from bigchaindb import util
|
|
||||||
from bigchaindb import crypto
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractConsensusRules(metaclass=ABCMeta):
|
class AbstractConsensusRules(metaclass=ABCMeta):
|
||||||
@ -101,7 +98,7 @@ class AbstractConsensusRules(metaclass=ABCMeta):
|
|||||||
bool: True if the votes's required signature data is present
|
bool: True if the votes's required signature data is present
|
||||||
and correct, False otherwise.
|
and correct, False otherwise.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
class BaseConsensusRules(AbstractConsensusRules):
|
class BaseConsensusRules(AbstractConsensusRules):
|
||||||
"""Base consensus rules for Bigchain.
|
"""Base consensus rules for Bigchain.
|
||||||
|
@ -6,18 +6,7 @@ import rethinkdb as r
|
|||||||
import rapidjson
|
import rapidjson
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb import util
|
from bigchaindb import config_utils, crypto, exceptions, util
|
||||||
from bigchaindb import config_utils
|
|
||||||
from bigchaindb import exceptions
|
|
||||||
from bigchaindb import crypto
|
|
||||||
|
|
||||||
|
|
||||||
class GenesisBlockAlreadyExistsError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ImproperVoteError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Bigchain(object):
|
class Bigchain(object):
|
||||||
@ -446,17 +435,22 @@ class Bigchain(object):
|
|||||||
block (dict): block to check.
|
block (dict): block to check.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if this block already has a valid vote from this node, False otherwise. If
|
bool: :const:`True` if this block already has a
|
||||||
there is already a vote, but the vote is invalid, raises an ImproperVoteError
|
valid vote from this node, :const:`False` otherwise.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ImproperVoteError: If there is already a vote,
|
||||||
|
but the vote is invalid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if block['votes']:
|
if block['votes']:
|
||||||
for vote in block['votes']:
|
for vote in block['votes']:
|
||||||
if vote['node_pubkey'] == self.me:
|
if vote['node_pubkey'] == self.me:
|
||||||
if util.verify_vote_signature(block, vote):
|
if not util.verify_vote_signature(block, vote):
|
||||||
return True
|
raise exceptions.ImproperVoteError(
|
||||||
else:
|
'Block {} already has an incorrectly signed vote from public key {}'
|
||||||
raise ImproperVoteError('Block {block_id} already has an incorrectly signed vote '
|
).format(block['id'], self.me)
|
||||||
'from public key {me}').format(block_id=block['id'], me=self.me)
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_valid_block(self, block):
|
def is_valid_block(self, block):
|
||||||
@ -509,7 +503,7 @@ class Bigchain(object):
|
|||||||
blocks_count = r.table('bigchain').count().run(self.conn)
|
blocks_count = r.table('bigchain').count().run(self.conn)
|
||||||
|
|
||||||
if blocks_count:
|
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'}
|
payload = {'message': 'Hello World from the BigchainDB'}
|
||||||
transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload)
|
transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload)
|
||||||
|
@ -1,36 +1,54 @@
|
|||||||
"""Custom exceptions used in the `bigchaindb` package.
|
"""Custom exceptions used in the `bigchaindb` package.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class OperationError(Exception):
|
class OperationError(Exception):
|
||||||
"""Raised when an operation cannot go through"""
|
"""Raised when an operation cannot go through"""
|
||||||
|
|
||||||
|
|
||||||
class TransactionDoesNotExist(Exception):
|
class TransactionDoesNotExist(Exception):
|
||||||
"""Raised if the transaction is not in the database"""
|
"""Raised if the transaction is not in the database"""
|
||||||
|
|
||||||
|
|
||||||
class TransactionOwnerError(Exception):
|
class TransactionOwnerError(Exception):
|
||||||
"""Raised if a user tries to transfer a transaction they don't own"""
|
"""Raised if a user tries to transfer a transaction they don't own"""
|
||||||
|
|
||||||
|
|
||||||
class DoubleSpend(Exception):
|
class DoubleSpend(Exception):
|
||||||
"""Raised if a double spend is found"""
|
"""Raised if a double spend is found"""
|
||||||
|
|
||||||
|
|
||||||
class InvalidHash(Exception):
|
class InvalidHash(Exception):
|
||||||
"""Raised if there was an error checking the hash for a particular operation"""
|
"""Raised if there was an error checking the hash for a particular operation"""
|
||||||
|
|
||||||
|
|
||||||
class InvalidSignature(Exception):
|
class InvalidSignature(Exception):
|
||||||
"""Raised if there was an error checking the signature for a particular operation"""
|
"""Raised if there was an error checking the signature for a particular operation"""
|
||||||
|
|
||||||
|
|
||||||
class DatabaseAlreadyExists(Exception):
|
class DatabaseAlreadyExists(Exception):
|
||||||
"""Raised when trying to create the database but the db is already there"""
|
"""Raised when trying to create the database but the db is already there"""
|
||||||
|
|
||||||
|
|
||||||
class DatabaseDoesNotExist(Exception):
|
class DatabaseDoesNotExist(Exception):
|
||||||
"""Raised when trying to delete the database but the db is not there"""
|
"""Raised when trying to delete the database but the db is not there"""
|
||||||
|
|
||||||
|
|
||||||
class KeypairNotFoundException(Exception):
|
class KeypairNotFoundException(Exception):
|
||||||
"""Raised if operation cannot proceed because the keypair was not given"""
|
"""Raised if operation cannot proceed because the keypair was not given"""
|
||||||
|
|
||||||
|
|
||||||
class KeypairMismatchException(Exception):
|
class KeypairMismatchException(Exception):
|
||||||
"""Raised if the private key(s) provided for signing don't match any of the curret owner(s)"""
|
"""Raised if the private key(s) provided for signing don't match any of the curret owner(s)"""
|
||||||
|
|
||||||
|
|
||||||
class StartupError(Exception):
|
class StartupError(Exception):
|
||||||
"""Raised when there is an error starting up the system"""
|
"""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"""
|
||||||
|
@ -220,15 +220,14 @@ def create_tx(current_owners, new_owners, inputs, operation, payload=None):
|
|||||||
inputs = [inputs]
|
inputs = [inputs]
|
||||||
|
|
||||||
# handle payload
|
# handle payload
|
||||||
data = None
|
if payload is not None and not isinstance(payload, dict):
|
||||||
if isinstance(payload, (dict, type(None))):
|
|
||||||
data = {
|
|
||||||
'uuid': str(uuid.uuid4()),
|
|
||||||
'payload': payload
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
raise TypeError('`payload` must be an dict instance or None')
|
raise TypeError('`payload` must be an dict instance or None')
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'uuid': str(uuid.uuid4()),
|
||||||
|
'payload': payload
|
||||||
|
}
|
||||||
|
|
||||||
# handle inputs
|
# handle inputs
|
||||||
fulfillments = []
|
fulfillments = []
|
||||||
|
|
||||||
@ -397,13 +396,15 @@ def fulfill_threshold_signature_fulfillment(fulfillment, parsed_fulfillment, ful
|
|||||||
try:
|
try:
|
||||||
subfulfillment = parsed_fulfillment_copy.get_subcondition_from_vk(current_owner)[0]
|
subfulfillment = parsed_fulfillment_copy.get_subcondition_from_vk(current_owner)[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
exceptions.KeypairMismatchException('Public key {} cannot be found in the fulfillment'
|
raise exceptions.KeypairMismatchException(
|
||||||
.format(current_owner))
|
'Public key {} cannot be found in the fulfillment'.format(current_owner))
|
||||||
try:
|
try:
|
||||||
subfulfillment.sign(serialize(fulfillment_message), key_pairs[current_owner])
|
private_key = key_pairs[current_owner]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exceptions.KeypairMismatchException('Public key {} is not a pair to any of the private keys'
|
raise exceptions.KeypairMismatchException(
|
||||||
.format(current_owner))
|
'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)
|
parsed_fulfillment.add_subfulfillment(subfulfillment)
|
||||||
|
|
||||||
return parsed_fulfillment
|
return parsed_fulfillment
|
||||||
|
2
setup.py
2
setup.py
@ -92,7 +92,7 @@ setup(
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'rethinkdb==2.3.0',
|
'rethinkdb~=2.3',
|
||||||
'pysha3==0.3',
|
'pysha3==0.3',
|
||||||
'pytz==2015.7',
|
'pytz==2015.7',
|
||||||
'cryptoconditions==0.4.1',
|
'cryptoconditions==0.4.1',
|
||||||
|
@ -9,7 +9,6 @@ Tasks:
|
|||||||
import pytest
|
import pytest
|
||||||
import rethinkdb as r
|
import rethinkdb as r
|
||||||
|
|
||||||
import bigchaindb
|
|
||||||
from bigchaindb import Bigchain
|
from bigchaindb import Bigchain
|
||||||
from bigchaindb.db import get_conn
|
from bigchaindb.db import get_conn
|
||||||
|
|
||||||
@ -73,6 +72,7 @@ def setup_database(request, node_config):
|
|||||||
@pytest.fixture(scope='function', autouse=True)
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
def cleanup_tables(request, node_config):
|
def cleanup_tables(request, node_config):
|
||||||
db_name = node_config['database']['name']
|
db_name = node_config['database']['name']
|
||||||
|
|
||||||
def fin():
|
def fin():
|
||||||
get_conn().repl()
|
get_conn().repl()
|
||||||
try:
|
try:
|
||||||
@ -87,11 +87,12 @@ def cleanup_tables(request, node_config):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def inputs(user_vk, amount=1, b=None):
|
def inputs(user_vk, amount=1, b=None):
|
||||||
|
from bigchaindb.exceptions import GenesisBlockAlreadyExistsError
|
||||||
# 1. create the genesis block
|
# 1. create the genesis block
|
||||||
b = b or Bigchain()
|
b = b or Bigchain()
|
||||||
try:
|
try:
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
except bigchaindb.core.GenesisBlockAlreadyExistsError:
|
except GenesisBlockAlreadyExistsError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 2. create block with transactions for `USER` to spend
|
# 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)
|
block = b.create_block(transactions)
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
return block
|
return block
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@ import rethinkdb as r
|
|||||||
import cryptoconditions as cc
|
import cryptoconditions as cc
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb import util
|
from bigchaindb import crypto, exceptions, util
|
||||||
from bigchaindb import exceptions
|
|
||||||
from bigchaindb import crypto
|
|
||||||
from bigchaindb.voter import Voter
|
from bigchaindb.voter import Voter
|
||||||
from bigchaindb.block import Block, BlockDeleteRevert
|
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):
|
def test_create_genesis_block_fails_if_table_not_empty(self, b):
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
with pytest.raises(bigchaindb.core.GenesisBlockAlreadyExistsError):
|
with pytest.raises(exceptions.GenesisBlockAlreadyExistsError):
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
genesis_blocks = list(r.table('bigchain')
|
genesis_blocks = list(r.table('bigchain')
|
||||||
|
15
tests/test_consensus.py
Normal file
15
tests/test_consensus.py
Normal 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)
|
@ -1,3 +1,7 @@
|
|||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
from rethinkdb.ast import RqlQuery
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@ -58,3 +62,36 @@ def test_bigchain_class_initialization_with_parameters(config):
|
|||||||
assert bigchain.nodes_except_me == init_kwargs['keyring']
|
assert bigchain.nodes_except_me == init_kwargs['keyring']
|
||||||
assert bigchain.consensus == BaseConsensusRules
|
assert bigchain.consensus == BaseConsensusRules
|
||||||
assert bigchain._conn is None
|
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
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
from unittest.mock import patch, call
|
|
||||||
import pytest
|
|
||||||
import queue
|
import queue
|
||||||
|
from unittest.mock import patch, call
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cryptoconditions import ThresholdSha256Fulfillment
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -142,3 +145,68 @@ def test_process_group_instantiates_and_start_processes(mock_process):
|
|||||||
for process in pg.processes:
|
for process in pg.processes:
|
||||||
process.start.assert_called_with()
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user