Exemple #1
0
    def __init__(self, epoch, validators=Validators()):
        initial_validators = validators.validators
        if not initial_validators:
            initial_validators = Validators.read_genesis_validators_from_file()
        self.epoch = epoch
        self.stake_manager = StakeManager(epoch)
        genesis_hash = self.epoch.dag.genesis_block().get_hash()
        validator_count = len(initial_validators)
        initial_signers_indexes = validators.signers_order
        if not initial_signers_indexes:
            initial_signers_indexes = self.epoch.calculate_validators_indexes(
                genesis_hash, validator_count, Source.SIGNERS)

        initial_randomizers_indexes = validators.randomizers_order
        if not initial_randomizers_indexes:
            initial_randomizers_indexes = self.epoch.calculate_validators_indexes(
                genesis_hash, validator_count, Source.RANDOMIZERS)

        self.log(
            "Initial signers:", initial_signers_indexes[0:ROUND_DURATION * 1],
            initial_signers_indexes[ROUND_DURATION * 1:ROUND_DURATION * 2],
            initial_signers_indexes[ROUND_DURATION * 2:ROUND_DURATION * 3],
            initial_signers_indexes[ROUND_DURATION * 3:ROUND_DURATION * 4],
            initial_signers_indexes[ROUND_DURATION * 4:ROUND_DURATION * 5],
            initial_signers_indexes[ROUND_DURATION * 5:ROUND_DURATION * 6 + 1])
        self.log("Initial randomizers:", initial_randomizers_indexes)

        # init validators list and indexes, so we can build list of future validators based on this
        self.epoch_validators = {genesis_hash: initial_validators}
        self.signers_indexes = {genesis_hash: initial_signers_indexes}
        self.randomizers_indexes = {genesis_hash: initial_randomizers_indexes}
    def test_send_positive_gossip(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)
        helper.generate_nodes(private_keys, 19)  # create validators

        # generate blocks to new epoch
        helper.perform_block_steps(22)
        DagVisualizer.visualize(network.nodes[0].dag)

        # invalidate that node DO not send negative gossip only if have ZETA negatives from next ZETA validators
        round_2_signers_order = list(
            network.nodes[0].permissions.signers_indexes.values())[
                1]  # SECOND! EPOCH
        last_block_signer_id = round_2_signers_order[2]
        last_signed_block_number = network.nodes[
            last_block_signer_id].last_signed_block_number
        # assert signers order
        self.assertEqual(last_signed_block_number, 22)

        expected_node_signer_id = round_2_signers_order[
            3]  # already have 22 blocks
        # maliciously skip block by next signer
        network.nodes[
            expected_node_signer_id].behaviour.transport_cancel_block_broadcast = True

        # perform in block step
        Time.advance_to_next_timeslot()
        # perform step by malicious node (create block and not broadcast)
        network.nodes[expected_node_signer_id].step()
        # assume that node has block in local dag
        for node in network.nodes:
            if node.node_id == expected_node_signer_id:
                self.assertEqual(len(node.dag.blocks_by_number), 24)
            else:
                self.assertEqual(len(node.dag.blocks_by_number), 23)

        # perform step in timeslot by all nodes
        helper.perform_in_block_single_step(3)
        Time.advance_to_next_timeslot()  # move to next timeslot
        network.nodes[
            expected_node_signer_id].behaviour.transport_cancel_block_broadcast = False
        helper.perform_in_block_single_step(1)
        # validate send gossips that all nodes receive block by positive gossip
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 25)

        helper.perform_block_steps(5)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 30)
    def test_two_node_groups(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)

        helper.generate_nodes(private_keys, 19)  # create validators
        helper.add_stakeholders(9)  # add stakeholders to network

        # generate blocks to new epoch
        helper.perform_block_steps(22)
        DagVisualizer.visualize(network.nodes[0].dag)

        # divide network into two groups
        network.move_nodes_to_group_by_id(1, [
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
            19
        ])
        network.move_nodes_to_group_by_id(2, [20, 21, 22, 23, 24, 25, 26, 27])

        test_group_1 = network.groups.get(1)
        test_group_2 = network.groups.get(2)
        # check nodes count in groups
        self.assertEqual(len(test_group_1), 20)
        self.assertEqual(len(test_group_2), 8)

        helper.perform_block_steps(5)
        self.assertEqual(len(network.groups.get(1)[0].dag.blocks_by_hash),
                         28)  # group_1 = 28 blocks
        self.assertEqual(len(network.groups.get(2)[0].dag.blocks_by_hash),
                         23)  # group_2 = 23 blocks

        network.merge_all_groups()  # marge all groups
        self.assertEqual(len(network.nodes), 28)
        helper.perform_block_steps(1)  # perform sync timeslot steps

        # !!! on performed step all nodes from second group obtain all missed blocks to last received
        # [21, 22, 23, 24, 25, 26, 27, 28]

        # check first node blocks
        self.assertEqual(len(network.nodes[0].dag.blocks_by_hash), 29)
        # check last node blocks
        self.assertEqual(len(network.nodes[27].dag.blocks_by_hash), 29)
        # nodes tops assert
        self.assertEqual(network.nodes[27].epoch.tops_and_epochs,
                         network.nodes[0].epoch.tops_and_epochs)
    def test_block_getter_by_node_groups(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)
        helper.generate_nodes(private_keys, 19)  # create validators
        # add validators for group
        helper.add_stakeholders(9)  # add stakeholders to network

        # generate blocks to new epoch
        helper.perform_block_steps(22)

        # divide network into two groups
        network.move_nodes_to_group_by_id(1, [
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
            19
        ])
        network.move_nodes_to_group_by_id(2, [20, 21, 22, 23, 24, 25, 26, 27])

        # perform step for generate merged block
        epoch_0_signers_order = list(
            network.nodes[0].permissions.signers_indexes.values())[
                0]  # SECOND! EPOCH
        next_malicious_signer_node_index = epoch_0_signers_order[3]
        network.groups[1][
            next_malicious_signer_node_index].behaviour.malicious_excessive_block_count = 1

        helper.perform_block_steps(1)

        self.assertEqual(len(network.groups.get(1)[0].dag.blocks_by_hash),
                         25)  # group_1 = 25 blocks
        self.assertEqual(len(network.groups.get(2)[0].dag.blocks_by_hash),
                         23)  # group_2 = 23 blocks
        # DagVisualizer.visualize(network.groups.get(1)[0].dag)
        # DagVisualizer.visualize(network.groups.get(2)[0].dag)

        network.merge_all_groups()

        self.assertEqual(len(network.nodes[0].dag.blocks_by_hash), 25)
        self.assertEqual(len(network.nodes[27].dag.blocks_by_hash), 23)

        helper.perform_block_steps(1)

        # all blocks are received and marged by orphan system
        self.assertEqual(len(network.nodes[0].dag.blocks_by_hash), 26)
        self.assertEqual(len(network.nodes[27].dag.blocks_by_hash), 26)
    def test_more_conflict_blocks(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)
        helper.generate_nodes(private_keys, 19)  # create validators

        # generate blocks to new epoch
        helper.perform_block_steps(5)

        epoch_0_signers_order = list(
            network.nodes[0].permissions.signers_indexes.values())[
                0]  # SECOND! EPOCH
        next_malicious_signer_node_index = epoch_0_signers_order[
            5]  # block count
        # set behavior for next validator is_malicious_excessive_block = True
        network.nodes[
            next_malicious_signer_node_index].behaviour.malicious_excessive_block_count = 2
        network.nodes[
            next_malicious_signer_node_index].tried_to_sign_current_block = False
        network.nodes[
            next_malicious_signer_node_index].last_signed_block_number = 0
        helper.perform_block_steps(1)
        #
        network.nodes[
            next_malicious_signer_node_index].tried_to_sign_current_block = False
        network.nodes[
            next_malicious_signer_node_index].last_signed_block_number = 0
        helper.perform_block_steps(1)
        #
        # nodes MUST HAVE +2 conflict blocks
        helper.perform_block_steps(1)
        helper.perform_block_steps(1)
        DagVisualizer.visualize(network.nodes[0].dag)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'],
                              10)  # + 2 conflict

        # -- next validator MUST get two block conflicts send transaction
        helper.perform_block_steps(1)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.system_txs'], 1)
        # all nodes have all blocks test done
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 11)
    def test_negative_gossips_zata_validators(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)
        helper.generate_nodes(private_keys, 19)  # create validators

        # generate blocks to new epoch
        helper.perform_block_steps(22)
        # DagVisualizer.visualize(network.nodes[0].dag)

        # invalidate that node DO not send negative gossip only if have ZETA negatives from next ZETA validators
        round_2_signers_order = list(
            network.nodes[0].permissions.signers_indexes.values())[
                1]  # SECOND! EPOCH
        expected_node_signer_id = round_2_signers_order[
            2]  # already have 22 blocks
        last_block_signer_index = network.nodes[
            expected_node_signer_id].last_signed_block_number

        # assert signers order
        self.assertEqual(last_block_signer_index, 22)
        # get next ZETA SIGNERS by order
        next_zeta_signers_order = round_2_signers_order[
            3:3 + ZETA +
            1]  # -20 for first epoch +1 for maliciously skip block
        # next_zeta_signers_order gossips need to wait

        # maliciously skip block by next signer
        network.nodes[
            expected_node_signer_id].behaviour.malicious_skip_block = True
        # perform in block step
        Time.advance_to_next_timeslot()
        helper.perform_in_block_single_step(BLOCK_TIME)
        Time.advance_to_next_timeslot()
        helper.perform_in_block_single_step(1)
        # validate send gossips

        # nodes may contain different count of gossips but MUST contain all ZETA validators keys for stop send -gossip
        self.assertEqual(len(network.nodes[0].mempool.gossips), 5)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              5)
    def test_conflict_block_validator_processing(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()

        network = Network()
        helper = TestHelper(network)
        helper.generate_nodes(private_keys, 19)  # create validators

        # generate blocks to new epoch
        helper.perform_block_steps(5)

        epoch_0_signers_order = list(
            network.nodes[0].permissions.signers_indexes.values())[
                0]  # SECOND! EPOCH
        next_malicious_signer_node_index = epoch_0_signers_order[
            5]  # block count
        # set behavior for next validator is_malicious_excessive_block = True
        network.nodes[
            next_malicious_signer_node_index].behaviour.malicious_excessive_block_count = 1
        # perform next one block step
        helper.perform_block_steps(1)
        # nodes MUST HAVE 2 conflict blocks
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'],
                              7)  # + 1 conflict
        network.nodes[
            next_malicious_signer_node_index].behaviour.malicious_excessive_block = False
        # -- next validator MUST get two block conflicts
        helper.perform_block_steps(1)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.system_txs'], 0)
        # all nodes have all blocks test done
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 8)
    def test_node_broadcast_unavailable(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node0)

        # behaviour flag for disabling node to broadcast
        behaviour = Behaviour()
        behaviour.transport_node_disable_output = True
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behaviour)
        network.register_node(node1)

        Time.advance_to_next_timeslot()
        node0.step()  # provide block
        self.assertEqual(len(node0.dag.blocks_by_number),
                         2)  # ensure that node0 provide block to chain
        self.assertEqual(len(node1.dag.blocks_by_number),
                         2)  # ensure that node1 receive block

        node1.step()  # do nothing
        self.assertEqual(len(node0.dag.blocks_by_number), 2)
        self.assertEqual(len(node1.dag.blocks_by_number), 2)

        Time.advance_to_next_timeslot()
        node0.step()  # do nothing
        node1.step(
        )  # node1 must provide block (and public key tx) but unable to broadcast it by network
        self.assertEqual(len(node0.dag.blocks_by_number), 2)
        self.assertEqual(len(node1.dag.blocks_by_number), 3)
    def test_penalty(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        permissions = Permissions(epoch)
        node_private = Private.generate()

        initial_validators = Validators.read_genesis_validators_from_file()

        genesis_hash = dag.genesis_block().get_hash()

        last_block_number = Epoch.get_epoch_end_block_number(1)
        prev_hash = ChainGenerator.fill_with_dummies(
            dag, genesis_hash, range(1, last_block_number))

        block = BlockFactory.create_block_with_timestamp(
            [prev_hash], BLOCK_TIME * last_block_number)
        tx = PenaltyTransaction()
        tx.conflicts = [prev_hash]
        tx.signature = Private.sign(tx.get_hash(), node_private)
        block.system_txs = [tx]
        signed_block = BlockFactory.sign_block(block, node_private)
        dag.add_signed_block(last_block_number, signed_block)

        initial_validators_order = permissions.get_signers_indexes(
            genesis_hash)
        # we substract two here: one because it is last but one block
        # and one, because epoch starts from 1
        validator_index_to_penalize = initial_validators_order[
            last_block_number - 2]

        resulting_validators = permissions.get_validators(block.get_hash())

        self.assertNotEqual(len(initial_validators), len(resulting_validators))

        initial_validators.pop(validator_index_to_penalize)

        init_pubkeys = list(
            map(lambda validator: validator.public_key, initial_validators))
        result_pubkeys = list(
            map(lambda validator: validator.public_key, resulting_validators))

        self.assertEqual(init_pubkeys, result_pubkeys)
    def test_stake_release_by_genesis_validator(self):
        # base initialization
        dag = Dag(0)
        epoch = Epoch(dag)
        permissions = Permissions(epoch)
        node_private = Private.generate()

        initial_validators = Validators.read_genesis_validators_from_file()

        genesis_hash = dag.genesis_block().get_hash()
        prev_hash = genesis_hash
        for i in range(1, 9):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        # get one of validators
        genesis_validator = initial_validators[9]

        # create stake release transaction for new stakeholder
        tx_release = StakeReleaseTransaction()
        tx_release.pubkey = Keys.to_bytes(genesis_validator.public_key)
        tx_release.signature = Private.sign(tx_release.get_hash(),
                                            node_private)

        # append signed stake release transaction
        block.system_txs.append(tx_release)

        # sign block by one of validators
        signed_block = BlockFactory.sign_block(block, node_private)
        # add signed block to DAG
        dag.add_signed_block(19, signed_block)

        resulting_validators = permissions.get_validators(block.get_hash())
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)
        self.assertNotIn(genesis_validator.public_key, pub_keys)
    def test_hold_stake(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        permissions = Permissions(epoch)
        node_private = Private.generate()

        initial_validators = Validators.read_genesis_validators_from_file()

        genesis_hash = dag.genesis_block().get_hash()
        prev_hash = genesis_hash
        for i in range(1, 9):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                         BLOCK_TIME * 9)

        tx = StakeHoldTransaction()
        tx.amount = 1000
        node_new_private = Private.generate()

        tx.pubkey = Private.publickey(node_new_private)
        tx.signature = Private.sign(tx.get_hash(), node_new_private)

        block.system_txs.append(tx)
        signed_block = BlockFactory.sign_block(block, node_private)
        dag.add_signed_block(9, signed_block)

        resulting_validators = permissions.get_validators(block.get_hash())
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)

        self.assertIn(Private.publickey(node_new_private), pub_keys)
