Example #1
0
class AnnouncerNode:
    def __init__(self, genesis_creation_time, logger):
        self.logger = logger
        self.dag = Dag(genesis_creation_time)
        self.epoch = Epoch(self.dag)
        self.epoch.set_logger(self.logger)
        self.logger.info("Starting announcer node")
        self.last_announced_round = None
        self.terminated = False

    def step(self):
        current_block_number = self.epoch.get_current_timeframe_block_number()
        if self.epoch.is_new_epoch_upcoming(current_block_number):
            self.logger.info("New epoch upcoming!")

        current_round = self.epoch.get_round_by_block_number(
            current_block_number)
        if current_round != self.last_announced_round:
            self.logger.info("Starting %s", str(current_round))
            self.last_announced_round = current_round

    async def run(self):
        while True:
            self.step()
            await asyncio.sleep(1)
Example #2
0
 def __init__(self, genesis_creation_time, logger):
     self.logger = logger
     self.dag = Dag(genesis_creation_time)
     self.epoch = Epoch(self.dag)
     self.epoch.set_logger(self.logger)
     self.logger.info("Starting announcer node")
     self.last_announced_round = None
     self.terminated = False
    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_find_epoch_hash_for_block(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        genesis_hash = dag.genesis_block().get_hash()
        genesis_epoch_hash = epoch.find_epoch_hash_for_block(genesis_hash)
        self.assertEqual(genesis_hash, genesis_epoch_hash)

        block = BlockFactory.create_block_with_timestamp([genesis_hash],
                                                         BLOCK_TIME)
        signed_block = BlockFactory.sign_block(block, Private.generate())
        dag.add_signed_block(1, signed_block)
        first_block_hash = block.get_hash()

        first_epoch_hash = epoch.find_epoch_hash_for_block(first_block_hash)
        self.assertEqual(genesis_hash, first_epoch_hash)
    def get_stake_actions(self, epoch_hash):
        epoch_iter = ChainIter(self.epoch.dag, epoch_hash)

        stake_actions = []

        count = 0
        for block in epoch_iter:
            if epoch_iter.block_number == 0:
                break

            if block:
                for tx in block.block.system_txs:
                    if isinstance(tx, StakeHoldTransaction) \
                    or isinstance(tx, StakeReleaseTransaction) \
                    or isinstance(tx, PenaltyTransaction) \
                    or isinstance(tx, PenaltyGossipTransaction):
                        stake_actions.append(tx)

            count += 1
            if count == Epoch.get_duration():
                break

        stake_actions = list(reversed(stake_actions))

        return stake_actions
Example #6
0
    def get_allowed_signers_for_block_number(self, block_number):
        # TODO take cached epoch hashes if block is of lastest epoch
        prev_epoch_number = self.epoch.get_epoch_number(block_number) - 1
        prev_epoch_start = self.epoch.get_epoch_start_block_number(
            prev_epoch_number)
        prev_epoch_end = self.epoch.get_epoch_end_block_number(
            prev_epoch_number)

        # this will extract every unconnected block in epoch, which is practically epoch hash
        # TODO maybe consider blocks to be epoch hashes if they are in final round and consider everything else is orphan
        epoch_hashes = self.dag.get_branches_for_timeslot_range(
            prev_epoch_start, prev_epoch_end + 1)

        if prev_epoch_number == 0:
            epoch_hashes = [self.dag.genesis_block().get_hash()]

        allowed_signers = []
        for epoch_hash in epoch_hashes:
            epoch_block_number = Epoch.convert_to_epoch_block_number(
                block_number)
            allowed_pubkey = self.permissions.get_sign_permission(
                epoch_hash, epoch_block_number).public_key
            allowed_signers.append(allowed_pubkey)

        assert len(allowed_signers) > 0, "No signers allowed to sign block"
        return allowed_signers
Example #7
0
    def try_to_sign_block(self, current_block_number):
        epoch_block_number = Epoch.convert_to_epoch_block_number(
            current_block_number)

        allowed_to_sign = False
        epoch_hashes = self.epoch.get_epoch_hashes()
        for top, epoch_hash in epoch_hashes.items():
            permission = self.permissions.get_sign_permission(
                epoch_hash, epoch_block_number)
            if permission.public_key == self.node_pubkey:
                allowed_to_sign = True
                break

        if allowed_to_sign:
            should_skip_maliciously = self.behaviour.is_malicious_skip_block()
            # first_epoch_ever = self.epoch.get_epoch_number(current_block_number) == 1
            if should_skip_maliciously:  # and not first_epoch_ever: # skip first epoch check
                self.epoch_private_keys.clear()
                self.logger.info("Maliciously skiped block")
            else:
                if self.last_signed_block_number < current_block_number:
                    self.last_signed_block_number = current_block_number
                    self.sign_block(current_block_number)
                else:
                    # skip once more block broadcast in same timeslot
                    pass
Example #8
0
    def step(self):
        current_block_number = self.epoch.get_current_timeframe_block_number()

        if self.epoch.is_new_epoch_upcoming(current_block_number):
            self.epoch.accept_tops_as_epoch_hashes()

        # service method for update node behavior (if behavior is temporary)
        self.behaviour.update(Epoch.get_epoch_number(current_block_number))
        # service method for update transport behavior (if behavior is temporary)
        self.behaviour.update_transport(current_block_number)

        current_round = self.epoch.get_round_by_block_number(
            current_block_number)
        if current_round == Round.PUBLIC:
            self.try_to_publish_public_key(current_block_number)
        elif current_round == Round.SECRETSHARE:
            self.try_to_share_random()
            # elif current_round == Round.PRIVATE:
            # do nothing as private key should be included to block by block signer
        elif current_round == Round.COMMIT:
            self.try_to_commit_random()
        elif current_round == Round.REVEAL:
            self.try_to_reveal_random()
        elif current_round == Round.FINAL:
            # at this point we may remove everything systemic from mempool,
            # so it does not interfere with pubkeys for next epoch
            self.mempool.remove_all_systemic_transactions()

        if self.behaviour.wants_to_hold_stake:
            self.broadcast_stakehold_transaction()
            self.behaviour.wants_to_hold_stake = False

        if self.behaviour.wants_to_release_stake:
            self.broadcast_stakerelease_transaction()
            self.behaviour.wants_to_release_stake = False

        if self.behaviour.malicious_send_negative_gossip_count > 0:
            self.broadcast_gossip_negative(self.last_expected_timeslot)
            self.behaviour.malicious_send_negative_gossip_count -= 1
        if self.behaviour.malicious_send_positive_gossip_count > 0:
            zero_block = self.dag.blocks_by_number[0][
                0].block  # send genesis block malicious
            self.broadcast_gossip_positive(zero_block.get_hash())
            self.behaviour.malicious_send_positive_gossip_count -= 1

        if self.owned_utxos:
            self.broadcast_payments()

        if current_block_number != self.last_expected_timeslot:
            self.tried_to_sign_current_block = False
            should_wait = self.handle_timeslot_changed(
                previous_timeslot_number=self.last_expected_timeslot,
                current_timeslot_number=current_block_number)
            if should_wait:
                return
        if not self.tried_to_sign_current_block:
            self.try_to_sign_block(current_block_number)
            self.tried_to_sign_current_block = True  # will reset in next timeslot
Example #9
0
    def get_random_senders_pubkeys(self, epoch_hash):
        selected_epoch_validators = self.get_validators(epoch_hash)
        epoch_random_indexes = self.get_randomizers_indexes(epoch_hash)
        validators = []
        for i in Epoch.get_round_range(1, Round.SECRETSHARE):
            index = epoch_random_indexes[i - 1]
            validators.append(selected_epoch_validators[index])

        return validators
Example #10
0
    def get_ordered_signers_pubkeys_for_round(self, epoch_hash, round_type):
        selected_epoch_validators = self.get_validators(epoch_hash)

        epoch_signers_indexes = self.get_signers_indexes(epoch_hash)
        validators = []
        for i in Epoch.get_round_range(1, round_type):
            index = epoch_signers_indexes[i - 1]
            validators.append(selected_epoch_validators[index])

        return validators
    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)
