Validate block metadata

This commit is contained in:
vrde 2016-08-05 11:58:07 +02:00
parent 3ad72077d3
commit 25d10957ca
No known key found for this signature in database
GPG Key ID: 6581C7C39B3D397D
2 changed files with 100 additions and 12 deletions

View File

@ -9,10 +9,21 @@ from collections import Counter
from multipipes import Pipeline, Node
from bigchaindb import config_utils, exceptions
from bigchaindb.pipelines.utils import ChangeFeed
from bigchaindb import Bigchain
def create_invalid_tx():
"""Create and return an invalid transaction.
The transaction is invalid because it's missing the signature."""
b = Bigchain()
tx = b.create_transaction(b.me, b.me, None, 'CREATE')
return tx
class Vote:
"""This class encapsulates the logic to vote on blocks.
@ -23,15 +34,34 @@ class Vote:
def __init__(self):
"""Initialize the Block voter."""
# Since cannot share a connection to RethinkDB using multiprocessing,
# we need to create a temporary instance of BigchainDB that we use
# only to query RethinkDB
last_voted = Bigchain().get_last_voted_block()
self.consensus = config_utils.load_consensus_plugin()
# This is the Bigchain instance that will be "shared" (aka: copied)
# by all the subprocesses
self.bigchain = Bigchain()
self.last_voted_id = last_voted['id']
self.counters = Counter()
self.validity = {}
def ungroup(self, block):
self.invalid_dummy_tx = create_invalid_tx()
def validate_block(self, block):
if not self.bigchain.has_previous_vote(block):
try:
self.consensus.validate_block(self.bigchain, block)
valid = True
except (exceptions.InvalidHash,
exceptions.OperationError,
exceptions.InvalidSignature) as e:
valid = False
return block, valid
def ungroup(self, block, valid):
"""Given a block, ungroup the transactions in it.
Args:
@ -43,12 +73,16 @@ class Vote:
transactions contained in the block otherwise.
"""
if self.bigchain.has_previous_vote(block):
return
num_tx = len(block['block']['transactions'])
for tx in block['block']['transactions']:
yield tx, block['id'], num_tx
# 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 transaction
# and propagate it to the next steps of the pipeline
if valid:
num_tx = len(block['block']['transactions'])
for tx in block['block']['transactions']:
yield tx, block['id'], num_tx
else:
yield self.invalid_dummy_tx, block['id'], 1
def validate_tx(self, tx, block_id, num_tx):
"""Validate a transaction.
@ -103,8 +137,8 @@ class Vote:
def initial():
"""Return unvoted blocks."""
b = Bigchain()
initial = b.get_unvoted_blocks()
return initial
rs = b.get_unvoted_blocks()
return rs
def get_changefeed():
@ -120,6 +154,7 @@ def create_pipeline():
voter = Vote()
vote_pipeline = Pipeline([
Node(voter.validate_block),
Node(voter.ungroup),
Node(voter.validate_tx, fraction_of_cores=1),
Node(voter.vote),

View File

@ -55,11 +55,30 @@ def test_vote_ungroup_returns_a_set_of_results(b):
b.create_genesis_block()
block = dummy_block(b)
vote_obj = vote.Vote()
txs = list(vote_obj.ungroup(block))
txs = list(vote_obj.ungroup(block, True))
assert len(txs) == 10
def test_vote_validate_block(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
tx = dummy_tx(b)
block = b.create_block([tx])
vote_obj = vote.Vote()
validation = vote_obj.validate_block(block)
assert validation == (block, True)
block = b.create_block([tx])
block['block']['id'] = 'this-is-not-a-valid-hash'
vote_obj = vote.Vote()
validation = vote_obj.validate_block(block)
assert validation == (block, False)
def test_vote_validate_transaction(b):
from bigchaindb.pipelines import vote
@ -99,7 +118,7 @@ def test_valid_block_voting_sequential(b, monkeypatch):
vote_obj = vote.Vote()
block = dummy_block(b)
for tx, block_id, num_tx in vote_obj.ungroup(block):
for tx, block_id, num_tx in vote_obj.ungroup(block, True):
last_vote = vote_obj.vote(*vote_obj.validate_tx(tx, block_id, num_tx))
vote_obj.write_vote(last_vote)
@ -256,7 +275,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
vote2_doc['signature']) is True
def test_invalid_block_voting(monkeypatch, b, user_vk):
def test_invalid_tx_in_block_voting(monkeypatch, b, user_vk):
from bigchaindb.pipelines import vote
inpipe = Pipe()
@ -290,6 +309,40 @@ def test_invalid_block_voting(monkeypatch, b, user_vk):
vote_doc['signature']) is True
def test_invalid_block_voting(monkeypatch, b, user_vk):
from bigchaindb.pipelines import vote
inpipe = Pipe()
outpipe = Pipe()
monkeypatch.setattr(util, 'timestamp', lambda: '1')
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
block = dummy_block(b)
block['block']['id'] = 'this-is-not-a-valid-hash'
inpipe.put(block)
vote_pipeline.start()
vote_out = outpipe.get()
vote_pipeline.terminate()
vote_rs = r.table('votes').get_all([block['id'], b.me],
index='block_and_voter').run(b.conn)
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block['id'],
'previous_block': genesis['id'],
'is_block_valid': False,
'invalid_reason': None,
'timestamp': '1'}
assert vote_doc['node_pubkey'] == b.me
assert crypto.VerifyingKey(b.me).verify(util.serialize(vote_doc['vote']),
vote_doc['signature']) is True
def test_voter_considers_unvoted_blocks_when_single_node(monkeypatch, b):
from bigchaindb.pipelines import vote