def test_fake_ownership(self):
        # Trying to spend someone else's output
        # (Re)Starting from GenBlock
        genesis_block = classes.GenesisBlock(self.address)
        fullnode_api.add_genesis_block(genesis_block)

        # Tx1 -> We send it to an address belonging to seed
        dict_of_inputs = {genesis_block.block_content[0].txhash: 0}
        dict_of_outputs = {self.address: 100}
        first_tx = classes.Transaction(dict_of_inputs, dict_of_outputs)
        first_tx.sign(self.seed)

        # Block1
        first_block = classes.Block([first_tx])
        first_mined_block = fullnode_api.mine_block(first_block)

        fullnode_api.add_block_to_db(first_mined_block)

        # Tx2 -> we try to spend someone else's output since we sign with seed2
        dict_of_inputs2 = {first_mined_block.block_content[0].txhash: 0}
        dict_of_outputs2 = {self.address3: 100}
        second_tx = classes.Transaction(dict_of_inputs2, dict_of_outputs2)
        second_tx.sign(self.seed2)

        # Block2
        second_block = classes.Block([second_tx])
        second_mined_block = fullnode_api.mine_block(second_block)

        fullnode_api.add_block_to_db(second_mined_block)

        with self.assertRaises(exceptions.ValidationError):
            validation.validate_block(second_mined_block)
    def test_wrong_input(self):
        # Trying to spend inexistant input
        # (Re)Starting from GenBlock
        genesis_block = classes.GenesisBlock(self.address)
        fullnode_api.add_genesis_block(genesis_block)

        # Tx1
        dict_of_inputs = {genesis_block.block_content[0].txhash: 0}
        dict_of_outputs = {self.address2: 100}
        first_tx = classes.Transaction(dict_of_inputs, dict_of_outputs)
        first_tx.sign(self.seed)

        # Block1
        first_block = classes.Block([first_tx])
        first_mined_block = fullnode_api.mine_block(first_block)

        fullnode_api.add_block_to_db(first_mined_block)

        # Tx2 -> we try to spend an unexisting input
        dict_of_inputs2 = {first_mined_block.block_content[0].txhash: 1}
        dict_of_outputs2 = {self.address3: 100}
        second_tx = classes.Transaction(dict_of_inputs2, dict_of_outputs2)
        second_tx.sign(self.seed2)

        # Block2
        second_block = classes.Block([second_tx])
        second_mined_block = fullnode_api.mine_block(second_block)

        fullnode_api.add_block_to_db(second_mined_block)

        with self.assertRaises(
                exceptions.APIError
        ):  # Since the exceptions comes from one level deeper.
            validation.validate_block(second_mined_block)
    def test_incorrect_amounts(self):
        # Trying to spend more than what we have in the inputs
        # (Re)Starting from GenBlock
        genesis_block = classes.GenesisBlock(self.address)
        fullnode_api.add_genesis_block(genesis_block)

        # Tx1
        dict_of_inputs = {genesis_block.block_content[0].txhash: 0}
        dict_of_outputs = {self.address2: 100}
        first_tx = classes.Transaction(dict_of_inputs, dict_of_outputs)
        first_tx.sign(self.seed)

        # Block1
        first_block = classes.Block([first_tx])
        first_mined_block = fullnode_api.mine_block(first_block)

        fullnode_api.add_block_to_db(first_mined_block)

        # Tx2 -> We try to put 101 as output amount
        dict_of_inputs2 = {first_mined_block.block_content[0].txhash: 0}
        dict_of_outputs2 = {self.address3: 101}
        second_tx = classes.Transaction(dict_of_inputs2, dict_of_outputs2)
        second_tx.sign(self.seed2)

        # Block2
        second_block = classes.Block([second_tx])
        second_mined_block = fullnode_api.mine_block(second_block)

        fullnode_api.add_block_to_db(second_mined_block)

        with self.assertRaises(exceptions.ValidationError):
            validation.validate_block(second_mined_block)
    def setUp(self):
        # Seed&Address (2 pairs)
        self.seed = crypto.new_seed()
        self.address = crypto.get_address(self.seed)
        self.verifying_key_string = crypto._get_verifying_key_string(self.seed)

        self.seed2 = crypto.new_seed()
        self.address2 = crypto.get_address(self.seed2)
        self.verifying_key_string2 = crypto._get_verifying_key_string(
            self.seed2)

        self.address3 = self.address2  # For testing purposes only

        # Db
        self.db_path = 'database/db_test'
        database.init_database_path(self.db_path)

        # GenBlock
        self.genesis_block = classes.GenesisBlock(self.address)
        fullnode_api.add_genesis_block(self.genesis_block)

        # Tx1
        self.dict_of_inputs = {self.genesis_block.block_content[0].txhash: 0}
        self.dict_of_outputs = {self.address2: 100}
        self.first_tx = classes.Transaction(self.dict_of_inputs,
                                            self.dict_of_outputs)
        self.first_tx.sign(self.seed)

        # Block1
        self.first_block = classes.Block([self.first_tx])
        self.first_mined_block = fullnode_api.mine_block(self.first_block)

        fullnode_api.add_block_to_db(self.first_mined_block)

        # Tx2
        self.dict_of_inputs2 = {
            self.first_mined_block.block_content[0].txhash: 0
        }
        self.dict_of_outputs2 = {self.address3: 100}
        self.second_tx = classes.Transaction(self.dict_of_inputs2,
                                             self.dict_of_outputs2)
        self.second_tx.sign(self.seed2)

        # Block2
        self.second_block = classes.Block([self.second_tx])
        self.second_mined_block = fullnode_api.mine_block(self.second_block)

        fullnode_api.add_block_to_db(self.second_mined_block)
    def test_double_spend(self):
        # (Re)Starting from GenBlock
        genesis_block = classes.GenesisBlock(self.address)
        fullnode_api.add_genesis_block(genesis_block)

        # Tx1
        dict_of_inputs = {genesis_block.block_content[0].txhash: 0}
        dict_of_outputs = {self.address2: 100}
        first_tx = classes.Transaction(dict_of_inputs, dict_of_outputs)
        first_tx.sign(self.seed)

        # Block1
        first_block = classes.Block([first_tx])
        first_mined_block = fullnode_api.mine_block(first_block)

        fullnode_api.add_block_to_db(first_mined_block)

        # Tx2
        dict_of_inputs2 = {first_mined_block.block_content[0].txhash: 0}
        dict_of_outputs2 = {self.address3: 100}
        second_tx = classes.Transaction(dict_of_inputs2, dict_of_outputs2)
        second_tx.sign(self.seed2)

        # Block2
        second_block = classes.Block([second_tx])
        second_mined_block = fullnode_api.mine_block(second_block)

        fullnode_api.add_block_to_db(second_mined_block)

        # Tx3 -> We try to double spend the same input as we spent in the previous block
        dict_of_inputs3 = {first_mined_block.block_content[0].txhash: 0}
        dict_of_outputs3 = {self.address3: 100}
        third_tx = classes.Transaction(dict_of_inputs3, dict_of_outputs3)
        third_tx.sign(self.seed2)

        third_block = classes.Block([third_tx])
        third_mined_block = fullnode_api.mine_block(third_block)

        with self.assertRaises(exceptions.ValidationError):
            validation.validate_block(third_mined_block)
