mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Validate block metadata
This commit is contained in:
parent
3ad72077d3
commit
25d10957ca
@ -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),
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user