Example #1
0
    def random_block_generator(genesis, initial_leaf_number, block_number):
        """
        Randomly generates blocks according to the given parameters.
        """
        max_initial_leaf_number = initial_leaf_number
        max_block_number = block_number

        all_blocks = set()
        all_blocks.add(hash(genesis))
        leaves = set()
        block_counter = 1
        yield genesis

        while block_counter <= max_block_number:
            if block_counter <= max_initial_leaf_number + 1:
                parents = {hash(genesis)}
            else:
                parents = frozenset(
                    sample(leaves, randint(1, max(1, round(len(leaves) / 5)))))
            cur_block = Block(block_counter, parents)

            leaves -= cur_block.get_parents()
            leaves.add(hash(cur_block))
            all_blocks.add(hash(cur_block))
            block_counter += 1
            yield cur_block
Example #2
0
    def add(self, block: Block, is_malicious: bool = False):
        super().add(block)

        global_id = hash(block)
        if is_malicious:
            self._malicious_blocks_to_add_to_honest_dag.append(global_id)

            if self.did_attack_fail():
                self._first_parallel_block_gid = global_id

            # The malicious attack generates a chain, so the new tip is the current block
            self._competing_chain_tip_gid = global_id
            self._competing_chain_tip_antipast -= self._G.node[global_id][self._BLUE_DIFF_PAST_ORDER_KEY].keys()
            self._competing_chain_tip_antipast -= self._G.node[global_id][self._RED_DIFF_PAST_ORDER_KEY].keys()

            # Because we are under the assumption that a selfish miner has zero network latency and the
            # simulation design, the assumption is that no new blocks are mined between the moment a new
            # selfish block is mined and the moment it is added to the DAG
            self._virtual_competing_chain_block_parents = \
                self._get_competing_chain_tip_parents(global_id,
                                                      self._competing_chain_tip_antipast,
                                                      block.get_parents())
        else:
            # Add malicious blocks to the honest DAG as soon as possible
            if self.did_attack_fail():
                self._add_malicious_blocks_to_honest_dag()

            # This is possible because this is a competing chain attack,
            # where the honest chain doesn't include any malicious blocks
            self._honest_dag.add(block)

        if self.did_attack_succeed():
            self._add_malicious_blocks_to_honest_dag()

        if not self.did_attack_fail():
            # need to update the data structure only if in the middle of a (seemingly) successful attack
            self._competing_chain_tip_antipast.add(global_id)
            if global_id == self._competing_chain_tip_gid or \
                    self._is_a_bluer_than_b(self._competing_chain_tip_gid, global_id):
                self._virtual_competing_chain_block_parents -= block.get_parents()
                self._virtual_competing_chain_block_parents.add(global_id)
            elif not self._is_attack_viable():
                self._stop_attack()
Example #3
0
    def test_blocks_with_missing_parents(self, miner, genesis):
        """
        Tests adding a fork with missing parents.
        """
        # Creating a fork that looks like this:
        # 0 <- 1 <- 2 <- 3
        # 0 <- 1 <- 4 <- 5
        block1 = Block(uuid.uuid4().int, {hash(genesis)})
        block2 = Block(uuid.uuid4().int, {hash(block1)})
        block3 = Block(uuid.uuid4().int, {hash(block2)})
        block4 = Block(uuid.uuid4().int, {hash(block1)})
        block5 = Block(uuid.uuid4().int, {hash(block4)})

        # Add the blocks in the following order:
        # 3 (and then the miner will fetch 2)
        # 4 (and then the miner will fetch 1)
        # 5
        # 1 (and then the miner will add 4, 5)
        # 2 (and then the miner will add 3)

        assert miner.add_block(block3) is False
        assert hash(block3) not in miner

        assert miner.add_block(block4) is False
        assert hash(block4) not in miner

        assert miner.add_block(block5) is False
        assert hash(block5) not in miner

        assert miner.add_block(block1) is True
        assert hash(block1) in miner

        assert hash(block4) in miner
        assert hash(block5) in miner
        assert hash(block2) not in miner
        assert hash(block3) not in miner

        assert miner.add_block(block2) is True
        assert hash(block2) in miner
        assert hash(block3) in miner
