bigchaindb/tests/integration/test_federation.py
Katha e377fb57af
Pre commit styling (#1914)
* run pre-commit and fix errors

* mention pre-commit in our guides
2017-11-30 15:04:14 +01:00

185 lines
5.3 KiB
Python

from copy import deepcopy
import pytest
import random
import bigchaindb
from bigchaindb.core import Bigchain
from contextlib import contextmanager
from bigchaindb.common.crypto import generate_key_pair
from tests.pipelines.stepping import create_stepper
################################################################################
# Test setup code
@contextmanager
def federation(n):
"""Return a list of Bigchain objects and pipeline steppers to represent
a BigchainDB federation
"""
keys = [generate_key_pair() for _ in range(n)]
config_orig = bigchaindb.config
@contextmanager
def make_nodes(i):
"""make_nodes is a recursive context manager. Essentially it is doing:
with f(a[0]) as b0:
with f(a[1]) as b1:
with f(a[2]) as b2:
yield [b0, b1, b2]
with an arbitrary depth. It is also temporarily patching global
configuration to simulate nodes with separate identities.
"""
nonlocal keys
if i == 0:
yield []
else:
config = deepcopy(config_orig)
keys = [keys[-1]] + keys[:-1] # Rotate keys
config['keyring'] = [pub for _, pub in keys[1:]]
config['keypair']['private'] = keys[0][0]
config['keypair']['public'] = keys[0][1]
bigchaindb.config = config
stepper = create_stepper()
with stepper.start():
node = (Bigchain(), stepper)
with make_nodes(i-1) as rest:
yield [node] + rest
with make_nodes(n) as steppers:
bigchaindb.config = config_orig
yield zip(*steppers)
@pytest.fixture
def federation_3():
with federation(3) as f:
yield f
def process_tx(steps):
steps.block_changefeed(timeout=1)
if steps.block_filter_tx():
steps.block_validate_tx()
steps.block_create(timeout=True)
steps.block_write()
steps.block_delete_tx()
def input_single_create(b):
from bigchaindb.common.transaction import Transaction
metadata = {'r': random.random()}
tx = Transaction.create([b.me], [([b.me], 1)], metadata).sign([b.me_private])
b.write_transaction(tx)
return tx
def process_vote(steps, result=None):
steps.vote_changefeed()
steps.vote_validate_block()
steps.vote_ungroup()
steps.vote_validate_tx()
if result is not None:
steps.queues['vote_vote'][0][0] = result
vote = steps.vote_vote()
steps.vote_write_vote()
return vote
################################################################################
# Tests here on down
@pytest.mark.bdb
@pytest.mark.genesis
@pytest.mark.skip_travis_rdb
def test_elect_valid(federation_3):
[bx, (s0, s1, s2)] = federation_3
tx = input_single_create(bx[0])
process_tx(s0)
process_tx(s1)
process_tx(s2)
process_vote(s2, False)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
process_vote(s0, True)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
process_vote(s1, True)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'valid'
@pytest.mark.bdb
@pytest.mark.genesis
@pytest.mark.skip_travis_rdb
def test_elect_invalid(federation_3):
[bx, (s0, s1, s2)] = federation_3
tx = input_single_create(bx[0])
process_tx(s0)
process_tx(s1)
process_tx(s2)
process_vote(s1, True)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
process_vote(s2, False)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
process_vote(s0, False)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] is None
@pytest.mark.bdb
@pytest.mark.genesis
@pytest.mark.skip_travis_rdb
def test_elect_sybill(federation_3):
[bx, (s0, s1, s2)] = federation_3
tx = input_single_create(bx[0])
process_tx(s0)
process_tx(s1)
process_tx(s2)
# What we need is some votes from unknown nodes!
# Incorrectly signed votes are ineligible.
for s in [s0, s1, s2]:
s.vote.bigchain.me_private = generate_key_pair()[0]
process_vote(s0, True)
process_vote(s1, True)
process_vote(s2, True)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
@pytest.mark.skip()
@pytest.mark.bdb
@pytest.mark.genesis
def test_elect_dos(federation_3):
"""https://github.com/bigchaindb/bigchaindb/issues/1314
Test that a node cannot block another node's opportunity to vote
on a block by writing an incorrectly signed vote
"""
raise NotImplementedError()
@pytest.mark.skip('Revisit when we have block election status cache')
@pytest.mark.bdb
@pytest.mark.genesis
def test_elect_bad_block_voters_list(federation_3):
"""See https://github.com/bigchaindb/bigchaindb/issues/1224"""
[bx, (s0, s1, s2)] = federation_3
b = s0.block.bigchain
# First remove other nodes from node 0 so that it self assigns the tx
b.nodes_except_me = []
tx = input_single_create(b)
# Now create a block voters list which will not match other keyrings
b.nodes_except_me = [bx[1].me]
process_tx(s0)
process_vote(s0)
process_vote(s1)
process_vote(s2)
for i in range(3):
assert bx[i].get_transaction(tx.id, True)[1] == 'invalid'