Ejemplo n.º 1
0
class TestBlockValidator(unittest.TestCase):
    def setUp(self):
        self.btm = BlockTreeManager()

    def create_block_validator(self, new_block, on_block_validated):
        return BlockValidator(consensus=TestModeVerifier(),
                              new_block=new_block,
                              chain_head=self.btm.chain_head,
                              block_cache=self.btm.block_cache,
                              done_cb=on_block_validated,
                              executor=MockTransactionExecutor(),
                              squash_handler=None)

    class BlockValidationHandler(object):
        def __init__(self):
            self.result = None

        def on_block_validated(self, commit_new_block, result):
            result["commit_new_block"] = commit_new_block
            self.result = result

        def has_result(self):
            return self.result is not None

    # fork based tests
    def test_fork_simple(self):
        """
        Test a simple case of a new block extending the current root.
        """
        bvh = self.BlockValidationHandler()
        new_block = self.btm.generate_block(previous_block=self.btm.chain_head,
                                            add_to_store=True)

        bv = self.create_block_validator(new_block, bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block.status == BlockStatus.Valid)
        self.assertTrue(bvh.result["commit_new_block"])

    def test_good_fork_lower(self):
        """
        Test case of a new block extending on a valid chain but not as long
        as the current chain.
        """
        bvh = self.BlockValidationHandler()

        root = self.btm.chain_head

        # create a new valid chain 5 long from the current root
        new_head = self.btm.generate_chain(root, 5, {'add_to_store': True})
        self.btm.set_chain_head(new_head[-1])
        # generate candidate chain 3 long from the same root
        new_block = self.btm.generate_chain(root, 3, {'add_to_cache': True})

        bv = self.create_block_validator(new_block[-1], bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block[-1].status == BlockStatus.Valid)
        self.assertFalse(bvh.result["commit_new_block"])

    def test_good_fork_higher(self):
        """
        Test case of a new block extending on a valid chain but longer
        than the current chain. ( similar to test_good_fork_lower but uses
        a different code path when finding the common root )
        """
        bvh = self.BlockValidationHandler()

        root = self.btm.chain_head

        # create a new valid chain 5 long from the current root
        new_head = self.btm.generate_chain(root, 5, {'add_to_store': True})
        self.btm.set_chain_head(new_head[-1])
        # generate candidate chain 8 long from the same root
        new_block = self.btm.generate_chain(root, 8, {'add_to_cache': True})
        bv = self.create_block_validator(new_block[-1], bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block[-1].status == BlockStatus.Valid)
        self.assertTrue(bvh.result["commit_new_block"])

    def test_fork_different_genesis(self):
        """"
        Test the case where new block is from a different genesis
        """
        bvh = self.BlockValidationHandler()

        # create a new valid chain 5 long from the current root
        new_head = self.btm.generate_chain(self.btm.chain_head, 5,
                                           {'add_to_store': True})
        self.btm.set_chain_head(new_head[-1])

        # generate candidate chain 5 long from it's own genesis
        new_block = self.btm.generate_chain(None, 5, {'add_to_cache': True})

        bv = self.create_block_validator(new_block[-1], bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block[-1].status == BlockStatus.Invalid)
        self.assertFalse(bvh.result["commit_new_block"])

    def test_fork_missing_predecessor(self):
        """"
        Test the case where new block is missing the a predecessor
        """
        bvh = self.BlockValidationHandler()

        root = self.btm.chain_head

        # generate candidate chain 3 long off the current head.
        new_block = self.btm.generate_chain(root, 3, {'add_to_cache': True})
        # remove one of the new blocks
        del self.btm.block_cache[new_block[1].identifier]

        bv = self.create_block_validator(new_block[-1], bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block[-1].status == BlockStatus.Invalid)
        self.assertFalse(bvh.result["commit_new_block"])

    def test_fork_invalid_predecessor(self):
        """"
        Test the case where new block has an invalid predecessor
        """
        bvh = self.BlockValidationHandler()

        root = self.btm.chain_head

        # generate candidate chain 3 long off the current head.
        new_block = self.btm.generate_chain(root, 3, {'add_to_cache': True})
        # Mark a predecessor as invalid
        new_block[1].status = BlockStatus.Invalid

        bv = self.create_block_validator(new_block[-1], bvh.on_block_validated)
        bv.run()

        self.assertTrue(bvh.has_result())
        self.assertTrue(new_block[-1].status == BlockStatus.Invalid)
        self.assertFalse(bvh.result["commit_new_block"])

    # block based tests
    def test_block_bad_signature(self):
        """
        Test the case where the new block has a bad signature.
        """
        pass

    def test_block_missing_batch(self):
        """
        Test the case where the new block is missing a batch.
        """
        pass

    def test_block_extra_batch(self):
        """
        Test the case where the new block has a batch.
        """
        pass

    def test_block_batches_order(self):
        """
        Test the case where the new block has batches that are
        out of order.
        """
        pass

    def test_block_bad_batch(self):
        """
        Test the case where the new block has a bad batch
        """
        pass

    def test_block_missing_batch_dependency(self):
        """
        Test the case where the new block has a batch that is missing a
        dependency.
        """
        pass

    def test_block_bad_state(self):
        """
        Test the case where the new block has a bad batch
        """
        pass

    def test_block_bad_consensus(self):
        """
        Test the case where the new block has a bad batch
        """
        pass