Example #4
0
 def mine_block(self) -> Union[Block, None]:
     """
     :return: the mined block or None if mining was unsuccessful.
     """
     gid = hash(uuid.uuid4().int)
     block = Block(global_id=gid,
                   parents=self._dag.get_virtual_block_parents().copy(),
                   size=self._block_size,  # assume for the simulation's purposes that blocks are maximal
                   data=self._name)  # use the data field to hold the miner's name for better logs
     if not self.add_block(block):
         return None
     if not self._broadcast_added_blocks:
         # The block will be broadcast by _basic_block_add
         self._broadcast_block(block)
     self._mined_blocks_gids.add(gid)
     return block
Example #5
0
    def add(self, block: Block):
        global_id = hash(block)
        parents = block.get_parents()

        # add the block to the phantom
        self._G.add_node(global_id)
        self._G.node[global_id][self._BLOCK_DATA_KEY] = block
        for parent in parents:
            self._G.add_edge(global_id, parent)

        # update the leaves
        self._leaves -= parents
        self._leaves.add(global_id)

        # update the coloring of the graph and everything related (the blue anticones and the topological order too)
        self._update_coloring_incrementally(global_id)
        self._update_topological_order_incrementally(global_id)
Example #6
0
    def test_coloring_advanced(self, greedy_dag, genesis, block1, block2,
                               block3, block4, block5, block6, block7, block8,
                               block9):
        """
        Tests coloring a big DAG.
        """
        greedy_dag.add(genesis)
        greedy_dag.add(block1)
        greedy_dag.add(block2)
        greedy_dag.add(block3)

        # gids: 0 <- 1, 2 <- 3
        # gids: 0 <- 4
        greedy_dag.add(block4)
        assert greedy_dag._k_chain == ({
            hash(genesis), hash(block1),
            hash(block3)
        }, 0)

        # gids: 0 <- 1, 2 <- 3
        # gids: 0 <- 4 <- 5
        greedy_dag.add(block5)
        assert greedy_dag._k_chain == ({
            hash(genesis), hash(block1),
            hash(block3)
        }, 0)

        # gids: 0 <- 1, 2 <- 3
        # gids: 0 <- 4 <- 5 <- 6
        greedy_dag.add(block6)
        assert greedy_dag._k_chain == ({
            hash(genesis), hash(block1),
            hash(block3)
        }, 0)

        # test colorings for various K values
        greedy_dag.set_k(0)
        assert greedy_dag._coloring_tip_gid == hash(block6)
        assert greedy_dag._k_chain == ({hash(block6)}, 3)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block4),
            hash(block5),
            hash(block6)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block4),
            hash(block5),
            hash(block6)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag,
            {hash(genesis),
             hash(block4),
             hash(block5),
             hash(block6)})

        greedy_dag.set_k(1)
        assert greedy_dag._coloring_tip_gid == hash(block3)
        assert greedy_dag._k_chain == ({hash(block3)}, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis), hash(block1),
            hash(block3)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag,
            {hash(genesis),
             hash(block1),
             hash(block2),
             hash(block3)})

        greedy_dag.set_k(2)
        assert greedy_dag._coloring_tip_gid == hash(block3)
        assert greedy_dag._k_chain == ({hash(block1), hash(block3)}, 1)
        assert greedy_dag._coloring_chain == {
            hash(genesis), hash(block1),
            hash(block3)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag,
            {hash(genesis),
             hash(block1),
             hash(block2),
             hash(block3)})

        greedy_dag.set_k(3)
        assert greedy_dag._coloring_tip_gid == hash(block3)
        assert greedy_dag._k_chain == ({
            hash(genesis), hash(block1),
            hash(block3)
        }, 0)
        assert greedy_dag._coloring_chain == {
            hash(genesis), hash(block1),
            hash(block3)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6)
            })

        greedy_dag.set_k(4)
        assert greedy_dag._coloring_tip_gid == hash(block3)
        assert greedy_dag._k_chain == ({
            hash(genesis), hash(block1),
            hash(block3)
        }, 0)
        assert greedy_dag._coloring_chain == {
            hash(genesis), hash(block1),
            hash(block3)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6)
            })

        # gids: 0 <- 1, 2 <- 3 <- 7
        # gids: 0 <- 4 <- 5 <- 6
        greedy_dag.add(block7)
        assert greedy_dag._k_chain == ({
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7)
        }, 0)

        # gids: 0 <- 1, 2 <- 3 <- 7 <- 8
        # gids: 0 <- 4 <- 5 <- 6
        greedy_dag.add(block8)
        assert greedy_dag._k_chain == ({
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8)
        }, 1)

        # gids: 0 <- 1, 2 <- 3 <- 7 <- 8 <- 9
        # gids: 0 <- 4 <- 5 <- 6
        greedy_dag.add(block9)
        assert greedy_dag._k_chain == ({
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 2)

        # test colorings for various K values
        greedy_dag.set_k(0)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({hash(block9)}, 5)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(1)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({hash(block8), hash(block9)}, 4)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(2)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block7), hash(block8),
            hash(block9)
        }, 3)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(3)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(4)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(5)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 1)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(6)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 0)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        # gids: 0 <- 1, 2 <- 3 <- 7 <- 8 <- 9
        # gids: 0 <- 1, 2 <- 3 <- 7 <- 10
        # gids: 0 <- 4 <- 5 <- 6 <- 10
        block10 = Block(10, {hash(block6), hash(block7)})
        greedy_dag.add(block10)

        greedy_dag.set_k(0)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({hash(block9)}, 5)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(1)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({hash(block8), hash(block9)}, 4)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9)
            })

        greedy_dag.set_k(2)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block7), hash(block8),
            hash(block9)
        }, 3)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(3)
        assert greedy_dag._coloring_tip_gid == hash(block9)
        assert greedy_dag._k_chain == ({
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(4)
        assert greedy_dag._coloring_tip_gid == hash(block10)
        assert greedy_dag._k_chain == ({hash(block7), hash(block10)}, 3)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(5)
        assert greedy_dag._coloring_tip_gid == hash(block10)
        assert greedy_dag._k_chain == ({
            hash(block3), hash(block7),
            hash(block10)
        }, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(6)
        assert greedy_dag._coloring_tip_gid == hash(block10)
        assert greedy_dag._k_chain == ({
            hash(block3), hash(block7),
            hash(block10)
        }, 2)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(7)
        assert greedy_dag._coloring_tip_gid == hash(block10)
        assert greedy_dag._k_chain == ({
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }, 1)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })

        greedy_dag.set_k(8)
        assert greedy_dag._coloring_tip_gid == hash(block10)
        assert greedy_dag._k_chain == ({
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }, 0)
        assert greedy_dag._coloring_chain == {
            hash(genesis),
            hash(block1),
            hash(block3),
            hash(block7),
            hash(block10)
        }
        assert greedy_dag._get_coloring() == {
            hash(genesis),
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block4),
            hash(block5),
            hash(block6),
            hash(block7),
            hash(block8),
            hash(block9),
            hash(block10)
        }
        TestGreedyColoring.assert_coloring(
            greedy_dag, {
                hash(genesis),
                hash(block1),
                hash(block2),
                hash(block3),
                hash(block4),
                hash(block5),
                hash(block6),
                hash(block7),
                hash(block8),
                hash(block9),
                hash(block10)
            })
