mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
federation tests
This commit is contained in:
parent
03ca4b1fd3
commit
d8b84891b6
215
tests/integration/test_federation.py
Normal file
215
tests/integration/test_federation.py
Normal file
@ -0,0 +1,215 @@
|
||||
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):
|
||||
nonlocal keys
|
||||
if i == 0:
|
||||
yield []
|
||||
else:
|
||||
config = deepcopy(config_orig)
|
||||
keys = [keys[-1]] + keys[:-1]
|
||||
config['keypair']['private'] = keys[0][0]
|
||||
config['keypair']['public'] = keys[0][1]
|
||||
config['keyring'] = list(list(zip(*keys[1:]))[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
|
||||
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
|
||||
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
|
||||
def test_elect_disagree_prev_block(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(s0, True)
|
||||
for i in range(3):
|
||||
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
|
||||
s1.vote.last_voted_id = '5' * 64
|
||||
process_vote(s1, True)
|
||||
for i in range(3):
|
||||
assert bx[i].get_transaction(tx.id, True)[1] == 'undecided'
|
||||
s2.vote.last_voted_id = '6' * 64
|
||||
process_vote(s2, True)
|
||||
for i in range(3):
|
||||
assert bx[i].get_transaction(tx.id, True)[1] is None
|
||||
|
||||
|
||||
@pytest.mark.skip() # TODO: wait for #1309
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.genesis
|
||||
def test_elect_dupe_vote(federation_3):
|
||||
from bigchaindb.exceptions import CriticalDuplicateVote
|
||||
[bx, (s0, s1, s2)] = federation_3
|
||||
tx = input_single_create(bx[0])
|
||||
process_tx(s0)
|
||||
process_tx(s1)
|
||||
process_tx(s2)
|
||||
vote = process_vote(s0, True)
|
||||
# Drop the unique index and write the vote again
|
||||
bx[0].connection.db.votes.drop_index('block_and_voter')
|
||||
s0.queues['vote_write_vote'].append([vote])
|
||||
s0.vote_write_vote()
|
||||
for i in range(3):
|
||||
with pytest.raises(CriticalDuplicateVote):
|
||||
bx[i].get_transaction(tx.id, True)[1]
|
||||
|
||||
|
||||
@pytest.mark.bdb
|
||||
@pytest.mark.genesis
|
||||
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!
|
||||
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'
|
@ -163,6 +163,8 @@ def _update_stepper(stepper, prefix, pipeline):
|
||||
n1 = (nodes + [None])[i+1]
|
||||
f = stepper.add_input if i == 0 else stepper.add_stage
|
||||
f(prefix, n0, n1)
|
||||
# Expose pipeline state
|
||||
setattr(stepper, prefix, nodes[-1].target.__self__)
|
||||
|
||||
|
||||
def create_stepper():
|
||||
|
Loading…
x
Reference in New Issue
Block a user