Merge pull request #76 from bigchaindb/bug/71/voter-wrong-input-validation

Bug/71/voter wrong input validation
This commit is contained in:
Rodolphe Marques 2016-02-23 16:05:11 +01:00
commit c54ef77e3e
3 changed files with 137 additions and 18 deletions

View File

@ -272,7 +272,6 @@ class Bigchain(object):
The transaction that used the `txid` as an input if it exists else it returns `None`
"""
# checks if an input was already spent
# checks if the bigchain has any transaction with input `transaction_id`
response = r.table('bigchain').concat_map(lambda doc: doc['block']['transactions'])\
@ -353,11 +352,12 @@ class Bigchain(object):
raise exceptions.TransactionOwnerError('current_owner `{}` does not own the input `{}`'.format(
transaction['transaction']['current_owner'], transaction['transaction']['input']))
# check if the input was already spent
# check if the input was already spent by a transaction other then this one.
spent = self.get_spent(tx_input['id'])
if spent:
raise exceptions.DoubleSpend('input `{}` was already spent'.format(
transaction['transaction']['input']))
if spent['id'] != transaction['id']:
raise exceptions.DoubleSpend('input `{}` was already spent'.format(
transaction['transaction']['input']))
# Check hash of the transaction
calculated_hash = hash_data(self.serialize(transaction['transaction']))
@ -496,13 +496,11 @@ class Bigchain(object):
# 2. create the block with one transaction
# 3. write the block to the bigchain
blocks_count = r.table('bigchain').count().run(self.conn)
if blocks_count:
raise GenesisBlockAlreadyExistsError('Cannot create the Genesis block')
payload = {'message': 'Hello World from the Bigchain'}
transaction = self.create_transaction(self.me, self.me, None, 'GENESIS', payload=payload)
transaction_signed = self.sign_transaction(transaction, self.me_private)

View File

@ -45,6 +45,7 @@ def inputs(user_public_key):
def test_remove_unclosed_sockets():
pass
class TestBigchainApi(object):
def test_create_transaction(self, b):
@ -58,7 +59,6 @@ class TestBigchainApi(object):
with pytest.raises(TypeError):
b.create_transaction('a', 'b', 'c', 'd', payload=[])
def test_transaction_hash(self, b):
payload = {'cats': 'are awesome'}
tx = b.create_transaction('a', 'b', 'c', 'd', payload)
@ -166,8 +166,8 @@ class TestBigchainApi(object):
b.create_genesis_block()
genesis_blocks = list(r.table('bigchain')
.filter(r.row['block_number'] == 0)
.run(b.conn))
.filter(r.row['block_number'] == 0)
.run(b.conn))
assert len(genesis_blocks) == 1
@ -349,6 +349,24 @@ class TestTransactionValidation(object):
assert tx_valid_signed == b.validate_transaction(tx_valid_signed)
assert tx_valid_signed == b.is_valid_transaction(tx_valid_signed)
@pytest.mark.usefixtures('inputs')
def test_valid_non_create_transaction_after_block_creation(self, b, user_public_key, user_private_key):
input_valid = b.get_owned_ids(user_public_key).pop()
tx_valid = b.create_transaction(user_public_key, 'b', input_valid, 'd')
tx_valid_signed = b.sign_transaction(tx_valid, user_private_key)
assert tx_valid_signed == b.validate_transaction(tx_valid_signed)
assert tx_valid_signed == b.is_valid_transaction(tx_valid_signed)
# create block
block = b.create_block([tx_valid_signed])
assert b.is_valid_block(block)
b.write_block(block, durability='hard')
# check that the transaction is still valid after being written to the bigchain
assert tx_valid_signed == b.validate_transaction(tx_valid_signed)
assert tx_valid_signed == b.is_valid_transaction(tx_valid_signed)
class TestBlockValidation(object):
@ -357,7 +375,7 @@ class TestBlockValidation(object):
# change block hash
block.update({'id': 'abc'})
with pytest.raises(exceptions.InvalidHash) as excinfo:
with pytest.raises(exceptions.InvalidHash):
b.validate_block(block)
@pytest.mark.skipif(reason='Separated tx validation from block creation.')
@ -368,7 +386,6 @@ class TestBlockValidation(object):
tx_invalid = b.create_transaction('a', 'b', valid_input, 'c')
block = b.create_block([tx_invalid])
assert invalid_transactions == [tx_invalid]
# create a block with invalid transactions
block = {
@ -520,7 +537,6 @@ class TestBigchainVoter(object):
time.sleep(1)
voter.kill()
# retrive block from bigchain
bigchain_block = r.table('bigchain').get(block['id']).run(b.conn)
@ -765,4 +781,3 @@ class TestBigchainBlock(object):
def test_duplicated_transactions(self):
pytest.skip('We may have duplicates in the initial_results and changefeed')

View File

@ -3,7 +3,6 @@ import time
import rethinkdb as r
import multiprocessing as mp
from bigchaindb import Bigchain
from bigchaindb.voter import Voter, BlockStream
from bigchaindb.crypto import PublicKey
@ -35,6 +34,47 @@ class TestBigchainVoter(object):
.order_by(r.asc((r.row['block']['timestamp'])))
.run(b.conn))
# validate vote
assert len(blocks[1]['votes']) == 1
vote = blocks[1]['votes'][0]
assert vote['vote']['voting_for_block'] == block['id']
assert vote['vote']['previous_block'] == genesis['id']
assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True
def test_valid_block_voting_with_create_transaction(self, b):
q_new_block = mp.Queue()
genesis = b.create_genesis_block()
# create a `CREATE` transaction
test_user_priv, test_user_pub = b.generate_keys()
tx = b.create_transaction(b.me, test_user_pub, None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
assert b.is_valid_transaction(tx_signed)
# create valid block
block = b.create_block([tx_signed])
# assert block is valid
assert b.is_valid_block(block)
b.write_block(block, durability='hard')
# create queue and voter
voter = Voter(q_new_block)
# vote
voter.start()
# wait for vote to be written
time.sleep(1)
voter.kill()
# retrive block from bigchain
blocks = list(r.table('bigchain')
.order_by(r.asc((r.row['block']['timestamp'])))
.run(b.conn))
# validate vote
assert len(blocks[1]['votes']) == 1
@ -47,6 +87,76 @@ class TestBigchainVoter(object):
assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True
def test_valid_block_voting_with_transfer_transactions(self, b):
q_new_block = mp.Queue()
b.create_genesis_block()
# create a `CREATE` transaction
test_user_priv, test_user_pub = b.generate_keys()
tx = b.create_transaction(b.me, test_user_pub, None, 'CREATE')
tx_signed = b.sign_transaction(tx, b.me_private)
assert b.is_valid_transaction(tx_signed)
# create valid block
block = b.create_block([tx_signed])
# assert block is valid
assert b.is_valid_block(block)
b.write_block(block, durability='hard')
# create queue and voter
voter = Voter(q_new_block)
# vote
voter.start()
# wait for vote to be written
time.sleep(1)
voter.kill()
# retrive block from bigchain
blocks = list(r.table('bigchain')
.order_by(r.asc((r.row['block']['timestamp'])))
.run(b.conn))
# validate vote
assert len(blocks[1]['votes']) == 1
# create a `TRANSFER` transaction
test_user2_priv, test_user2_pub = b.generate_keys()
tx2 = b.create_transaction(test_user_pub, test_user2_pub, tx['id'], 'TRANSFER')
tx2_signed = b.sign_transaction(tx2, test_user_priv)
assert b.is_valid_transaction(tx2_signed)
# create valid block
block = b.create_block([tx2_signed])
# assert block is valid
assert b.is_valid_block(block)
b.write_block(block, durability='hard')
# create queue and voter
voter = Voter(q_new_block)
# vote
voter.start()
# wait for vote to be written
time.sleep(1)
voter.kill()
# retrive block from bigchain
blocks = list(r.table('bigchain')
.order_by(r.asc((r.row['block']['timestamp'])))
.run(b.conn))
# validate vote
assert len(blocks[2]['votes']) == 1
vote = blocks[2]['votes'][0]
assert vote['vote']['voting_for_block'] == block['id']
assert vote['vote']['is_block_valid'] is True
assert vote['vote']['invalid_reason'] is None
assert vote['node_pubkey'] == b.me
assert PublicKey(b.me).verify(b.serialize(vote['vote']), vote['signature']) is True
def test_invalid_block_voting(self, b, user_public_key):
# create queue and voter
@ -66,7 +176,6 @@ class TestBigchainVoter(object):
assert not b.is_valid_block(block)
b.write_block(block, durability='hard')
# vote
voter.start()
time.sleep(1)
@ -167,7 +276,6 @@ class TestBigchainVoter(object):
time.sleep(1)
voter.kill()
# retrive blocks from bigchain
blocks = list(r.table('bigchain')
.order_by(r.asc((r.row['block']['timestamp'])))
@ -187,7 +295,6 @@ class TestBigchainVoter(object):
pass
class TestBlockStream(object):
def test_if_federation_size_is_greater_than_one_ignore_past_blocks(self, b):
@ -219,7 +326,6 @@ class TestBlockStream(object):
assert bs.get() == block_1
assert bs.get() == block_2
def test_if_old_blocks_get_should_return_old_block_first(self, b):
# create two blocks
block_1 = b.create_block([])