Example #7
0
 def block5(self, block4):
     # graph should look like this: 0 <- 2 <- 4 <- 5
     return Block(hash(block4) + 1, {hash(block4)})
Example #8
0
    def test_complex_attack(self, competing_chain_greedy_dag, genesis, block1,
                            block2, block3):
        """
        Tests a complex attack scenario.
        """
        competing_chain_greedy_dag.add(genesis)

        # gids: 0 <- 1
        competing_chain_greedy_dag.add(block1)

        # gids: 0 <- 1
        # gids: 0 <- 4
        block4 = Block(
            4,
            competing_chain_greedy_dag.get_virtual_block_parents(
                is_malicious=True))
        competing_chain_greedy_dag.add(block4, is_malicious=True)
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block4)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block4)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        assert competing_chain_greedy_dag.get_virtual_block_parents(
            is_malicious=True) == {hash(block4)}
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block4)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block4)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        # gids: 0 <- 1, 2
        # gids: 0 <- 4
        competing_chain_greedy_dag.add(block2)
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block4)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block2),
            hash(block4)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        assert competing_chain_greedy_dag.get_virtual_block_parents(
            is_malicious=True) == {hash(block4)}
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block4)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block2),
            hash(block4)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        # gids: 0 <- 1, 2
        # gids: 0 <- 4 <- 5
        block5 = Block(
            5,
            competing_chain_greedy_dag.get_virtual_block_parents(
                is_malicious=True))
        competing_chain_greedy_dag.add(block5, is_malicious=True)
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block5)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block2),
            hash(block5)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        assert competing_chain_greedy_dag.get_virtual_block_parents(
            is_malicious=True) == {hash(block1),
                                   hash(block2),
                                   hash(block5)}
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block5)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1), hash(block2),
            hash(block5)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed(
        )  # block1 is still unconfirmed

        # gids: 0 <- 1, 2 <- 3
        # gids: 0 <- 4 <- 5
        competing_chain_greedy_dag.add(block3)
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block5)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block5)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        assert competing_chain_greedy_dag.get_virtual_block_parents(
            is_malicious=True) == {hash(block1),
                                   hash(block2),
                                   hash(block5)}
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block5)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block1),
            hash(block2),
            hash(block3),
            hash(block5)
        }
        assert not competing_chain_greedy_dag.did_attack_succeed()

        # gids: 0 <- 1, 2 <- 3
        # gids: 0 <- 1, 2 <- 6
        # gids: 0 <- 4 <- 5 <- 6
        block6 = Block(
            6,
            competing_chain_greedy_dag.get_virtual_block_parents(
                is_malicious=True))
        competing_chain_greedy_dag.add(block6, is_malicious=True)
        assert competing_chain_greedy_dag._currently_attacked_block_gid == hash(
            block1)
        assert competing_chain_greedy_dag._competing_chain_tip_gid == hash(
            block6)
        assert competing_chain_greedy_dag._competing_chain_tip_antipast == {
            hash(block3), hash(block6)
        }
        assert competing_chain_greedy_dag.did_attack_succeed()
