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
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 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_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])
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_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)