Ejemplo n.º 2
0
class TestBlockValidator(unittest.TestCase):
    def setUp(self):
        self.state_view_factory = MockStateViewFactory()

        self.block_tree_manager = BlockTreeManager()
        self.root = self.block_tree_manager.chain_head

        self.block_validation_handler = self.BlockValidationHandler()

    # fork based tests
    def test_fork_simple(self):
        """
        Test a simple case of a new block extending the current root.
        """

        new_block = self.block_tree_manager.generate_block(
            previous_block=self.root,
            add_to_store=True)

        self.validate_block(new_block)

        self.assert_valid_block(new_block)
        self.assert_new_block_committed()

    def test_good_fork_lower(self):
        """
        Test case of a new block extending on a valid chain but not as long
        as the current chain.
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 3 long from the same root
        new_chain, new_head = self.generate_chain_with_head(
            self.root, 3, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_valid_block(new_head)
        self.assert_new_block_not_committed()

    def test_good_fork_higher(self):
        """
        Test case of a new block extending on a valid chain but longer
        than the current chain. ( similar to test_good_fork_lower but uses
        a different code path when finding the common root )
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 8 long from the same root
        new_chain, new_head = self.generate_chain_with_head(
            head, 8, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_valid_block(new_head)
        self.assert_new_block_committed()

    def test_fork_different_genesis(self):
        """"
        Test the case where new block is from a different genesis
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 5 long from its own genesis
        new_chain, new_head = self.generate_chain_with_head(
            None, 5, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_invalid_block(new_head)
        self.assert_new_block_not_committed()

    def test_fork_missing_predecessor(self):
        """"
        Test the case where new block is missing the a predecessor
        """
        # generate candidate chain 5 long off the current head.
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_cache': True})

        # remove one of the new blocks
        del self.block_tree_manager.block_cache[chain[1].identifier]

        self.validate_block(head)

        self.assert_invalid_block(head)
        self.assert_new_block_not_committed()

    def test_fork_invalid_predecessor(self):
        """"
        Test the case where new block has an invalid predecessor
        """
        # generate candidate chain 5 long off the current head.
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_cache': True})

        # Mark a predecessor as invalid
        chain[1].status = BlockStatus.Invalid

        self.validate_block(head)

        self.assert_invalid_block(head)
        self.assert_new_block_not_committed()

    def test_block_bad_consensus(self):
        """
        Test the case where the new block has a bad batch
        """
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        new_block = self.block_tree_manager.generate_block(
            previous_block=head,
            add_to_cache=True,
            invalid_consensus=True)

        self.validate_block(new_block)

        self.assert_invalid_block(new_block)
        self.assert_new_block_not_committed()

    def test_block_bad_batch(self):
        """
        Test the case where the new block has a bad batch
        """
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        new_block = self.block_tree_manager.generate_block(
            previous_block=head,
            add_to_cache=True,
            invalid_batch=True)

        self.validate_block(new_block)

        self.assert_invalid_block(new_block)
        self.assert_new_block_not_committed()

    def test_block_missing_batch(self):
        """
        Test the case where the new block is missing a batch.
        """
        pass

    def test_block_extra_batch(self):
        """
        Test the case where the new block has an extra batch.
        """
        pass

    def test_block_batches_order(self):
        """
        Test the case where the new block has batches that are
        out of order.
        """
        pass

    def test_block_missing_batch_dependency(self):
        """
        Test the case where the new block has a batch that is missing a
        dependency.
        """
        pass

    # assertions

    def assert_valid_block(self, block):
        self.assertEqual(
            block.status, BlockStatus.Valid,
            "Block should be valid")

    def assert_invalid_block(self, block):
        self.assertEqual(
            block.status, BlockStatus.Invalid,
            "Block should be invalid")

    def assert_new_block_committed(self):
        self.assert_handler_has_result()
        self.assertTrue(
            self.block_validation_handler.result["commit_new_block"],
            "New block not committed, should be")

    def assert_new_block_not_committed(self):
        self.assert_handler_has_result()
        self.assertFalse(
            self.block_validation_handler.result["commit_new_block"],
            "New block committed, shouldn't be")

    def assert_handler_has_result(self):
        msg = "Validation handler doesn't have result"
        self.assertTrue(self.block_validation_handler.has_result(), msg)

    # block validation

    def validate_block(self, block):
        validator = self.create_block_validator(
            block,
            self.block_validation_handler.on_block_validated)

        validator.run()

    def create_block_validator(self, new_block, on_block_validated):
        return BlockValidator(
            consensus_module=mock_consensus,
            new_block=new_block,
            chain_head=self.block_tree_manager.chain_head,
            state_view_factory=self.state_view_factory,
            block_cache=self.block_tree_manager.block_cache,
            done_cb=on_block_validated,
            executor=MockTransactionExecutor(),
            squash_handler=None,
            identity_signing_key=self.block_tree_manager.identity_signing_key,
            data_dir=None,
            config_dir=None)

    class BlockValidationHandler(object):
        def __init__(self):
            self.result = None

        def on_block_validated(self, commit_new_block, result):
            result["commit_new_block"] = commit_new_block
            self.result = result

        def has_result(self):
            return self.result is not None

    # block tree manager interface

    def generate_chain_with_head(self, root_block, num_blocks, params=None):
        chain = self.block_tree_manager.generate_chain(
            root_block, num_blocks, params)

        head = chain[-1]

        return chain, head
Ejemplo n.º 3
0
class TestBlockValidator(unittest.TestCase):
    def setUp(self):
        self.state_view_factory = MockStateViewFactory()

        self.block_tree_manager = BlockTreeManager()
        self.root = self.block_tree_manager.chain_head

        self.block_validation_handler = self.BlockValidationHandler()

    # fork based tests
    def test_fork_simple(self):
        """
        Test a simple case of a new block extending the current root.
        """

        new_block = self.block_tree_manager.generate_block(
            previous_block=self.root)

        self.validate_block(new_block)

        self.assert_valid_block(new_block)
        self.assert_new_block_committed()

    def test_good_fork_lower(self):
        """
        Test case of a new block extending on a valid chain but not as long
        as the current chain.
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 3 long from the same root
        new_chain, new_head = self.generate_chain_with_head(
            self.root, 3, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_valid_block(new_head)
        self.assert_new_block_not_committed()

    def test_good_fork_higher(self):
        """
        Test case of a new block extending on a valid chain but longer
        than the current chain. ( similar to test_good_fork_lower but uses
        a different code path when finding the common root )
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 8 long from the same root
        new_chain, new_head = self.generate_chain_with_head(
            head, 8, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_valid_block(new_head)
        self.assert_new_block_committed()

    def test_fork_different_genesis(self):
        """"
        Test the case where new block is from a different genesis
        """
        # create a new valid chain 5 long from the current root
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        self.block_tree_manager.set_chain_head(head)

        # generate candidate chain 5 long from its own genesis
        new_chain, new_head = self.generate_chain_with_head(
            None, 5, {'add_to_cache': True})

        self.validate_block(new_head)

        self.assert_invalid_block(new_head)
        self.assert_new_block_not_committed()

    def test_fork_missing_predecessor(self):
        """"
        Test the case where new block is missing the a predecessor
        """
        # generate candidate chain 5 long off the current head.
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_cache': True})

        # remove one of the new blocks
        del self.block_tree_manager.block_cache[chain[1].identifier]

        self.validate_block(head)

        self.assert_invalid_block(head)
        self.assert_new_block_not_committed()

    def test_fork_invalid_predecessor(self):
        """"
        Test the case where new block has an invalid predecessor
        """
        # generate candidate chain 5 long off the current head.
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_cache': True})

        # Mark a predecessor as invalid
        chain[1].status = BlockStatus.Invalid

        self.validate_block(head)

        self.assert_invalid_block(head)
        self.assert_new_block_not_committed()

    def test_block_bad_consensus(self):
        """
        Test the case where the new block has a bad batch
        """
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        new_block = self.block_tree_manager.generate_block(
            previous_block=head,
            add_to_cache=True,
            invalid_consensus=True)

        self.validate_block(new_block)

        self.assert_invalid_block(new_block)
        self.assert_new_block_not_committed()

    def test_block_bad_batch(self):
        """
        Test the case where the new block has a bad batch
        """
        chain, head = self.generate_chain_with_head(
            self.root, 5, {'add_to_store': True})

        new_block = self.block_tree_manager.generate_block(
            previous_block=head,
            add_to_cache=True,
            invalid_batch=True)

        self.validate_block(new_block)

        self.assert_invalid_block(new_block)
        self.assert_new_block_not_committed()


    def test_block_missing_batch_dependency(self):
        """
        Test the case where the new block has a batch that is missing a
        dependency.
        """
        pass

    # assertions

    def assert_valid_block(self, block):
        self.assertEqual(
            block.status, BlockStatus.Valid,
            "Block should be valid")

    def assert_invalid_block(self, block):
        self.assertEqual(
            block.status, BlockStatus.Invalid,
            "Block should be invalid")

    def assert_new_block_committed(self):
        self.assert_handler_has_result()
        self.assertTrue(
            self.block_validation_handler.result["commit_new_block"],
            "New block not committed, should be")

    def assert_new_block_not_committed(self):
        self.assert_handler_has_result()
        self.assertFalse(
            self.block_validation_handler.result["commit_new_block"],
            "New block committed, shouldn't be")

    def assert_handler_has_result(self):
        msg = "Validation handler doesn't have result"
        self.assertTrue(self.block_validation_handler.has_result(), msg)

    # block validation

    def validate_block(self, block):
        validator = self.create_block_validator(
            block,
            self.block_validation_handler.on_block_validated)

        validator.run()

    def create_block_validator(self, new_block, on_block_validated):
        return BlockValidator(
            consensus_module=mock_consensus,
            new_block=new_block,
            chain_head=self.block_tree_manager.chain_head,
            state_view_factory=self.state_view_factory,
            block_cache=self.block_tree_manager.block_cache,
            done_cb=on_block_validated,
            executor=MockTransactionExecutor(batch_execution_result=None),
            squash_handler=None,
            identity_signing_key=self.block_tree_manager.identity_signing_key,
            data_dir=None,
            config_dir=None)

    class BlockValidationHandler(object):
        def __init__(self):
            self.result = None

        def on_block_validated(self, commit_new_block, result):
            result["commit_new_block"] = commit_new_block
            self.result = result

        def has_result(self):
            return self.result is not None

    # block tree manager interface

    def generate_chain_with_head(self, root_block, num_blocks, params=None):
        chain = self.block_tree_manager.generate_chain(
            root_block, num_blocks, params)

        head = chain[-1]

        return chain, head