Example #9
0
def block7(block3, block6):
    # graph should look like this: 3 <- 7
    return Block(hash(block6) + 1, {hash(block3)})
Example #10
0
def block6(block5):
    # graph should look like this: 5 <- 6
    return Block(hash(block5) + 1, {hash(block5)})
Example #11
0
 def block3(self, block1, block2):
     # graph should look like this: 0 <- 1 <- 3
     return Block(hash(block2) + 1, {hash(block1)})
Example #12
0
 def block4(self, block2, block3):
     # graph should look like this: 0 <- 2 <- 4
     return Block(hash(block3) + 1, {hash(block2)})
Example #13
0
class Network:
    """
    The network connecting the various miners.
    """

    # Dictionary key for the miner data of each miner
    _MINER_KEY = 'miner'

    # Dictionary key for the hash rate of each miner
    _HASH_RATE_KEY = 'hash_rate'

    # Dictionary key for the edge weight
    _EDGE_WEIGHT_KEY = 'weight'

    # The default genesis block
    _GENESIS_BLOCK = Block(global_id=0, size=0, data="Genesis")

    def __init__(self,
                 propagation_delay_parameter: int = 0,
                 median_speed: Block.BlockSize = 1 << 20,
                 no_delay_for_malicious_miners: bool = True,
                 completely_connected_malicious_miners: bool = True,
                 total_network_dag: DAG = None,
                 simulation: "Simulation" = None):
        """
        Initializes the network.
        """
        self._network_graph = nx.DiGraph()

        self._propagation_delay_parameter = propagation_delay_parameter
        self._median_speed = median_speed
        self._no_delay_for_malicious_miners = no_delay_for_malicious_miners
        self._completely_connected_malicious_miners = completely_connected_malicious_miners
        self._simulation = simulation

        self._total_network_dag = total_network_dag
        self._total_network_dag.add(self._GENESIS_BLOCK)

        self._removed_miners: Set[str] = set()
        self._malicious_miner_names: Set[str] = set()

    def __contains__(self, miner_name: "Miner.Name") -> bool:
        return miner_name in self._network_graph

    def __getitem__(self, miner_name: "Miner.Name") -> "Miner":
        if miner_name not in self._network_graph:
            return None
        return self._network_graph.node[miner_name][self._MINER_KEY]

    def __iter__(self) -> Iterator["Miner.Name"]:
        return iter(self._network_graph)

    def __len__(self) -> int:
        return len(self._network_graph)

    @staticmethod
    def get_random_ip() -> "Miner.Name":
        """
        :return: a random IP address as a string.
        """
        return ".".join(str(random.randint(0, 255)) for _ in range(4))

    def get_random_miner(self, according_to_hash_rate: bool = True) -> "Miner":
        """
        :return: a random miner, randomness is distributed according to the given parameter.
        """
        miners = []
        hash_rates = []
        total_hash_rate = 0
        for miner_name in self:
            miners.append(miner_name)
            miner_hash_rate = self._network_graph.node[miner_name][self._HASH_RATE_KEY]
            hash_rates.append(miner_hash_rate)
            total_hash_rate += miner_hash_rate

        if according_to_hash_rate:
            miner_name = numpy.random.choice(miners, p=numpy.array(hash_rates) / total_hash_rate)
        else:
            miner_name = numpy.random.choice(miners)
        return self[miner_name]

    def add_miner(self,
                  miner: "Miner",
                  hash_rate: float,
                  is_malicious: bool = False,
                  discover_peers: bool = True):
        """
        Adds a miner with the given hash rate to the network.
        """
        miner_name = miner.get_name()
        self._network_graph.add_node(miner_name)
        self._network_graph.node[miner_name][Network._MINER_KEY] = miner
        self._network_graph.node[miner_name][Network._HASH_RATE_KEY] = hash_rate

        miner.set_network(self)
        miner.add_block(self._GENESIS_BLOCK)

        if is_malicious:
            self._malicious_miner_names.add(miner_name)

        if discover_peers:
            for miner_name in self:
                self[miner_name].discover_peers()

    def remove_miner(self, name: "Miner.Name"):
        """
        Removes a miner from the network.
        """
        peer_names = set(self._network_graph.predecessors(name))
        self._removed_miners.add(self[name])
        self._network_graph.remove_node(name)

        if name in self._malicious_miner_names:
            self._malicious_miner_names.remove(name)

        for peer_name in peer_names:
            self[peer_name].discover_peers()

    def discover_peers(self, miner_name: "Miner.Name", max_peer_num: Union[int, float]):
        """
        :return: a random collection of miner_num miner names.
        """
        new_peers = set()
        old_peers = set(self._network_graph.neighbors(miner_name)) | {miner_name}
        while len(new_peers) < min(len(self._network_graph) - len(old_peers), max_peer_num - len(old_peers) + 1):
            potential_peer = random.choice(list(self._network_graph.nodes()))
            if potential_peer not in old_peers:
                new_peers.add(potential_peer)

        return new_peers

    def _is_there_delay(self, miner_name: "Miner.Name") -> bool:
        """
        :return: True iff the given miner suffers from network delay.
        """
        return not ((miner_name in self._malicious_miner_names) and self._no_delay_for_malicious_miners)

    def _get_delay(self, sender_name: "Miner.Name", recipient_name: "Miner.Name") -> float:
        """
        :return: if there is an edge between the miners, the actual delay between them.
        If not, generates a random delay.
        """
        if self._network_graph.has_edge(sender_name, recipient_name):
            return self._network_graph[sender_name][recipient_name][self._EDGE_WEIGHT_KEY]

        return numpy.random.poisson(self._propagation_delay_parameter) * self._is_there_delay(sender_name) * \
            self._is_there_delay(recipient_name)

    def add_peers(self, miner_name: "Miner.Name", peers: Set["Miner.Name"]):
        """
        Adds the given miners as peers to the given miner.
        """
        self._network_graph.add_weighted_edges_from(
            [(miner_name, peer_name, self._get_delay(miner_name, peer_name)) for peer_name in (peers - {miner_name})])

    def remove_peers(self, miner_name: "Miner.Name", peers: Iterable["Miner.Name"]):
        """
        Removes the given miners as peers to the given miner.
        """
        self._network_graph.remove_edges_from([(miner_name, peer) for peer in peers])

    def attack_success(self):
        """
        A method that allows miners to notify the network of a successful attack.
        """
        self._simulation.attack_success()

    def add_block(self, block: Block):
        """
        Adds the given block to the total network DAG.
        """
        if hash(block) not in self._total_network_dag:
            self._total_network_dag.add(block)

    def send_block(self, sender_name: "Miner.Name", recipient_name: "Miner.Name", block: Block):
        """
        Sends the given block to the given miner.
        """
        if sender_name not in self._network_graph or recipient_name not in self._network_graph:
            return

        delay_lambda = round(self._get_delay(sender_name, recipient_name) * sys.getsizeof(block) / self._median_speed)
        delay_time = min(numpy.random.poisson(delay_lambda), self._propagation_delay_parameter)
        self._simulation.send_block(sender_name, recipient_name, block, delay_time)

    def broadcast_block(self, miner_name: "Miner.Name", block: Block):
        """
        Broadcasts the given block from the given miner to its peers.
        """
        self.add_block(block)

        peers = set(self._network_graph.neighbors(miner_name))
        if self._completely_connected_malicious_miners:
            if miner_name in self._malicious_miner_names:
                peers |= self._network_graph.nodes()
            else:
                peers |= self._malicious_miner_names

        for peer_name in peers:
            self.send_block(miner_name, peer_name, block)

    def fetch_block(self, miner_name: "Miner.Name", gid: Block.GlobalID):
        """
        :return: retrieves the block with the given global id from the network for the given miner.
        """
        for peer in self._network_graph.neighbors(miner_name):
            self.send_block(peer, miner_name, self._total_network_dag[gid])

    def draw_total_network_dag(self, with_labels: bool = False):
        """
        :return: draws the "total" DAG of all the blocks in the network.
        """
        self._total_network_dag.draw(with_labels=with_labels)

    def draw_network(self, with_labels: bool = False):
        """
        Draws the network.
        """
        plt.figure()
        pos = nx.spectral_layout(self._network_graph)
        nx.draw_networkx(self._network_graph, pos=pos, font_size=8)
        if with_labels:
            edge_labels = nx.get_edge_attributes(self._network_graph, self._EDGE_WEIGHT_KEY)
            nx.draw_networkx_edge_labels(self._network_graph, pos=pos, edge_labels=edge_labels, font_size=8)
        plt.show()

    def __str__(self):
        """
        :return: a string representation of the network.
        """
        network_params = ', '.join([
            "network info: delay lambda: " + str(self._propagation_delay_parameter),
            "median speed: " + str(self._median_speed),
            "no delay for malicious miners: " + str(self._no_delay_for_malicious_miners),
            "malicious miners are completely connected: " +
            str(self._completely_connected_malicious_miners) + ".\n"
        ])
        network_graph_str = "Network graph: " + str(list(self._network_graph.edges(data=True))) + "\n"
        total_dag_str = "Total network DAG: " + str(self._total_network_dag) + ".\n"
        miners_str = "Active miners in the network:\n" + \
                     '\n'.join([
                         str(self[miner_name]) +
                         ", hash rate: " + str(self._network_graph.node[miner_name][self._HASH_RATE_KEY]) + ", "
                         + str(len(self[miner_name].get_mined_blocks()) / len(self._total_network_dag)) +
                         " of network blocks. Its peers are: " +
                         ', '.join([str(peer_name) + " with delay: " +
                                    str(self._network_graph[miner_name][peer_name][self._EDGE_WEIGHT_KEY])
                                    for peer_name in self._network_graph.neighbors(miner_name)])
                         for miner_name in self])
        return network_params + network_graph_str + total_dag_str + miners_str + "\n"