Example #12
0
    def insert_verified_block(self, signed_block, allowed_pubkey):
        block = signed_block.block
        block_number = self.epoch.get_block_number_from_timestamp(
            block.timestamp)
        epoch_number = Epoch.get_epoch_number(block_number)

        self.dag.add_signed_block(block_number, signed_block)
        self.mempool.remove_transactions(block.system_txs)
        self.mempool.remove_transactions(block.payment_txs)
        self.utxo.apply_payments(block.payment_txs)
        self.conflict_watcher.on_new_block_by_validator(
            block.get_hash(), epoch_number, allowed_pubkey)
    def test_private_keys_extraction(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        node_private = Private.generate()

        prev_hash = dag.genesis_block().get_hash()
        round_start, round_end = Epoch.get_round_bounds(1, Round.PRIVATE)
        for i in range(1, round_start):
            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()

        generated_private_keys = []
        for i in range(round_start,
                       round_end):  # intentionally skip last block of round
            generated_private = Private.generate()
            generated_private_keys.append(Keys.to_bytes(generated_private))

            private_key_tx = PrivateKeyTransaction()
            private_key_tx.key = Keys.to_bytes(generated_private)
            block = Block()
            block.system_txs = [private_key_tx]
            block.prev_hashes = dag.get_top_blocks_hashes()
            block.timestamp = i * BLOCK_TIME
            signed_block = BlockFactory.sign_block(block, node_private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        ChainGenerator.fill_with_dummies(dag, prev_hash,
                                         Epoch.get_round_range(1, Round.FINAL))

        epoch_hash = dag.blocks_by_number[ROUND_DURATION * 6 + 1][0].get_hash()

        extracted_privates = epoch.get_private_keys_for_epoch(epoch_hash)

        for i in range(0, ROUND_DURATION - 1):
            self.assertEqual(extracted_privates[i], generated_private_keys[i])
Example #14
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_sane_prev_hashes_found(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        genesis_hash = dag.genesis_block().get_hash()

        block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1)
        block_hash2 = ChainGenerator.insert_dummy(dag, [block_hash1], 2)
        _block_hash3 = ChainGenerator.insert_dummy(dag,
                                                   [genesis_hash, block_hash2],
                                                   3)

        verifier = BlockAcceptor(epoch, None)

        with self.assertRaises(AcceptionException):
            verifier.validate_non_ancestor_prev_hashes(
                [genesis_hash, block_hash2])
Example #16
0
 def try_to_send_negative_gossip(self, previous_timeslot_number):
     if previous_timeslot_number not in self.dag.blocks_by_number:
         epoch_block_number = Epoch.convert_to_epoch_block_number(
             previous_timeslot_number)
         allowed_to_send_negative_gossip = False
         epoch_hashes = self.epoch.get_epoch_hashes()
         for _, epoch_hash in epoch_hashes.items():
             permissions = self.permissions.get_gossip_permission(
                 epoch_hash, epoch_block_number)
             for permission in permissions:
                 if permission.public_key == self.node_pubkey:
                     allowed_to_send_negative_gossip = True
                     break
         if allowed_to_send_negative_gossip:
             self.broadcast_gossip_negative(previous_timeslot_number)
         return True
     return False
    def test_sane_prev_hashes_not_found(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        genesis_hash = dag.genesis_block().get_hash()

        block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1)
        block_hash2 = ChainGenerator.insert_dummy(dag, [block_hash1], 2)
        block_hash3 = ChainGenerator.insert_dummy(dag, [genesis_hash], 3)
        _block_hash4 = ChainGenerator.insert_dummy(dag,
                                                   [block_hash2, block_hash3],
                                                   4)

        verifier = BlockAcceptor(epoch, None)

        try:
            verifier.validate_non_ancestor_prev_hashes(
                [block_hash2, block_hash3])
        except:
            self.fail("Prev hashes should not be self referential")
Example #18
0
    def get_ordered_randomizers_pubkeys_for_round(self, epoch_hash,
                                                  round_type):
        selected_epoch_validators = self.get_validators(epoch_hash)

        if round_type == Round.PUBLIC or round_type == Round.PRIVATE:
            epoch_random_indexes = self.get_signers_indexes(epoch_hash)
            round_type = Round.PRIVATE

            validators = []
            for i in Epoch.get_round_range(1, round_type):
                index = epoch_random_indexes[i - 1]
                validators.append(selected_epoch_validators[index])

            return validators

        elif round_type == Round.SECRETSHARE:
            return self.get_secret_sharers(epoch_hash)
        elif round_type == Round.COMMIT or round_type == Round.REVEAL:
            return self.get_commiters(epoch_hash)

        assert False, "No randomizers exist for this round type!"
    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)
Example #21
0
    def get_allowed_signers_for_next_block(self, block):
        current_block_number = self.epoch.get_current_timeframe_block_number()
        epoch_block_number = Epoch.convert_to_epoch_block_number(
            current_block_number)
        if self.epoch.is_new_epoch_upcoming(current_block_number):
            self.epoch.accept_tops_as_epoch_hashes()
        epoch_hashes = self.epoch.get_epoch_hashes()
        allowed_signers = []
        for prev_hash in block.prev_hashes:
            epoch_hash = None
            if prev_hash in epoch_hashes:
                epoch_hash = epoch_hashes[prev_hash]
            else:
                epoch_hash = self.epoch.find_epoch_hash_for_block(prev_hash)

            if epoch_hash:
                # self.logger.info("Calculating permissions from epoch_hash %s", epoch_hash.hex())
                allowed_pubkey = self.permissions.get_sign_permission(
                    epoch_hash, epoch_block_number)
                allowed_signers.append(allowed_pubkey)

        assert len(
            allowed_signers) > 0, "No signers allowed to sign next block"
        return allowed_signers
    def test_secret_sharing_rounds(self):
        dag = Dag(0)
        epoch = Epoch(dag)

        dummy_private = Private.generate()

        signers = []
        for i in range(0, ROUND_DURATION + 1):
            signers.append(Private.generate())

        private_keys = []

        block_number = 1
        genesis_hash = dag.genesis_block().get_hash()
        prev_hash = genesis_hash
        signer_index = 0
        for i in Epoch.get_round_range(1, Round.PUBLIC):
            private = Private.generate()
            private_keys.append(private)

            signer = signers[signer_index]
            pubkey_tx = PublicKeyTransaction()
            pubkey_tx.generated_pubkey = Private.publickey(private)
            pubkey_tx.pubkey_index = signer_index
            pubkey_tx.signature = Private.sign(
                pubkey_tx.get_signing_hash(genesis_hash), signer)

            block = Block()
            block.timestamp = i * BLOCK_TIME
            block.prev_hashes = [prev_hash]
            block.system_txs = [pubkey_tx]
            signed_block = BlockFactory.sign_block(block, signer)
            dag.add_signed_block(i, signed_block)
            signer_index += 1
            prev_hash = block.get_hash()

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.COMMIT))

        public_keys = []
        for private in private_keys:
            public_keys.append(Private.publickey(private))

        randoms_list = []
        expected_random_pieces = []
        for i in Epoch.get_round_range(1, Round.SECRETSHARE):
            random_bytes = os.urandom(32)
            random_value = int.from_bytes(random_bytes, byteorder='big')
            split_random_tx = SplitRandomTransaction()
            splits = split_secret(random_bytes, 2, 3)
            encoded_splits = encode_splits(splits, public_keys)
            split_random_tx.pieces = encoded_splits
            split_random_tx.pubkey_index = 0
            expected_random_pieces.append(split_random_tx.pieces)
            split_random_tx.signature = Private.sign(pubkey_tx.get_hash(),
                                                     dummy_private)
            block = Block()
            block.timestamp = i * BLOCK_TIME
            block.prev_hashes = [prev_hash]
            block.system_txs = [split_random_tx]
            signed_block = BlockFactory.sign_block(block, dummy_private)
            dag.add_signed_block(i, signed_block)
            randoms_list.append(random_value)
            prev_hash = block.get_hash()

        expected_seed = sum_random(randoms_list)

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.REVEAL))

        signer_index = 0
        private_key_index = 0
        raw_private_keys = []
        for i in Epoch.get_round_range(1, Round.PRIVATE):
            private_key_tx = PrivateKeyTransaction()
            private_key_tx.key = Keys.to_bytes(private_keys[private_key_index])
            raw_private_keys.append(private_key_tx.key)
            signer = signers[signer_index]
            block = Block()
            block.system_txs = [private_key_tx]
            block.prev_hashes = [prev_hash]
            block.timestamp = block_number * BLOCK_TIME
            signed_block = BlockFactory.sign_block(block, signer)
            dag.add_signed_block(i, signed_block)
            signer_index += 1
            private_key_index += 1
            prev_hash = block.get_hash()

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.FINAL))

        top_block_hash = dag.get_top_blocks_hashes()[0]

        random_splits = epoch.get_random_splits_for_epoch(top_block_hash)
        self.assertEqual(expected_random_pieces, random_splits)

        restored_randoms = []
        for i in range(0, len(random_splits)):
            random = decode_random(random_splits[i],
                                   Keys.list_from_bytes(raw_private_keys))
            restored_randoms.append(random)

        self.assertEqual(randoms_list, restored_randoms)

        seed = epoch.extract_shared_random(top_block_hash)
        self.assertEqual(expected_seed, seed)
    def test_top_blocks(self):
        dag = Dag(0)
        epoch = Epoch(dag)
        private = Private.generate()

        epoch_hash = dag.genesis_block().get_hash()

        self.assertEqual(dag.genesis_block().get_hash(),
                         list(epoch.get_epoch_hashes().keys())[0])
        self.assertEqual(dag.genesis_block().get_hash(),
                         list(epoch.get_epoch_hashes().values())[0])

        block1 = BlockFactory.create_block_with_timestamp(
            [dag.genesis_block().get_hash()], BLOCK_TIME)
        signed_block1 = BlockFactory.sign_block(block1, private)
        dag.add_signed_block(1, signed_block1)

        self.assertEqual(block1.get_hash(),
                         list(epoch.get_epoch_hashes().keys())[0])
        self.assertEqual(epoch_hash,
                         list(epoch.get_epoch_hashes().values())[0])

        prev_hash = block1.get_hash()
        epoch_length = ROUND_DURATION * 6 + 1
        for i in range(2, epoch_length + 1):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        if epoch.is_new_epoch_upcoming(epoch_length + 1):
            epoch.accept_tops_as_epoch_hashes()

        top_block_hash = dag.blocks_by_number[epoch_length][0].get_hash()
        epoch_hash = dag.blocks_by_number[epoch_length][0].get_hash()

        self.assertEqual(top_block_hash,
                         list(epoch.get_epoch_hashes().keys())[0])
        self.assertEqual(epoch_hash,
                         list(epoch.get_epoch_hashes().values())[0])

        epoch2 = epoch_length * 2 + 1
        for i in range(epoch_length + 1, epoch2):
            block = BlockFactory.create_block_with_timestamp([prev_hash],
                                                             BLOCK_TIME * i)
            signed_block = BlockFactory.sign_block(block, private)
            dag.add_signed_block(i, signed_block)
            prev_hash = block.get_hash()

        if epoch.is_new_epoch_upcoming(epoch2):
            epoch.accept_tops_as_epoch_hashes()

        top_block_hash = dag.blocks_by_number[epoch2 - 1][0].get_hash()
        epoch_hash = dag.blocks_by_number[epoch2 - 1][0].get_hash()

        self.assertEqual(top_block_hash,
                         list(epoch.get_epoch_hashes().keys())[0])
        self.assertEqual(epoch_hash,
                         list(epoch.get_epoch_hashes().values())[0])
    def test_commit_reveal(self):
        dag = Dag(0)
        epoch = Epoch(dag)

        private = Private.generate()

        prev_hash = ChainGenerator.fill_with_dummies(
            dag,
            dag.genesis_block().get_hash(),
            Epoch.get_round_range(1, Round.PUBLIC))

        randoms_list = []
        for i in Epoch.get_round_range(1, Round.COMMIT):
            random_value = int.from_bytes(os.urandom(32), byteorder='big')
            randoms_list.append(random_value)

        expected_seed = sum_random(randoms_list)

        reveals = []

        epoch_hash = dag.genesis_block().get_hash()

        for i in Epoch.get_round_range(1, Round.COMMIT):
            rand = randoms_list.pop()
            random_bytes = rand.to_bytes(32, byteorder='big')
            commit, reveal = TestEpoch.create_dummy_commit_reveal(
                random_bytes, epoch_hash)
            commit_block = BlockFactory.create_block_with_timestamp(
                [prev_hash], i * BLOCK_TIME)
            commit_block.system_txs = [commit]
            signed_block = BlockFactory.sign_block(commit_block, private)
            dag.add_signed_block(i, signed_block)
            prev_hash = commit_block.get_hash()
            reveals.append(reveal)

            revealing_key = Keys.from_bytes(reveal.key)
            encrypted_bytes = Public.encrypt(random_bytes,
                                             Private.publickey(revealing_key))
            decrypted_bytes = Private.decrypt(encrypted_bytes, revealing_key)
            # TODO check if encryption decryption can work million times in a row
            self.assertEqual(decrypted_bytes, random_bytes)

            revealed_value = Private.decrypt(commit.rand, revealing_key)
            self.assertEqual(revealed_value, random_bytes)

        # self.assertEqual(len(reveals), ROUND_DURATION)

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.SECRETSHARE))

        for i in Epoch.get_round_range(1, Round.REVEAL):
            reveal_block = BlockFactory.create_block_with_timestamp(
                [prev_hash], i * BLOCK_TIME)
            reveal_block.system_txs = [reveals.pop()]
            signed_block = BlockFactory.sign_block(reveal_block, private)
            dag.add_signed_block(i, signed_block)
            prev_hash = reveal_block.get_hash()

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.PRIVATE))

        prev_hash = ChainGenerator.fill_with_dummies(
            dag, prev_hash, Epoch.get_round_range(1, Round.FINAL))

        seed = epoch.reveal_commited_random(prev_hash)
        self.assertEqual(expected_seed, seed)
    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(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_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)