예제 #6
0
def process(connection, neighbors_selector):
    """This function takes a connection as input and processes the information it yields. It manages the state of the
    fullnode. It requires the neighbors selector to instantiate the gossip."""

    global received_transactions_stack
    global received_databases_stack

    with open('network/config.json') as cfg_file:
        cfg = json.load(cfg_file)

    neighbor_address = cfg["NeighborsInfo"]["neighbor_address"]
    neighbor_port = cfg["NeighborsInfo"]["neighbor_port"]

    # ---------- Clients Processing------------------

    # Processing received transaction
    if hasattr(connection,
               "transaction_received"):  # Is it a ClientConnection ?
        if connection.transaction_received is not None:
            received_transactions_stack.append(
                pickle.loads(connection.transaction_received))
            print("New transaction received.")

    # ---------- Block Creation ------------------
    if len(received_transactions_stack
           ) >= 1:  # A block contains 10 transactions
        print("Creating a new block")
        # We mine a block
        new_block = classes.Block(received_transactions_stack)

        # We check if the transactions of the block are valid
        if validation.validate_block(new_block):
            print("Block is valid, now mining.")
            mined_new_block = fullnode_api.mine_block(new_block)
            fullnode_api.add_block_to_db(mined_new_block)
            received_transactions_stack = []
        else:
            print("Invalid new block, discarding transactions")
            received_transactions_stack = []

        # We start the broadcasting procedure with the serialized database
        database_bytes = pickle.dumps(fullnode_api.get_database())
        fsm.start_gossip(address_tuple=(neighbor_address, neighbor_port),
                         database_bytes=database_bytes,
                         selector=neighbors_selector)

    # ---------- Neighbors Processing------------------

    # Processing received database
    if hasattr(connection,
               "database_received"):  # Is it a NeighborConnection ?
        # Every NeighborConnection has a "database_received" property but it is only different
        # from None when we have successfully received a database message
        if connection.database_received is not None:
            received_databases_stack.append(
                pickle.loads(connection.database_received))
            print("New database received from a neighbor.")

    # Checking if a database has been successfully sent
    if hasattr(connection, "database_sent"):
        # Every NeighborConnection has a "database_sent" property but it is only True
        # when we have successfully sent a database message
        if connection.database_sent:
            # We now know that the sending process is over, we can close the connection and empty stack
            connection.close()
            received_transactions_stack = []

    # Consensus : choosing the longest chain
    if len(received_databases_stack) == 1:
        if len(received_databases_stack[0]) > len(fullnode_api.get_database()):
            # The received database replaces our database
            print("Received database is the longest chain, copying.")
            database.write_to_db(received_databases_stack[0])
        else:
            print("Received database is not the longest chain, discarding.")

        # In any case we empty the stack
        received_databases_stack = []