Example #14
0
def block2(genesis, block1):
    # graph should look like this: 0 <- 2
    return Block(hash(block1) + 1, {hash(genesis)})
Example #15
0
def block1(genesis):
    # graph should look like this: 0 <- 1
    return Block(hash(genesis) + 1, {hash(genesis)})
Example #16
0
def block9(block8):
    # graph should look like this: 8 <- 9
    return Block(hash(block8) + 1, {hash(block8)})
Example #17
0
def block8(block7):
    # graph should look like this: 7 <- 8
    return Block(hash(block7) + 1, {hash(block7)})
Example #18
0
def genesis():
    return Block()
Example #19
0
def block5(block4):
    # graph should look like this: 4 <- 5
    return Block(hash(block4) + 1, {hash(block4)})
Example #20
0
def block3(block1, block2):
    # graph should look like this: 1, 2 <- 3
    return Block(hash(block2) + 1, {hash(block1), hash(block2)})
Example #21
0
 def block6(self, block3, block5):
     # graph should look like this: 0 <- 1 <- 3 <- 6
     return Block(hash(block5) + 1, {hash(block3)})
Example #22
0
def block4(genesis, block3):
    # graph should look like this: 0 <- 4
    return Block(hash(block3) + 1, {hash(genesis)})
Example #23
0
 def genesis(self):
     return Block()