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 multipipes import Pipeline, Node
|
||||||
|
|
||||||
|
from bigchaindb import config_utils, exceptions
|
||||||
from bigchaindb.pipelines.utils import ChangeFeed
|
from bigchaindb.pipelines.utils import ChangeFeed
|
||||||
from bigchaindb import Bigchain
|
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:
|
class Vote:
|
||||||
"""This class encapsulates the logic to vote on blocks.
|
"""This class encapsulates the logic to vote on blocks.
|
||||||
|
|
||||||
@ -23,15 +34,34 @@ class Vote:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Initialize the Block voter."""
|
"""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()
|
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.bigchain = Bigchain()
|
||||||
self.last_voted_id = last_voted['id']
|
self.last_voted_id = last_voted['id']
|
||||||
|
|
||||||
self.counters = Counter()
|
self.counters = Counter()
|
||||||
self.validity = {}
|
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.
|
"""Given a block, ungroup the transactions in it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -43,12 +73,16 @@ class Vote:
|
|||||||
transactions contained in the block otherwise.
|
transactions contained in the block otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.bigchain.has_previous_vote(block):
|
# XXX: if a block is invalid we should skip the `validate_tx` step,
|
||||||
return
|
# 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'])
|
num_tx = len(block['block']['transactions'])
|
||||||
for tx in block['block']['transactions']:
|
for tx in block['block']['transactions']:
|
||||||
yield tx, block['id'], num_tx
|
yield tx, block['id'], num_tx
|
||||||
|
else:
|
||||||
|
yield self.invalid_dummy_tx, block['id'], 1
|
||||||
|
|
||||||
def validate_tx(self, tx, block_id, num_tx):
|
def validate_tx(self, tx, block_id, num_tx):
|
||||||
"""Validate a transaction.
|
"""Validate a transaction.
|
||||||
@ -103,8 +137,8 @@ class Vote:
|
|||||||
def initial():
|
def initial():
|
||||||
"""Return unvoted blocks."""
|
"""Return unvoted blocks."""
|
||||||
b = Bigchain()
|
b = Bigchain()
|
||||||
initial = b.get_unvoted_blocks()
|
rs = b.get_unvoted_blocks()
|
||||||
return initial
|
return rs
|
||||||
|
|
||||||
|
|
||||||
def get_changefeed():
|
def get_changefeed():
|
||||||
@ -120,6 +154,7 @@ def create_pipeline():
|
|||||||
voter = Vote()
|
voter = Vote()
|
||||||
|
|
||||||
vote_pipeline = Pipeline([
|
vote_pipeline = Pipeline([
|
||||||
|
Node(voter.validate_block),
|
||||||
Node(voter.ungroup),
|
Node(voter.ungroup),
|
||||||
Node(voter.validate_tx, fraction_of_cores=1),
|
Node(voter.validate_tx, fraction_of_cores=1),
|
||||||
Node(voter.vote),
|
Node(voter.vote),
|
||||||
|
@ -55,11 +55,30 @@ def test_vote_ungroup_returns_a_set_of_results(b):
|
|||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
block = dummy_block(b)
|
block = dummy_block(b)
|
||||||
vote_obj = vote.Vote()
|
vote_obj = vote.Vote()
|
||||||
txs = list(vote_obj.ungroup(block))
|
txs = list(vote_obj.ungroup(block, True))
|
||||||
|
|
||||||
assert len(txs) == 10
|
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):
|
def test_vote_validate_transaction(b):
|
||||||
from bigchaindb.pipelines import vote
|
from bigchaindb.pipelines import vote
|
||||||
|
|
||||||
@ -99,7 +118,7 @@ def test_valid_block_voting_sequential(b, monkeypatch):
|
|||||||
vote_obj = vote.Vote()
|
vote_obj = vote.Vote()
|
||||||
block = dummy_block(b)
|
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))
|
last_vote = vote_obj.vote(*vote_obj.validate_tx(tx, block_id, num_tx))
|
||||||
|
|
||||||
vote_obj.write_vote(last_vote)
|
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
|
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
|
from bigchaindb.pipelines import vote
|
||||||
|
|
||||||
inpipe = Pipe()
|
inpipe = Pipe()
|
||||||
@ -290,6 +309,40 @@ def test_invalid_block_voting(monkeypatch, b, user_vk):
|
|||||||
vote_doc['signature']) is True
|
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):
|
def test_voter_considers_unvoted_blocks_when_single_node(monkeypatch, b):
|
||||||
from bigchaindb.pipelines import vote
|
from bigchaindb.pipelines import vote
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user