Exemple #12
0
    def __init__(self,
                 genesis_creation_time,
                 node_id,
                 network,
                 block_signer=BlockSigner(Private.generate()),
                 validators=Validators(),
                 behaviour=Behaviour(),
                 logger=DummyLogger()):
        self.logger = logger
        self.dag = Dag(genesis_creation_time)
        self.epoch = Epoch(self.dag)
        self.epoch.set_logger(self.logger)
        self.permissions = Permissions(self.epoch, validators)
        self.mempool = Mempool()
        self.utxo = Utxo(self.logger)
        self.conflict_watcher = ConflictWatcher(self.dag)
        self.behaviour = behaviour

        self.block_signer = block_signer
        self.node_pubkey = Private.publickey(block_signer.private_key)
        self.logger.info("Public key is %s",
                         Keys.to_visual_string(self.node_pubkey))
        self.network = network
        self.node_id = node_id
        self.epoch_private_keys = []  # TODO make this single element
        # self.epoch_private_keys where first element is era number, and second is key to reveal commited random
        self.reveals_to_send = {}
        self.sent_shares_epochs = []  # epoch hashes of secret shares
        self.last_expected_timeslot = 0
        self.last_signed_block_number = 0
        self.tried_to_sign_current_block = False
        self.owned_utxos = []
        self.terminated = False

        self.blocks_buffer = [
        ]  # uses while receive block and do not have its ancestor in local dag (before verify)
    def test_maliciously_send_negative_and_positive_gossip(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] + [2] + [3] + [
            4
        ] + [5] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        signer_index = 0
        for i in Epoch.get_round_range(1, Round.PRIVATE):
            validators.signers_order[i] = signer_index
            signer_index += 1

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())

        network.register_node(node0)

        behavior = Behaviour(
        )  # this node maliciously send positive and negative gossip
        behavior.malicious_send_negative_gossip_count = 1
        behavior.malicious_send_positive_gossip_count = 1
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behavior)
        network.register_node(node1)

        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node2)

        node3 = Node(genesis_creation_time=1,
                     node_id=3,
                     network=network,
                     block_signer=private_keys[3],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node3)

        node4 = Node(genesis_creation_time=1,
                     node_id=4,
                     network=network,
                     block_signer=private_keys[4],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node4)

        node5 = Node(genesis_creation_time=1,
                     node_id=5,
                     network=network,
                     block_signer=private_keys[5],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node5)

        helper = TestHelper(network)

        Time.advance_to_next_timeslot()  # current block number 1
        node0.step()  # create and sign block
        # validate block created and broadcasted
        # validate mempool is empty
        # validate tx by hash is empty
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)
        # validate 2 public key tx
        helper.list_validator(self, network.nodes,
                              ['dag.transactions_by_hash.length'], 1)

        # on one step sends +and- (add test for different steps ?)
        node1.step(
        )  # ! maliciously sand positive and negative gossip (request by genesis 0 block)
        # all node receive positive gossip
        # txs for now only in mempool (not in block)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        # all nodes has 1-gossip and 6+gossips (1-gossip and 6+gossip from (0,1,2,3,4,5))
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              7)
        helper.list_validator(self, network.nodes,
                              ['dag.transactions_by_hash.length'], 1)

        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # after all steps situation same
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              7)
        helper.list_validator(self, network.nodes,
                              ['dag.transactions_by_hash.length'], 1)

        Time.advance_to_next_timeslot()  # current block number 2
        node0.step()  # do nothing
        node1.step(
        )  # is validator by order (need to marge mempool and provide block)
        # in current case node will penaltize SELF !!!
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 3)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)
        # tx_s
        # 3 - public key tx
        # 1 - negative gossip tx
        # 6 - positive gossip txs
        # 1 - penalty tx
        # total = 11 txs
        if ROUND_DURATION > 6:  # total 6 nodes in test
            public_key_tx_count = 6
        else:
            public_key_tx_count = ROUND_DURATION
        negative_gossip_tx_count = 1
        positive_gossips_tx_count = 6
        penalty_tx_count = 1
        tx_total_count = public_key_tx_count + negative_gossip_tx_count + positive_gossips_tx_count + penalty_tx_count

        helper.list_validator(self, network.nodes,
                              ['dag.transactions_by_hash.length'],
                              tx_total_count)

        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # validate that all keeps the same
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 3)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)
        helper.list_validator(self, network.nodes,
                              ['dag.transactions_by_hash.length'],
                              tx_total_count)
        # verify that node1 is steel in validators list
        helper.list_validator(self, network.nodes,
                              ['permissions.epoch_validators.length'],
                              GENESIS_VALIDATORS_COUNT)

        Time.advance_to_next_timeslot()  # current block number 3
        node0.step()  # do nothing
        node1.step()  # do nothing
        node2.step()  # provide block
        node3.step()
        node4.step()
        node5.step()

        # validate new block by node2
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 4)
        # verify that node1 is steel in validators list until epoch end
        helper.list_validator(self, network.nodes,
                              ['permissions.epoch_validators.length'],
                              GENESIS_VALIDATORS_COUNT)

        for i in range(5, ROUND_DURATION * 6 + 1):
            Time.advance_to_next_timeslot()
            if i == ROUND_DURATION * 6 + 1:
                node0.step()
            node0.step()
            node1.step()
            node2.step()
            node3.step()
            node4.step()
            node5.step()
            if i == ROUND_DURATION * 6 + 1:
                # ! chek up validators list on new epoch upcoming
                # TODO sometimes fall for unknoun reason
                # self.list_validator(network.nodes, ['dag.blocks_by_number.length'], i)
                for node in network.nodes:
                    if len(node.dag.blocks_by_number) != i - 1:
                        print('BLOCK_NUMBER : ' + str(i))
                        print('node id:' + str(node.node_id) +
                              " dag.block_by_number:" +
                              str(len(node1.dag.blocks_by_number)))

                helper.list_validator(self, network.nodes,
                                      ['permissions.epoch_validators.length'],
                                      GENESIS_VALIDATORS_COUNT - 1)
                # TODO nodes recalculates 2 times ?
                helper.list_validator(
                    self, network.nodes,
                    ['permissions.epoch_validators.epoch0.length'],
                    GENESIS_VALIDATORS_COUNT - 1)
                # maybe 20 (on default block time and round duration)
                helper.list_validator(
                    self, network.nodes,
                    ['permissions.epoch_validators.epoch1.length'],
                    GENESIS_VALIDATORS_COUNT - 1)
    def test_maliciously_send_positive_gossip(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0, 1, 2, 3, 4, 5] * ROUND_DURATION * 6
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())

        network.register_node(node0)

        behavior = Behaviour()  # this node maliciously send positive gossip
        behavior.malicious_send_positive_gossip_count = 1
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behavior)
        network.register_node(node1)

        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node2)

        node3 = Node(genesis_creation_time=1,
                     node_id=3,
                     network=network,
                     block_signer=private_keys[3],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node3)

        node4 = Node(genesis_creation_time=1,
                     node_id=4,
                     network=network,
                     block_signer=private_keys[4],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node4)

        node5 = Node(genesis_creation_time=1,
                     node_id=5,
                     network=network,
                     block_signer=private_keys[5],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node5)

        helper = TestHelper(network)

        Time.advance_to_next_timeslot()  # current block number 1
        node0.step()  # create and sign block
        # validate block created and broadcasted
        # validate mempool is empty
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)

        node1.step(
        )  # ! maliciously sand positive gossip (request by genesis 0 block)
        # all node receive positive gossip
        # txs for now only in mempool (not in block)
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        # all nodes has 1+gossips
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              1)

        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # after all steps situation same
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              1)

        Time.advance_to_next_timeslot()  # current block number 2
        node0.step()  # do nothing
        node1.step(
        )  # is validator by order (need to marge mempool and provide block)
        # возможно добавить проверку на малишес скип блок в добавок ?
        # (по идеи все должны еще раз обменятся госипами и уже не найти блок 2)
        # (в таком случае следующий валидатор должен смерджить все и отправить блок)
        # (нода 1 должна быть исключена из списка валидаторов ?)
        # after node create and sign block all node clean its mem pool
        # here we have 3 blocks, empty mem pools, and transaction in dag.transaction_by_hash
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 3)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)

        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # validate that all keeps the same
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 3)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)

        Time.advance_to_next_timeslot()  # current block number 3
        node0.step()  # do nothing
        node1.step()  # do nothing
        node2.step()  # provide block
        node3.step()
        node4.step()
        node5.step()

        # validate new block by node2
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 4)
    def test_negative_gossip_by_zeta(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] + [2] + [3] + [
            4
        ] + [5] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node0)

        behavior = Behaviour()  # this node malicious skip block
        behavior.malicious_skip_block = True
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behavior)
        network.register_node(node1)

        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node2)

        node3 = Node(genesis_creation_time=1,
                     node_id=3,
                     network=network,
                     block_signer=private_keys[3],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node3)

        node4 = Node(genesis_creation_time=1,
                     node_id=4,
                     network=network,
                     block_signer=private_keys[4],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node4)

        node5 = Node(genesis_creation_time=1,
                     node_id=5,
                     network=network,
                     block_signer=private_keys[5],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node5)

        helper = TestHelper(network)

        Time.advance_to_next_timeslot()  # current block number 1
        node0.step()  # create and sign block
        node1.step()
        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # validate block created and broadcasted
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)

        Time.advance_to_next_timeslot()  # current block number 1
        node0.step()
        node1.step()  # skip block creation
        node2.step()
        node3.step()
        node4.step()
        node5.step()
        # validate block NOT created and NOT broadcasted
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 2)

        Time.advance_to_next_timeslot()  # current block number 2
        # for now all chain do not have block from previous timeslot
        node0.step()  # broadcast negative gossip
        # all nodes handle negative gossips by node0
        # not broadcast to self (ADD TO MEMPOOL before broadcast)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)  # not permited for gossip send

        node1.step()  # broadcast negative gossip
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              1)

        node2.step(
        )  # broadcast negative gossip AND skip block signing for current step !!!
        node3.step()  # broadcast negative gossip
        node4.step()  # broadcast negative gossip
        node5.step(
        )  # VALIDATE 5 NEGATIVE GOSSIPS AND DO NOT BROADCAST ANOTHER ONE (current ZETA == 5)
        # GOSSIPS may be more - see test_negative_gossips_zata_validators
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              5)

        # duplicate gossips tx will NOT include to mempool !
        # if node already send negative gossip IT NOT broadcast it again !
        # if node already have x < ZETA (x - different negative gossips by block count) IT NOT broadcast it again !
        Time.advance_time(1)  # advance time by one second in current timeslot
        # make steps by nodes
        node0.step()  #
        node1.step()  #
        # steel 5 negative gossips (from 0,1,2,3,4) on all nodes (add validation ?)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              5)

        node2.step(
        )  # CREATE, SIGN, BROADCAST block (block by node1 not exist)

        # all nodes handle new block
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 3)
        # gossips cleaned from mem pool by block handling
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)

        node3.step()  #
        node4.step()  #
        node5.step()  #

        #  provide validation for next normal block and FOR GOSSIPS is NOT in mempool after next block
        Time.advance_to_next_timeslot()  # current block number 3
        node0.step()  #
        node1.step()  #
        node2.step()  #
        node3.step(
        )  # must create and sign and broadcast block (all gossips MUST be mined and erased from mempool)
        node4.step()  #
        node5.step()  #

        # after node2 step
        helper.list_validator(self, network.nodes,
                              ['dag.blocks_by_number.length'], 4)
        helper.list_validator(self, network.nodes, ['mempool.gossips.length'],
                              0)
    def test_send_negative_gossip_by_validator(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] + [2] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node0)

        behavior = Behaviour()
        behavior.transport_cancel_block_broadcast = True
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behavior)
        network.register_node(node1)

        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node2)
        # same config from prev. test

        Time.advance_to_next_timeslot()  # current block number 1
        node0.step()  # create and sign block
        node1.step()
        node2.step()
        self.assertTrue(len(node0.dag.blocks_by_number) == 2, True)
        self.assertTrue(len(node1.dag.blocks_by_number) == 2, True)
        self.assertTrue(len(node2.dag.blocks_by_number) == 2, True)
        # asset that node0 create block number 2
        #
        Time.advance_to_next_timeslot()  # current block number 2
        node0.step()
        node1.step()  # skip broadcasting block
        node2.step()
        self.assertTrue(len(node0.dag.blocks_by_number) == 2, True)
        self.assertTrue(len(node1.dag.blocks_by_number) == 3, True)
        self.assertTrue(len(node2.dag.blocks_by_number) == 2, True)
        # assert that block 3 created on node1 but not broadcasted to node0 and node2
        #
        Time.advance_to_next_timeslot()  # current block number 3
        node2.step(
        )  # MAKE FIRST STEP BY CURRENT TIMESLOT VALIDATOR (BLOCK SIGNER)
        self.assertTrue(len(node0.dag.blocks_by_number) == 3, True)
        self.assertTrue(len(node1.dag.blocks_by_number) == 3, True)
        self.assertTrue(len(node2.dag.blocks_by_number) == 3, True)
        # assert that all listeners nodes receive missed block`s
        Time.advance_time(1)  # ADVANCE TIME BY ONE SECOND TIMESLOT SAME
        node2.step()
        self.assertTrue(len(node0.dag.blocks_by_number) == 4, True)
        self.assertTrue(len(node1.dag.blocks_by_number) == 4, True)
        self.assertTrue(len(node2.dag.blocks_by_number) == 4, True)
        # assert that  node 2 create, sign, broadcast and deliver block number 4
        # for certainty we will make some more steps by NOT VALIDATOR nodes's
        node0.step()
        node1.step()
        node1.step()
        node0.step()
        node0.step()

        Time.advance_to_next_timeslot()  # current block number 4
        node0.step()
        node1.step()
        node2.step()
        # step by all and validate block 5
        self.assertTrue(len(node0.dag.blocks_by_number) == 5, True)
        self.assertTrue(len(node1.dag.blocks_by_number) == 5, True)
        self.assertTrue(len(node2.dag.blocks_by_number) == 5, True)
    def test_node_handle_unavailable(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())

        network.register_node(node0)

        # behaviour flag for disabling node to broadcast
        behaviour = Behaviour()
        behaviour.transport_node_disable_input = True
        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=behaviour)
        network.register_node(node1)

        Time.advance_to_next_timeslot()
        node0.step()  # provide block
        self.assertEqual(len(node0.dag.blocks_by_number),
                         2)  # ensure that node0 provide block to chain
        self.assertEqual(len(node1.dag.blocks_by_number),
                         1)  # ensure that node1 DO NOT receive block
        node1.step()

        Time.advance_to_next_timeslot()
        node0.step()  # do nothing
        node1.step()  # send negative gossip (block from node0 not received)

        self.assertEqual(
            len(node0.mempool.gossips),
            2)  # ensure negative gossip received by node0 (+positive gossip)
        self.assertEqual(len(node1.mempool.gossips),
                         1)  # ensure node1 NOT receive positive gossip

        node0.step()  # provide and block by hash in gossip logic scope
        node1.step(
        )  # node1 do not ask block by hash (cant receive positive gossip due behaviour)
        # for node1 block 1 (created by node 0) is not available due behaviour
        # node1 produce block by step and sand it by broadcast

        self.assertEqual(len(node0.dag.blocks_by_number),
                         3)  # NODE_0 have 2 blocks with genesis ancestor
        self.assertEqual(len(node1.dag.blocks_by_number),
                         2)  # have genesis + self produced block
    def test_send_negative_gossip(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0, 1] * (Epoch.get_duration() // 2)
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()
        behavior = Behaviour()
        behavior.malicious_skip_block = True
        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=behavior)
        network.register_node(node0)

        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node1)

        Time.advance_to_next_timeslot()
        node0.step()
        self.assertEqual(len(node0.dag.blocks_by_number),
                         1)  # ensure that block skipped by node0
        node1.step()
        self.assertEqual(len(node0.dag.blocks_by_number),
                         1)  # ensure that block not received by node1

        Time.advance_to_next_timeslot()
        # on next step node0 broadcast negative gossip
        node0.step()
        # and include! it to (node0) self.mempool
        self.assertEqual(len(node0.mempool.gossips), 1)
        # assume that negative gossip broadcasted and placed to node1 mempool
        self.assertEqual(len(node1.mempool.gossips), 1)
        # -----------------------------------
        # on next step node 1 will send negative gossip
        # node1 MUST create and sign block which contain negative gossip and broadcast it
        node1.step()
        # node1 in it's step broadcast(GOSSIP-) and at the same time SKIP!!! method
        # self.try_to_sign_block(current_block_number)

        # A second step is needed to create and sign a block within the same time slot
        Time.advance_time(
            1
        )  # !!! -----> advance time by 1 second (DO NOT CHANGE TIMESLOT) !!!
        node1.step()
        # -----------------------------------
        # verify that node1 make block broadcast
        self.assertEqual(len(node1.dag.blocks_by_number), 2)
        # verify that node0 receive new block
        self.assertEqual(len(node0.dag.blocks_by_number), 2)
        # verify that negative gossip transaction is in block
        system_txs = node0.dag.blocks_by_number[2][0].block.system_txs
        self.assertTrue(NegativeGossipTransaction.__class__,
                        system_txs[3].__class__)
    def test_release_stake(self):
        # base initialization
        dag = Dag(0)
        epoch = Epoch(dag)
        permissions = Permissions(epoch)
        node_private = Private.generate()

        initial_validators = Validators.read_genesis_validators_from_file()

        genesis_hash = dag.genesis_block().get_hash()
        prev_hash = genesis_hash
        for i in range(1, 9):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                         BLOCK_TIME * 9)

        # create new node for stake hold
        new_node_private = Private.generate()
        new_node_public = Private.publickey(new_node_private)

        # create transaction for stake hold for new node
        tx_hold = StakeHoldTransaction()
        tx_hold.amount = 2000
        tx_hold.pubkey = Keys.to_bytes(new_node_public)
        tx_hold.signature = Private.sign(tx_hold.get_hash(), new_node_private)

        # append signed stake hold transaction
        block.system_txs.append(tx_hold)

        # sign block by one of validators
        signed_block = BlockFactory.sign_block(block, node_private)
        # add signed block to DAG
        dag.add_signed_block(9, signed_block)

        resulting_validators = permissions.get_validators(block.get_hash())
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)
        self.assertIn(new_node_public, pub_keys)

        # add blocks for new epoch
        for i in range(10, 18):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        # create stake release transaction for new stakeholder
        tx_release = StakeReleaseTransaction()
        tx_release.pubkey = Keys.to_bytes(new_node_public)
        tx_release.signature = Private.sign(tx_hold.get_hash(),
                                            new_node_private)

        # append signed stake release transaction
        block.system_txs.append(tx_release)

        # sign block by one of validators
        signed_block = BlockFactory.sign_block(block, node_private)
        # add signed block to DAG
        dag.add_signed_block(19, signed_block)

        # verify that new stake holder now is NOT in validators list (after stake release transaction signed by holder)
        resulting_validators = permissions.get_validators(block.get_hash())
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)
        self.assertNotIn(new_node_public, pub_keys)
    def test_remove_from_validators_by_penalty_gossip(self):
        # base initialization
        dag = Dag(0)
        epoch = Epoch(dag)
        permissions = Permissions(epoch)
        node_private = Private.generate()

        initial_validators = Validators.read_genesis_validators_from_file()

        genesis_hash = dag.genesis_block().get_hash()
        prev_hash = genesis_hash
        for i in range(1, 9):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        # get one of validators
        genesis_validator_private = Private.generate()
        genesis_validator_public = initial_validators[9].public_key

        # put to 10 block gossip+ AND gossip- by one node
        block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                         BLOCK_TIME * 10)

        gossip_negative_tx = NegativeGossipTransaction()
        gossip_negative_tx.pubkey = genesis_validator_public
        gossip_negative_tx.timestamp = Time.get_current_time()
        gossip_negative_tx.number_of_block = 5
        gossip_negative_tx.signature = Private.sign(
            gossip_negative_tx.get_hash(), genesis_validator_private)
        # create and add to block negative gossip
        block.system_txs.append(gossip_negative_tx)

        gossip_positive_tx = PositiveGossipTransaction()
        gossip_positive_tx.pubkey = genesis_validator_public
        gossip_positive_tx.timestamp = Time.get_current_time()
        gossip_positive_tx.block_hash = dag.blocks_by_number[5][0].get_hash()
        gossip_positive_tx.signature = Private.sign(
            gossip_positive_tx.get_hash(), genesis_validator_private)
        # create and add to block positive gossip for same number 5 block
        block.system_txs.append(gossip_positive_tx)

        signed_block = BlockFactory.sign_block(block,
                                               genesis_validator_private)
        dag.add_signed_block(10, signed_block)
        prev_hash = block.get_hash()
        # --------------------------------------------------

        # put to 11 block penalty gossip
        block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                         BLOCK_TIME * 11)
        penalty_gossip_tx = PenaltyGossipTransaction()
        penalty_gossip_tx.timestamp = Time.get_current_time()
        penalty_gossip_tx.conflicts = [
            gossip_positive_tx.get_hash(),
            gossip_negative_tx.get_hash()
        ]
        # set genesis validator for sign penalty gossip
        penalty_gossip_tx.signature = Private.sign(
            penalty_gossip_tx.get_hash(), genesis_validator_private)
        block.system_txs.append(penalty_gossip_tx)

        signed_block = BlockFactory.sign_block(block,
                                               genesis_validator_private)
        dag.add_signed_block(11, signed_block)
        prev_hash = block.get_hash()
        # --------------------------------------------------

        # verify that genesis node is steel in validators list
        current_epoch_hash = epoch.get_epoch_hashes()
        # for now we DO NOT NEED to recalculate validators (send genesis block hash)
        resulting_validators = permissions.get_validators(
            current_epoch_hash.get(prev_hash))
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)
        self.assertIn(genesis_validator_public, pub_keys)

        # produce epoch till end
        from chain.params import ROUND_DURATION
        for i in range(12, (ROUND_DURATION * 6 + 4)):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        # check for new epoch
        self.assertTrue(epoch.is_new_epoch_upcoming(i))
        self.assertTrue(epoch.current_epoch == 2)

        # recalculate validators for last block hash
        resulting_validators = permissions.get_validators(prev_hash)
        pub_keys = []
        for validator in resulting_validators:
            pub_keys.append(validator.public_key)

        self.assertNotIn(genesis_validator_public, pub_keys)
    def test_network_methods(self):
        private_keys = BlockSigners()
        private_keys = private_keys.block_signers
        validators = Validators()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())

        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=Behaviour())

        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=Behaviour())

        node3 = Node(genesis_creation_time=1,
                     node_id=3,
                     network=network,
                     block_signer=private_keys[3],
                     validators=validators,
                     behaviour=Behaviour())

        node4 = Node(genesis_creation_time=1,
                     node_id=4,
                     network=network,
                     block_signer=private_keys[4],
                     validators=validators,
                     behaviour=Behaviour())

        network.register_node(node0)
        network.register_node(node1)
        network.register_node(node2)
        network.register_node(node3)
        network.register_node(node4)

        self.assertEqual(len(network.nodes) == 5, True)

        network.move_nodes_to_group(
            0, [node0, node1])  # create group 0 with nodes 0, 1
        network.move_nodes_to_group(
            1, [node2, node3, node4])  # create group 1 with nodes 2, 3, 4

        self.assertEqual(len(network.groups) == 2, True)
        self.assertEqual(len(network.groups[0]) == 2, True)
        self.assertEqual(len(network.groups[1]) == 3, True)

        network.merge_all_groups()  # test marge groups
        self.assertEqual(len(network.nodes) == 5, True)
    def test_node_offline(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = [0] + [1] + [2] * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node0)

        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node1)

        behaviour = Behaviour()
        behaviour.transport_node_disable_input = True
        behaviour.transport_node_disable_output = True
        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=behaviour)
        network.register_node(node2)  # emulate node total offline

        Time.advance_to_next_timeslot()
        node0.step()  # provide block
        node1.step()
        node2.step()

        self.assertEqual(len(node0.dag.blocks_by_number), 2)
        self.assertEqual(len(node1.dag.blocks_by_number), 2)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         1)  # steel have 1 block

        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()  # provide block
        node2.step()

        self.assertEqual(len(node0.dag.blocks_by_number), 3)
        self.assertEqual(len(node1.dag.blocks_by_number), 3)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         1)  # steel have 1 block

        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()
        node2.step()  # wit for block and try to broadcast negative gossip

        self.assertEqual(len(node0.dag.blocks_by_number), 3)
        self.assertEqual(len(node1.dag.blocks_by_number), 3)
        self.assertEqual(len(node2.dag.blocks_by_number), 1)

        node0.step()
        node1.step()
        node2.step()  # provide block BUT DO NOT BROADCAST

        self.assertEqual(len(node0.dag.blocks_by_number), 3)
        self.assertEqual(len(node1.dag.blocks_by_number), 3)
        self.assertEqual(len(node2.dag.blocks_by_number), 2)
    def test_make_node_offline_from_block(self):
        Time.use_test_time()
        Time.set_current_time(1)

        private_keys = BlockSigners()
        private_keys = private_keys.block_signers

        validators = Validators()
        validators.validators = Validators.read_genesis_validators_from_file()
        validators.signers_order = ([0] + [1] + [2]) * Epoch.get_duration()
        validators.randomizers_order = [0] * Epoch.get_duration()

        network = Network()

        node0 = Node(genesis_creation_time=1,
                     node_id=0,
                     network=network,
                     block_signer=private_keys[0],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node0)

        node1 = Node(genesis_creation_time=1,
                     node_id=1,
                     network=network,
                     block_signer=private_keys[1],
                     validators=validators,
                     behaviour=Behaviour())
        network.register_node(node1)

        behaviour = Behaviour()
        behaviour.transport_keep_offline = [
            4, 6
        ]  # keep offline from 4 block till 6 block
        node2 = Node(genesis_creation_time=1,
                     node_id=2,
                     network=network,
                     block_signer=private_keys[2],
                     validators=validators,
                     behaviour=behaviour)
        network.register_node(
            node2)  # emulate node total offline from 4 block till 6 block

        # ------------------------------- block 1
        Time.advance_to_next_timeslot()
        node0.step()  # provide block
        node1.step()
        node2.step()
        self.assertEqual(len(node0.dag.blocks_by_number), 2)
        self.assertEqual(len(node1.dag.blocks_by_number), 2)
        self.assertEqual(len(node2.dag.blocks_by_number), 2)

        # ------------------------------- block 2
        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()  # provide block
        node2.step()
        self.assertEqual(len(node0.dag.blocks_by_number), 3)
        self.assertEqual(len(node1.dag.blocks_by_number), 3)
        self.assertEqual(len(node2.dag.blocks_by_number), 3)

        # ------------------------------- block 3
        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()
        node2.step()  # provide block
        self.assertEqual(len(node0.dag.blocks_by_number), 4)
        self.assertEqual(len(node1.dag.blocks_by_number), 4)
        self.assertEqual(len(node2.dag.blocks_by_number), 4)

        # ------------------------------- block 4
        # node 2 must set offline on next block
        Time.advance_to_next_timeslot()
        node0.step()  # provide block
        node1.step()
        node2.step()  # AFTER NODE STEP IT MAKES OFFLINE !
        self.assertEqual(len(node0.dag.blocks_by_number), 5)
        self.assertEqual(len(node1.dag.blocks_by_number), 5)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         5)  # RECEIVE BLOCK BEFORE BEHAVIOUR UPDATES

        # ------------------------------- block 5
        # node 2 offline
        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()  # provide block
        node2.step()
        self.assertEqual(len(node0.dag.blocks_by_number), 6)
        self.assertEqual(len(node1.dag.blocks_by_number), 6)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         5)  # DO NOT RECEIVE BLOCK !

        # ------------------------------- block 6
        # node 2 offline
        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()
        node2.step(
        )  # DO NOT RECEIVE BLOCK wait for nex step # + produce but not broadcast negative gossip
        self.assertEqual(len(node0.dag.blocks_by_number), 6)
        self.assertEqual(len(node1.dag.blocks_by_number), 6)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         5)  # DO NOT RECEIVE BLOCK !

        node0.step()
        node1.step()
        # node 2 try to sand negative gossip by block 5 (on offline store it in local mempool and add to block !!!!!!!)
        # NODE_0 AND NODE_1 DO NOT RECEIVE NEGATIVE GOSSIP BY BLOCK 5
        node2.step()  # create and store block localy (steel offline)
        self.assertEqual(len(node0.dag.blocks_by_number), 6)
        self.assertEqual(len(node1.dag.blocks_by_number), 6)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         6)  # node 2 forks chain

        # ------------------------------- block 7 (timeslot)
        # node 2 make online again on step
        Time.advance_to_next_timeslot()
        node0.step(
        )  # provide negative gossip for block 6 before creating and broadcasting block
        node1.step()  # provide negative gossip for block 6
        node2.step(
        )  # current step makes node online (its do not receive gossips from node0 and node1) (variant A)

        self.assertEqual(len(node0.dag.blocks_by_number), 6)
        self.assertEqual(len(node1.dag.blocks_by_number), 6)
        self.assertEqual(len(node2.dag.blocks_by_number), 6)

        # visualization and description block ===========================================
        # DagVisualizer.visualize(node0.dag)
        # DagVisualizer.visualize(node2.dag)
        # on current time nodes have such blocks
        # timeslot[0, 1, 2, 3, 4, 5, 6,     7]
        # ====================================
        # node0 - [0, 1, 2, 3, 4, 5, <>, node0]
        # node1 - [0, 1, 2, 3, 4, 5, <>,      ]
        # node2 - [0, 1, 2, 3, 4, <>, 6,      ]

        # ancessor for block 7 is block 5
        # ancessor for block 5 is block 4

        # node2 request block 5 as ancestor for block 7 (block 6 was skipped till node was offline)
        # node2 received and insert block 5 as conflict to empty timeslot till offline to block 6
        # block 6 was created offline, need to skip all its tx's and softly drop
        # visualization and description block ===========================================

        # node0 - provide block 7
        node0.step()  # create and broadcast block number 7

        # visualization and description block ===========================================
        # DagVisualizer.visualize(node0.dag)  # [0,1,2,3,4,5,6,<>,7]
        # by timeslots                          [           5,<>, 6]
        # DagVisualizer.visualize(node2.dag)  # [0,1,2,3,4,<>, 6, -]
        # visualization and description block ===========================================

        node1.step()  # handle and add block normaly
        node2.step(
        )  # handled all ancestor blocks and inset it to dag with processing included transactions

        self.assertEqual(len(node0.dag.blocks_by_number), 7)
        self.assertEqual(len(node1.dag.blocks_by_number), 7)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         8)  # steel have redundant block 6

        # ------------------------------- block 8 (timeslot)
        # all nodes online
        Time.advance_to_next_timeslot()
        node0.step()
        node1.step()
        node2.step()

        self.assertEqual(len(node0.dag.blocks_by_number), 8)
        self.assertEqual(len(node1.dag.blocks_by_number), 8)
        self.assertEqual(len(node2.dag.blocks_by_number),
                         9)  # steel have redundant block 6