def validate_block(bigchain, block):
        """Validate a block.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            block (dict): block to validate.

        Returns:
            The block if the block is valid else it raises an exception
            describing the reason why the block is invalid.

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # Check if the block was created by a federation node
        if block['block']['node_pubkey'] not in (bigchain.federation_nodes + [bigchain.me]):
            raise exceptions.OperationError('Only federation nodes can create blocks')

        # Check if block signature is valid
        verifying_key = crypto.VerifyingKey(block['block']['node_pubkey'])
        if not verifying_key.verify(util.serialize(block['block']), block['signature']):
            raise exceptions.InvalidSignature('Invalid block signature')

        return block
Ejemplo n.º 2
0
    def validate_block(bigchain, block):
        """Validate a block.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            block (dict): block to validate.

        Returns:
            The block if the block is valid else it raises an exception
            describing the reason why the block is invalid.

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # Check if the block was created by a federation node
        if block['block']['node_pubkey'] not in (bigchain.nodes_except_me +
                                                 [bigchain.me]):
            raise exceptions.OperationError(
                'Only federation nodes can create blocks')

        # Check if block signature is valid
        verifying_key = crypto.VerifyingKey(block['block']['node_pubkey'])
        if not verifying_key.verify(util.serialize(block['block']),
                                    block['signature']):
            raise exceptions.InvalidSignature('Invalid block signature')

        return block
    def test_create_new_block(self, b):
        new_block = b.create_block([])
        block_hash = crypto.hash_data(util.serialize(new_block['block']))

        assert new_block['block']['voters'] == [b.me]
        assert new_block['block']['node_pubkey'] == b.me
        assert crypto.VerifyingKey(b.me).verify(util.serialize(new_block['block']), new_block['signature']) is True
        assert new_block['id'] == block_hash
        assert new_block['votes'] == []
Ejemplo n.º 4
0
    def test_read_transaction(self, b, user_public_key, user_private_key):
        input_tx = b.get_owned_ids(user_public_key).pop()
        tx = b.create_transaction(user_public_key, 'b', input_tx, 'd')
        tx_signed = b.sign_transaction(tx, user_private_key)
        b.write_transaction(tx_signed)

        # create block and write it to the bighcain before retrieving the transaction
        block = b.create_block([tx_signed])
        b.write_block(block, durability='hard')

        response = b.get_transaction(tx_signed["id"])
        assert util.serialize(tx_signed) == util.serialize(response)
Ejemplo n.º 5
0
    def test_read_transaction(self, b, user_public_key, user_private_key):
        input_tx = b.get_owned_ids(user_public_key).pop()
        tx = b.create_transaction(user_public_key, 'b', input_tx, 'd')
        tx_signed = b.sign_transaction(tx, user_private_key)
        b.write_transaction(tx_signed)

        # create block and write it to the bighcain before retrieving the transaction
        block = b.create_block([tx_signed])
        b.write_block(block, durability='hard')

        response = b.get_transaction(tx_signed["id"])
        assert util.serialize(tx_signed) == util.serialize(response)
    def test_valid_block_voting(self, b):
        # create queue and voter
        q_new_block = mp.Queue()
        voter = Voter(q_new_block)

        genesis = b.create_genesis_block()
        # create valid block
        block = b.create_block([])
        # assert block is valid
        assert b.is_valid_block(block)
        b.write_block(block, durability='hard')

        # insert into queue
        # FIXME: we disable this because the voter can currently vote more than one time for a block
        # q_new_block.put(block)

        # vote
        voter.start()
        # wait for vote to be written
        time.sleep(1)
        voter.kill()

        # retrive block from bigchain
        bigchain_block = r.table('bigchain').get(block['id']).run(b.conn)

        # validate vote
        assert len(bigchain_block['votes']) == 1
        vote = bigchain_block['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 crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
    def test_invalid_transactions_in_block(self, b, user_public_key, ):
        # invalid transaction
        valid_input = b.get_owned_ids(user_public_key).pop()
        tx_invalid = b.create_transaction('a', 'b', valid_input, 'c')

        block = b.create_block([tx_invalid])

        # create a block with invalid transactions
        block = {
            'timestamp': util.timestamp(),
            'transactions': [tx_invalid],
            'node_pubkey': b.me,
            'voters': b.federation_nodes
        }

        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(b.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
            b.validate_block(block)

        assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
Ejemplo n.º 8
0
    def vote(self, block, previous_block_id, decision, invalid_reason=None):
        """Cast your vote on the block given the previous_block_hash and the decision (valid/invalid)
        return the block to the updated in the database.

        Args:
            block (dict): Block to vote.
            previous_block_id (str): The id of the previous block.
            decision (bool): Whether the block is valid or invalid.
            invalid_reason (Optional[str]): Reason the block is invalid
        """

        vote = {
            'voting_for_block': block['id'],
            'previous_block': previous_block_id,
            'is_block_valid': decision,
            'invalid_reason': invalid_reason,
            'timestamp': util.timestamp()
        }

        vote_data = util.serialize(vote)
        signature = crypto.SigningKey(self.me_private).sign(vote_data).decode()

        vote_signed = {
            'node_pubkey': self.me,
            'signature': signature,
            'vote': vote
        }

        return vote_signed
Ejemplo n.º 9
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError("Empty block creation is not allowed")

        # Create the new block
        block = {
            "timestamp": util.timestamp(),
            "transactions": validated_transactions,
            "node_pubkey": self.me,
            "voters": self.nodes_except_me + [self.me],
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {"id": block_hash, "block": block, "signature": block_signature}

        return block
Ejemplo n.º 10
0
    def vote(self, block, previous_block_id, decision, invalid_reason=None):
        """Cast your vote on the block given the previous_block_hash and the decision (valid/invalid)
        return the block to the updated in the database.

        Args:
            block (dict): Block to vote.
            previous_block_id (str): The id of the previous block.
            decision (bool): Whether the block is valid or invalid.
            invalid_reason (Optional[str]): Reason the block is invalid
        """

        vote = {
            'voting_for_block': block['id'],
            'previous_block': previous_block_id,
            'is_block_valid': decision,
            'invalid_reason': invalid_reason,
            'timestamp': util.timestamp()
        }

        vote_data = util.serialize(vote)
        signature = crypto.SigningKey(self.me_private).sign(vote_data)

        vote_signed = {
            'node_pubkey': self.me,
            'signature': signature,
            'vote': vote
        }

        return vote_signed
Ejemplo n.º 11
0
    def vote(self, block_id, previous_block_id, decision, invalid_reason=None):
        """Cast your vote on the block given the previous_block_hash and the decision (valid/invalid)
        return the block to the updated in the database.

        Args:
            block_id (str): The id of the block to vote.
            previous_block_id (str): The id of the previous block.
            decision (bool): Whether the block is valid or invalid.
            invalid_reason (Optional[str]): Reason the block is invalid
        """

        if block_id == previous_block_id:
            raise exceptions.CyclicBlockchainError()

        vote = {
            "voting_for_block": block_id,
            "previous_block": previous_block_id,
            "is_block_valid": decision,
            "invalid_reason": invalid_reason,
            "timestamp": util.timestamp(),
        }

        vote_data = util.serialize(vote)
        signature = crypto.SigningKey(self.me_private).sign(vote_data)

        vote_signed = {"node_pubkey": self.me, "signature": signature, "vote": vote}

        return vote_signed
Ejemplo n.º 12
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.federation_nodes + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Ejemplo n.º 13
0
def exists(digital_asset_payload):
    payload_hash = crypto.hash_data(util.serialize(digital_asset_payload))

    transactions = b.get_tx_by_payload_hash(payload_hash)
    result = transactions is not None and len(transactions) > 0

    return result
Ejemplo n.º 14
0
    def test_valid_block_voting(self, b):
        q_new_block = mp.Queue()

        genesis = b.create_genesis_block()

        # create valid block
        block = b.create_block([])
        # 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
        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 crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 15
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.federation_nodes + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data).decode()

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Ejemplo n.º 16
0
def exists(digital_asset_payload):
    payload_hash = crypto.hash_data(util.serialize(digital_asset_payload))

    transactions = b.get_tx_by_payload_hash(payload_hash)
    result = transactions is not None and len(transactions) > 0

    return result
Ejemplo n.º 17
0
    def test_valid_block_voting(self, b):
        q_new_block = mp.Queue()

        genesis = b.create_genesis_block()

        # create valid block
        block = b.create_block([])
        # 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
        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(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 18
0
    def test_vote_creation_invalid(self, b):
        # create valid block
        block = b.create_block([])
        # retrieve vote
        vote = b.vote(block, 'abc', False)

        # assert vote is correct
        assert vote['vote']['voting_for_block'] == block['id']
        assert vote['vote']['previous_block'] == 'abc'
        assert vote['vote']['is_block_valid'] is False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 19
0
    def test_vote_creation_invalid(self, b):
        # create valid block
        block = b.create_block([])
        # retrieve vote
        vote = b.vote(block, 'abc', False)

        # assert vote is correct
        assert vote['vote']['voting_for_block'] == block['id']
        assert vote['vote']['previous_block'] == 'abc'
        assert vote['vote']['is_block_valid'] is False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 20
0
 def test_transaction_hash(self, b):
     payload = {'cats': 'are awesome'}
     tx = b.create_transaction('a', 'b', 'c', 'd', payload)
     tx_calculated = {
         'current_owners': ['a'],
         'new_owners': ['b'],
         'input': 'c',
         'operation': 'd',
         'timestamp': tx['transaction']['timestamp'],
         'data': {
             'hash': hash_data(util.serialize(payload)),
             'payload': payload
         }
     }
     assert tx['transaction']['data'] == tx_calculated['data']
Ejemplo n.º 21
0
 def test_transaction_hash(self, b):
     payload = {'cats': 'are awesome'}
     tx = b.create_transaction('a', 'b', 'c', 'd', payload)
     tx_calculated = {
         'current_owner': 'a',
         'new_owner': 'b',
         'input': 'c',
         'operation': 'd',
         'timestamp': tx['transaction']['timestamp'],
         'data': {
             'hash': crypto.hash_data(util.serialize(payload)),
             'payload': payload
         }
     }
     assert tx['transaction']['data'] == tx_calculated['data']
Ejemplo n.º 22
0
    def test_invalid_block_voting(self, b, user_public_key):
        # create queue and voter
        q_new_block = mp.Queue()
        voter = Voter(q_new_block)

        # create transaction
        transaction = b.create_transaction(b.me, user_public_key, None,
                                           'CREATE')
        transaction_signed = b.sign_transaction(transaction, b.me_private)

        genesis = b.create_genesis_block()
        # create invalid block
        block = b.create_block([transaction_signed])
        # change transaction id to make it invalid
        block['block']['transactions'][0]['id'] = 'abc'
        assert b.is_valid_block(block) is False
        b.write_block(block, durability='hard')

        # insert into queue
        # FIXME: we disable this because the voter can currently vote more than one time for a block
        # q_new_block.put(block)

        # vote
        voter.start()
        # wait for the vote to be written
        time.sleep(1)
        voter.kill()

        # retrive block from bigchain
        bigchain_block = r.table('bigchain').get(block['id']).run(b.conn)

        # validate vote
        assert len(bigchain_block['votes']) == 1
        vote = bigchain_block['votes'][0]

        assert vote['vote']['voting_for_block'] == block['id']
        assert vote['vote']['previous_block'] == genesis['id']
        assert vote['vote']['is_block_valid'] is False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert PublicKey(b.me).verify(util.serialize(vote['vote']),
                                      vote['signature']) is True
Ejemplo n.º 23
0
    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 = crypto.generate_key_pair()
        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
        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 crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 24
0
    def validate_block(bigchain, block):
        """Validate a block.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            block (dict): block to validate.

        Returns:
            The block if the block is valid else it raises an exception
            describing the reason why the block is invalid.

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        return block
Ejemplo n.º 25
0
    def validate_block(bigchain, block):
        """Validate a block.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            block (dict): block to validate.

        Returns:
            The block if the block is valid else it raises an exception
            describing the reason why the block is invalid.

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        return block
Ejemplo n.º 26
0
    def test_invalid_block_voting(self, b, user_public_key):
        # create queue and voter
        q_new_block = mp.Queue()
        voter = Voter(q_new_block)

        # create transaction
        transaction = b.create_transaction(b.me, user_public_key, None, 'CREATE')
        transaction_signed = b.sign_transaction(transaction, b.me_private)

        genesis = b.create_genesis_block()
        # create invalid block
        block = b.create_block([transaction_signed])
        # change transaction id to make it invalid
        block['block']['transactions'][0]['id'] = 'abc'
        assert b.is_valid_block(block) is False
        b.write_block(block, durability='hard')

        # insert into queue
        # FIXME: we disable this because the voter can currently vote more than one time for a block
        # q_new_block.put(block)

        # vote
        voter.start()
        # wait for the vote to be written
        time.sleep(1)
        voter.kill()

        # retrive block from bigchain
        bigchain_block = r.table('bigchain').get(block['id']).run(b.conn)

        # validate vote
        assert len(bigchain_block['votes']) == 1
        vote = bigchain_block['votes'][0]

        assert vote['vote']['voting_for_block'] == block['id']
        assert vote['vote']['previous_block'] == genesis['id']
        assert vote['vote']['is_block_valid'] is False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 27
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError(
                'Empty block creation is not allowed')

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.nodes_except_me + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Ejemplo n.º 28
0
    def test_invalid_block_voting(self, b, user_vk):
        # create queue and voter
        q_new_block = mp.Queue()
        voter = Voter(q_new_block)

        # create transaction
        transaction = b.create_transaction(b.me, user_vk, None, 'CREATE')
        transaction_signed = b.sign_transaction(transaction, b.me_private)

        genesis = b.create_genesis_block()

        # create invalid block
        block = b.create_block([transaction_signed])
        # change transaction id to make it invalid
        block['block']['transactions'][0]['id'] = 'abc'
        assert not b.is_valid_block(block)
        b.write_block(block, durability='hard')

        # vote
        voter.start()
        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
        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 False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 29
0
    def test_invalid_block_voting(self, b, user_public_key):
        # create queue and voter
        q_new_block = mp.Queue()
        voter = Voter(q_new_block)

        # create transaction
        transaction = b.create_transaction(b.me, user_public_key, None, 'CREATE')
        transaction_signed = b.sign_transaction(transaction, b.me_private)

        genesis = b.create_genesis_block()

        # create invalid block
        block = b.create_block([transaction_signed])
        # change transaction id to make it invalid
        block['block']['transactions'][0]['id'] = 'abc'
        assert not b.is_valid_block(block)
        b.write_block(block, durability='hard')

        # vote
        voter.start()
        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
        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 False
        assert vote['vote']['invalid_reason'] is None
        assert vote['node_pubkey'] == b.me
        assert PublicKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 30
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError('Empty block creation is not allowed')

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.nodes_except_me + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Ejemplo n.º 31
0
    def validate_block(self, block):
        """Validate a block.

        Args:
            block (dict): block to validate.

        Returns:
            The block if the block is valid else it raises and exception
            describing the reason why the block is invalid.
        """

        # 1. Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # 2. Validate all transactions in the block
        for transaction in block['block']['transactions']:
            if not self.is_valid_transaction(transaction):
                # this will raise the exception
                self.validate_transaction(transaction)

        return block
Ejemplo n.º 32
0
    def validate_transaction(bigchain, transaction):
        """Validate a transaction.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            transaction (dict): transaction to validate.

        Returns:
            The transaction if the transaction is valid else it raises an
            exception describing the reason why the transaction is invalid.

        Raises:
            OperationError: if the transaction operation is not supported
            TransactionDoesNotExist: if the input of the transaction is not found
            TransactionOwnerError: if the new transaction is using an input it doesn't own
            DoubleSpend: if the transaction is a double spend
            InvalidHash: if the hash of the transaction is wrong
            InvalidSignature: if the signature of the transaction is wrong
        """

        # If the operation is CREATE the transaction should have no inputs and
        # should be signed by a federation node
        if transaction['transaction']['operation'] == 'CREATE':
            if transaction['transaction']['input']:
                raise ValueError('A CREATE operation has no inputs')
            if transaction['transaction']['current_owner'] not in (
                    bigchain.federation_nodes + [bigchain.me]):
                raise exceptions.OperationError(
                    'Only federation nodes can use the operation `CREATE`')

        else:
            # check if the input exists, is owned by the current_owner
            if not transaction['transaction']['input']:
                raise ValueError(
                    'Only `CREATE` transactions can have null inputs')

            tx_input = bigchain.get_transaction(
                transaction['transaction']['input'])

            if not tx_input:
                raise exceptions.TransactionDoesNotExist(
                    'input `{}` does not exist in the bigchain'.format(
                        transaction['transaction']['input']))

            if (tx_input['transaction']['new_owner'] !=
                transaction['transaction']['current_owner']):
                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 by a transaction other than
            # this one.
            spent = bigchain.get_spent(tx_input['id'])
            if spent and spent['id'] != transaction['id']:
                raise exceptions.DoubleSpend(
                    'input `{}` was already spent'.format(
                        transaction['transaction']['input']))

        # Check hash of the transaction
        calculated_hash = crypto.hash_data(util.serialize(
            transaction['transaction']))
        if calculated_hash != transaction['id']:
            raise exceptions.InvalidHash()

        # Check signature
        if not util.verify_signature(transaction):
            raise exceptions.InvalidSignature()

        return transaction
Ejemplo n.º 33
0
    def validate_transaction(bigchain, transaction):
        """Validate a transaction.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            transaction (dict): transaction to validate.

        Returns:
            The transaction if the transaction is valid else it raises an
            exception describing the reason why the transaction is invalid.

        Raises:
            OperationError: if the transaction operation is not supported
            TransactionDoesNotExist: if the input of the transaction is not found
            TransactionOwnerError: if the new transaction is using an input it doesn't own
            DoubleSpend: if the transaction is a double spend
            InvalidHash: if the hash of the transaction is wrong
            InvalidSignature: if the signature of the transaction is wrong
        """

        # If the operation is CREATE the transaction should have no inputs and
        # should be signed by a federation node
        if transaction['transaction']['operation'] == 'CREATE':
            if transaction['transaction']['input']:
                raise ValueError('A CREATE operation has no inputs')
            if transaction['transaction']['current_owner'] not in (
                    bigchain.federation_nodes + [bigchain.me]):
                raise exceptions.OperationError(
                    'Only federation nodes can use the operation `CREATE`')

        else:
            # check if the input exists, is owned by the current_owner
            if not transaction['transaction']['input']:
                raise ValueError(
                    'Only `CREATE` transactions can have null inputs')

            tx_input = bigchain.get_transaction(
                transaction['transaction']['input'])

            if not tx_input:
                raise exceptions.TransactionDoesNotExist(
                    'input `{}` does not exist in the bigchain'.format(
                        transaction['transaction']['input']))

            if (tx_input['transaction']['new_owner'] !=
                    transaction['transaction']['current_owner']):
                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 by a transaction other than
            # this one.
            spent = bigchain.get_spent(tx_input['id'])
            if spent and spent['id'] != transaction['id']:
                raise exceptions.DoubleSpend(
                    'input `{}` was already spent'.format(
                        transaction['transaction']['input']))

        # Check hash of the transaction
        calculated_hash = crypto.hash_data(
            util.serialize(transaction['transaction']))
        if calculated_hash != transaction['id']:
            raise exceptions.InvalidHash()

        # Check signature
        if not util.verify_signature(transaction):
            raise exceptions.InvalidSignature()

        return transaction
Ejemplo n.º 34
0
    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 = crypto.generate_key_pair()
        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 = crypto.generate_key_pair()
        tx2 = b.create_transaction(test_user_pub, test_user2_pub, {'txid': tx['id'], 'cid': 0}, '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 crypto.VerifyingKey(b.me).verify(util.serialize(vote['vote']), vote['signature']) is True
Ejemplo n.º 35
0
 def test_serializer(self, b):
     tx = b.create_transaction('a', 'b', 'c', 'd')
     assert util.deserialize(util.serialize(tx)) == tx
Ejemplo n.º 36
0
 def test_serializer(self, b):
     tx = b.create_transaction('a', 'b', 'c', 'd')
     assert util.deserialize(util.serialize(tx)) == tx