Exemple #1
0
    def calculate_merkle_root(self):
        """ Gets the Merkle root hash for a given list of transactions.

        This method is incomplete!  Right now, it only hashes the
        transactions together, which does not enable the same type
        of lite client support a true Merkle hash would.

        Returns:
            str: Merkle hash of the list of transactions in a block, uniquely identifying the list.
        """
        # Placeholder for (1c)
        all_hashes = [sha256_2_string(str(tx)) for tx in self.transactions]

        if len(all_hashes) == 0:
            return ""

        new_lst = all_hashes
        while len(new_lst) > 1:
            next_lst = []
            if len(new_lst) % 2 == 0:  # even
                for i in range(0, len(new_lst), 2):
                    next_lst.append(
                        sha256_2_string("".join([new_lst[i], new_lst[i + 1]])))
            elif len(new_lst) % 2 != 0:  # odd
                for i in range(0, len(new_lst) - 1, 2):
                    next_lst.append(
                        sha256_2_string("".join([new_lst[i], new_lst[i + 1]])))
                next_lst.append(
                    sha256_2_string("".join([new_lst[-1], new_lst[-1]])))
            new_lst = next_lst
        return new_lst[0]
Exemple #2
0
 def test_valid_merkle_three_leafs(self):
     tx1 = Transaction([], [TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1)])
     tx2 = Transaction([tx1.hash + ":1"],
                       [TransactionOutput("Alice", "Bob", .9), TransactionOutput("Alice", "Carol", 0)])
     tx3 = Transaction([tx2.hash + ":0"], [TransactionOutput("Bob", "Bob", .8)])
     transactions = [tx1, tx2, tx3]
     block = TestBlock(0, transactions, "genesis", is_genesis=True)
     expected_value = sha256_2_string(sha256_2_string(str(tx1)) + sha256_2_string(str(tx2)))
     expected_value = sha256_2_string(expected_value + sha256_2_string(str(tx3)))
     self.assertEqual(block.calculate_merkle_root(), expected_value)
Exemple #3
0
 def list_to_merkle_hash(self, list1):
     if len(list1) == 0:
         return sha256_2_string("")
     elif len(list1) == 1:
         return sha256_2_string(str(list1[0]))
     else:
         first_list = list1[0:int(len(list1) / 2)]
         second_list = list1[int(len(list1) / 2):]
         return sha256_2_string(
             self.list_to_merkle_hash(first_list) +
             self.list_to_merkle_hash(second_list))
Exemple #4
0
 def test_double_sha256(self):
     self.assertEqual(
         sha256_2_string("Data test"),
         "95cb8ec3a627b3b25902c7d38ca7e51a3d54ad99df0302e93e899337b8e73b2e")
     self.assertEqual(
         sha256_2_string("Data test 2"),
         "bd069191dbc430b9627617c4480a5ec6d25106ad278de785509510ab2f5b2eff")
     self.assertEqual(
         sha256_2_string("whee"),
         "53447992e1eea5d4dea2d74ce6e4e911022c7222d07246c1ff9a47f83853ad6a")
     self.assertEqual(
         sha256_2_string(""),
         "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456")
Exemple #5
0
    def test_valid_merkle_one_leaf(self):

        tx1 = Transaction([], [TransactionOutput("Alice", "Bob", 1), TransactionOutput("Alice", "Alice", 1)])
        transactions = [tx1]
        block = TestBlock(0, transactions, "genesis", is_genesis=True)
        expected_value = sha256_2_string(str(tx1))
        self.assertEqual(block.calculate_merkle_root(), expected_value)
    def calculate_hash(self):
        """ Get the SHA256^2 hash of the block header.

        Returns:
            str: SHA256^2 hash of self.header()
        """
        return sha256_2_string(str(self.header()))
Exemple #7
0
    def calculate_merkle_root(self):
        """ Gets the Merkle root hash for a given list of transactions.

        This method is incomplete!  Right now, it only hashes the
        transactions together, which does not enable the same type
        of lite client support a true Merkle hash would.

        Follow the description in the problem sheet to calculte the merkle root. 
        If there is no transaction, return SHA256(SHA256("")).
        If there is only one transaction, the merkle root is the transaction hash.
        For simplicity, when computing H(AB) = SHA256(SHA256(H(A) + H(B))), directly double-hash the hex string of H(A) concatenated with H(B).
        E.g., H(A) = 0x10, H(B) = 0xff, then H(AB) = SHA256(SHA256("10ff")).

        Returns:
            str: Merkle root hash of the list of transactions in a block, uniquely identifying the list.
        """
        # Placeholder for (1c)

        trans_hash = []
        for each_trans in self.transactions:
            trans_hash.append(sha256_2_string(str(each_trans)))

        if len(trans_hash) == 0:
            return sha256_2_string("")

        if len(trans_hash) == 1:
            return trans_hash[0]

        while not (len(trans_hash) == 1):
            temp = []
            if len(trans_hash) % 2 == 0:
                for i in range(0, len(trans_hash), 2):
                    temp.append(
                        sha256_2_string("".join(
                            [trans_hash[i], trans_hash[i + 1]])))
            elif len(trans_hash) % 2 == 1:
                temp.append(
                    sha256_2_string("".join(trans_hash[-1] + trans_hash[-1])))
            trans_hash = temp

        return trans_hash[0]
Exemple #8
0
    def calculate_merkle_root(self):
        """ Gets the Merkle root hash for a given list of transactions.

        This method is incomplete!  Right now, it only hashes the
        transactions together, which does not enable the same type
        of lite client support a true Merkle hash would.

        Returns:
            str: Merkle hash of the list of transactions in a block, uniquely identifying the list.
        """
        all_txs_as_string = "".join([str(x) for x in self.transactions])
        return sha256_2_string(all_txs_as_string)
Exemple #9
0
 def test_blockchain_hash(self):
     tx1 = Transaction([], [
         TransactionOutput("Alice", "Bob", 1),
         TransactionOutput("Alice", "Alice", 1)
     ])
     tx2 = Transaction([tx1.hash + ":0"], [
         TransactionOutput("Alice", "Bob", 1),
         TransactionOutput("Alice", "Carol", 1)
     ])
     block = TestBlock(0, [tx1, tx2], "genesis", is_genesis=True)
     block.set_dummy_timestamp()
     self.assertEqual(
         sha256_2_string(block.header()),
         "9fc4ae4f2e6a68a0e79a57c4491b03a72f9a4bcdbc6ab7213e0f9334d800c57d")
Exemple #10
0
    def calculate_merkle_root(self):
        hash_trans = [sha256_2_string(str(x)) for x in self.transactions]

        if len(hash_trans) == 0:
            hash_trans = sha256_2_string("")

        while len(hash_trans) > 1:

            i = 0
            hash_trans_nex = []

            while i < len(hash_trans):
                if i == len(hash_trans) - 1:
                    hash_trans_nex.append((hash_trans[i]))

                else:
                    hash_trans_nex.append(
                        sha256_2_string(hash_trans[i] + hash_trans[i + 1]))
                    i += 1
                i += 1
            hash_trans = hash_trans_nex

        return hash_trans[0]
Exemple #11
0
 def test_blockchain_hash(self):
     tx1 = Transaction([], [
         TransactionOutput("Alice", "Bob", 1),
         TransactionOutput("Alice", "Alice", 1)
     ])
     tx2 = Transaction([tx1.hash + ":0"], [
         TransactionOutput("Alice", "Bob", 1),
         TransactionOutput("Alice", "Carol", 1)
     ])
     block = TestBlock(0, [tx1, tx2], "genesis", is_genesis=True)
     block.set_dummy_vals()
     self.assertEqual(
         sha256_2_string(block.header()),
         "b0d35a2ffb46c6a8f48658d77f990656f7a9ec753b77342eb3b0e6a1d7acf934")
Exemple #12
0
    def calculate_merkle_root(self):
        """ Gets the Merkle root hash for a given list of transactions.

        This method is incomplete!  Right now, it only hashes the
        transactions together, which does not enable the same type
        of lite client support a true Merkle hash would.

        Follow the description in the problem sheet to calculte the merkle root. 
        If there is no transaction, return SHA256(SHA256("")).
        If there is only one transaction, the merkle root is the transaction hash.
        For simplicity, when computing H(AB) = SHA256(SHA256(H(A) + H(B))), directly double-hash the hex string of H(A) concatenated with H(B).
        E.g., H(A) = 0x10, H(B) = 0xff, then H(AB) = SHA256(SHA256("10ff")).

        Returns:
            str: Merkle root hash of the list of transactions in a block, uniquely identifying the list.
        """

        hashed_tx = []

        for x in self.transactions:
            hashed_tx.append(sha256_2_string(str(x)))

        if len(hashed_tx) == 0:
            return sha256_2_string("")
        elif len(hashed_tx) == 1:
            return hashed_tx[0]
        else:
            # handling if merkle tree is uneven and greater than 1: https://bitcoin.stackexchange.com/questions/79364/are-number-of-transactions-in-merkle-tree-always-even
            if len(hashed_tx) % 2 != 0:
                hashed_tx.append(hashed_tx[-1])
            while len(hashed_tx) > 1:
                holder = []
                for i, k in zip(hashed_tx[0::2], hashed_tx[1::2]):
                    holder.append(sha256_2_string(i + k))
                hashed_tx = holder
            return hashed_tx[0]
Exemple #13
0
    def __init__(
        self,
        height,
        transactions,
        parent_hash,
        is_genesis=False,
        include_merkle_root=True,
    ):
        """ Creates a block template (unsealed).

        Args:
            height (int): height of the block in the chain (# of blocks between block and genesis).
            transactions (:obj:`list` of :obj:`Transaction`): ordered list of transactions in the block.
            parent_hash (str): the hash of the parent block in the blockchain.
            is_genesis (bool, optional): True only if the block is a genesis block.

        Attributes:
            parent_hash (str): the hash of the parent block in blockchain.
            height (int): height of the block in the chain (# of blocks between block and genesis).
            transactions (:obj:`list` of :obj:`Transaction`): ordered list of transactions in the block.
            timestamp (int): Unix timestamp of the block.
            target (int): Target value for the block's seal to be valid (different for each seal mechanism)
            is_genesis (bool): True only if the block is a genesis block (first block in the chain).
            merkle (str): Merkle hash of the list of transactions in a block, uniquely identifying the list.
            seal_data (int): Seal data for block (in PoW this is the nonce satisfying the PoW puzzle; in PoA, the signature of the authority").
            hash (str): Hex-encoded SHA256^2 hash of the block header (self.header()).
        """
        self.parent_hash = parent_hash
        self.height = height
        self.transactions = transactions
        self.timestamp = int(time.time())
        self.target = self.calculate_appropriate_target()
        self.is_genesis = is_genesis

        if include_merkle_root:
            self.merkle = self.calculate_merkle_root()
        else:
            self.merkle = sha256_2_string("".join(
                [str(x) for x in self.transactions]))

        self.seal_data = 0  # temporarily set seal_data to 0
        self.hash = self.calculate_hash(
        )  # keep track of hash for caching purposes
    def is_valid(self):
        """ Check whether block is fully valid according to block rules.

        Includes checking for no double spend, that all transactions are valid, that all header fields are correctly
        computed, etc.

        Returns:
            bool, str: True if block is valid, False otherwise plus an error or success message.
        """

        chain = blockchain.chain  # This object of type Blockchain may be useful
        # Placeholder for (1a)

        # (checks that apply to all blocks)
        # Check that Merkle root calculation is consistent with transactions in block (use the calculate_merkle_root function) [test_rejects_invalid_merkle]
        # On failure: return False, "Merkle root failed to match"
        if not self.merkle == self.calculate_merkle_root():
            return False, "Merkle root failed to match"

        # Check that block.hash is correctly calculated [test_rejects_invalid_hash]
        # On failure: return False, "Hash failed to match"
        if not self.hash == sha256_2_string(str(self.header())):
            return False, "Hash failed to match"

        # Check that there are at most 900 transactions in the block [test_rejects_too_many_txs]
        # On failure: return False, "Too many transactions"
        if len(self.transactions) > 900:
            return False, "Too many transactions"

        # (checks that apply to genesis block)
        # Check that height is 0 and parent_hash is "genesis" [test_invalid_genesis]
        # On failure: return False, "Invalid genesis"
        if (self.height == 0 and self.parent_hash != "genesis") or (
                self.parent_hash == "genesis" and self.height != 0):
            return False, "Invalid genesis"

        # (checks that apply only to non-genesis blocks)
        # Check that parent exists (you may find chain.blocks helpful) [test_nonexistent_parent]
        # On failure: return False, "Nonexistent parent"
        if self.height != 0 and self.parent_hash != "genesis":
            if self.parent_hash not in chain.blocks:
                return False, "Nonexistent parent"

            # Check that height is correct w.r.t. parent height [test_bad_height]
            # On failure: return False, "Invalid height"
            if self.height != (chain.blocks[self.parent_hash].height + 1):
                return False, "Invalid height"

            # Check that timestamp is non-decreasing [test_bad_timestamp]
            # On failure: return False, "Invalid timestamp"
            if self.timestamp < (chain.blocks[self.parent_hash].timestamp):
                return False, "Invalid timestamp"

            # Check that seal is correctly computed and satisfies "target" requirements; use the provided is_valid_seal method [test_bad_seal]
            # On failure: return False, "Invalid seal"
            if not self.seal_is_valid():
                return False, "Invalid seal"

            # Check that all transactions within are valid (use tx.is_valid) [test_malformed_txs]
            # On failure: return False, "Malformed transaction included"
            for transac in self.transactions:
                if not transac.is_valid():
                    return False, "Malformed transaction included"

            # Check that for every transaction
            tx_map = {}
            for tx in self.transactions:
                if tx.hash in chain.blocks_containing_tx:
                    for bk_hash in chain.blocks_containing_tx[tx.hash]:
                        if chain.blocks[bk_hash].height != self.height:
                            return False, "Double transaction inclusion"
                # the transaction has not already been included on a block on the same blockchain as this block [test_double_tx_inclusion_same_chain]
                if tx.hash in tx_map:
                    return False, "Double transaction inclusion"
                else:
                    tx_map[tx.hash] = tx
                # (or twice in this block; you will have to check this manually) [test_double_tx_inclusion_same_block]
                # (you may find chain.get_chain_ending_with and chain.blocks_containing_tx and util.nonempty_intersection useful)

            input_ref_set = set([])
            for tx in self.transactions:
                # for every input ref in the tx
                sender = []
                in_val = 0
                out_val = 0

                for input_ref in tx.input_refs:
                    input_ref_split = input_ref.split(':', 1)
                    input_tx_hash = input_ref_split[0]
                    output_idx = int(input_ref_split[1])

                    if (input_tx_hash
                            not in chain.all_transactions) and (input_tx_hash
                                                                not in tx_map):
                        return False, "Required output not found"
                    if input_tx_hash in chain.all_transactions:
                        if output_idx >= len(
                                chain.all_transactions[input_tx_hash].outputs):
                            return False, "Required output not found"
                        else:
                            input_tran = chain.all_transactions[
                                input_tx_hash].outputs[output_idx]
                    elif input_tx_hash in tx_map:
                        if output_idx >= len(tx_map[input_tx_hash].outputs):
                            return False, "Required output not found"
                        else:
                            input_tran = tx_map[input_tx_hash].outputs[
                                output_idx]

                    in_val += input_tran.amount

                    # (you may find the string split method for parsing the input into its components)
                    # each input_ref is valid (aka corresponding transaction can be looked up in its holding transaction) [test_failed_input_lookup]
                    # (you may find chain.all_transactions useful here)
                    # On failure: return False, "Required output not found"

                    if len(sender) == 0:
                        sender.append(input_tran.receiver)
                    else:
                        if input_tran.receiver != sender[0]:
                            return False, "User inconsistencies"
                    # every input was sent to the same user (would normally carry a signature from this user; we leave this out for simplicity) [test_user_consistency]
                    # On failure: return False, "User inconsistencies"

                    # no input_ref has been spent in a previous block on this chain [test_doublespent_input_same_chain]
                    if input_ref in chain.blocks_spending_input:
                        fork = False
                        for bk_hash in chain.blocks_spending_input[input_ref]:
                            if chain.blocks[bk_hash].height == self.height:
                                fork = True
                        if not fork:
                            return False, "Double-spent input"
                    # (or in this block; you will have to check this manually) [test_doublespent_input_same_block]
                    # (you may find nonempty_intersection and chain.blocks_spending_input helpful here)
                    if input_ref in input_ref_set:
                        return False, "Double-spent input"
                    else:
                        input_ref_set.add(input_ref)
                    # On failure: return False, "Double-spent input"

                    input_tx_fnd = False
                    if input_tx_hash in tx_map:
                        input_tx_fnd = True
                    elif input_tx_hash in chain.blocks_containing_tx:
                        for bk_hash in chain.blocks_containing_tx[
                                input_tx_hash]:
                            if chain.blocks[bk_hash].height < self.height:
                                input_tx_fnd = True

                    if not input_tx_fnd:
                        return False, "Input transaction not found"

                    # each input_ref points to a transaction on the same blockchain as this block [test_input_txs_on_chain]
                    # (or in this block; you will have to check this manually) [test_input_txs_in_block]
                    # (you may find chain.blocks_containing_tx.get and nonempty_intersection as above helpful)
                    # On failure: return False, "Input transaction not found"

                # for every output in the tx
                for tran_output in tx.outputs:
                    if sender and (tran_output.sender != sender[0]):
                        return False, "User inconsistencies"
                    # every output was sent from the same user (would normally carry a signature from this user; we leave this out for simplicity)
                    # (this MUST be the same user as the outputs are locked to above) [test_user_consistency]
                    # On failure: return False, "User inconsistencies"
                    out_val += tran_output.amount

                # the sum of the input values is at least the sum of the output values (no money created out of thin air) [test_no_money_creation]
                # On failure: return False, "Creating money"
                if in_val < out_val:
                    return False, "Creating money"

        return True, "All checks passed"
Exemple #15
0
 def two_strings_to_merkle_hash(self, str1, str2):
     return sha256_2_string(str1 + str2)
Exemple #16
0
 def test_valid_merkle_empty(self):
     block = TestBlock(0, [], "genesis", is_genesis=True)
     expected_value = sha256_2_string("")
     self.assertEqual(block.calculate_merkle_root(), expected_value)
Exemple #17
0
 def hash_pair_of_nodes(l):
     hashed = []
     for i in range(0, l, 2):
         hashed.append(sha256_2_string(l[i] + l[i + 1]))
     return hashed
Exemple #18
0
    def is_valid(self):
        """ Check whether block is fully valid according to block rules.

        Includes checking for no double spend, that all transactions are valid, that all header fields are correctly
        computed, etc.

        Returns:
            bool, str: True if block is valid, False otherwise plus an error or success message.
        """

        chain = blockchain.chain  # This object of type Blockchain may be useful

        # Placeholder for (1a)

        # (checks that apply to all blocks)

        # Check that Merkle root calculation is consistent with transactions in block (use the calculate_merkle_root function) [test_rejects_invalid_merkle]
        # On failure: return False, "Merkle root failed to match"
        if self.calculate_merkle_root() != self.merkle:
            return False, "Merkle root failed to match"

        # Check that block.hash is correctly calculated [test_rejects_invalid_hash]
        # On failure: return False, "Hash failed to match"
        if self.hash != sha256_2_string(str(self.header())):
            return False, "Hash failed to match"

        # Check that there are at most 900 transactions in the block [test_rejects_too_many_txs]
        # On failure: return False, "Too many transactions"
        if len(self.transactions) > 900:
            return False, "Too many transactions"

        # (checks that apply to genesis block)
        # Check that height is 0 and parent_hash is "genesis" [test_invalid_genesis]
        # On failure: return False, "Invalid genesis"
        if self.is_genesis:
            if self.height != 0 or self.parent_hash != "genesis" or not self.seal_is_valid(
            ):
                return False, "Invalid genesis"
        else:
            # (checks that apply only to non-genesis blocks)
            # Check that parent exists (you may find chain.blocks helpful) [test_nonexistent_parent]
            # On failure: return False, "Nonexistent parent"
            if chain.blocks.get(self.parent_hash) is None:
                return False, "Nonexistent parent"

            # Check that height is correct w.r.t. parent height [test_bad_height]
            # On failure: return False, "Invalid height"
            parent = chain.blocks.get(self.parent_hash)
            if self.height != parent.height + 1:
                return False, "Invalid height"

            # Check that timestamp is non-decreasing [test_bad_timestamp]
            # On failure: return False, "Invalid timestamp"
            if self.timestamp < parent.timestamp:
                return False, "Invalid timestamp"

            # Check that seal is correctly computed and satisfies "target" requirements; use the provided seal_is_valid method [test_bad_seal]
            # On failure: return False, "Invalid seal"
            if not self.seal_is_valid():
                return False, "Invalid seal"

            # Check that all transactions within are valid (use tx.is_valid) [test_malformed_txs]
            # On failure: return False, "Malformed transaction included"
            for tx in self.transactions:
                if not tx.is_valid():
                    return False, "Malformed transaction included"

            # If above works, this should work?
            # if any([x.is_valid() for x in self.transactions]) is False:
            #    return False, "Malformed transaction included"

            block_inp_user = {}
            block_tx_out_inp_user = {}
            block_tx_out_out_user = {}

            block_tx_inp_receivers = {}
            # Check that for every transaction
            for tx in self.transactions:

                for output in tx.outputs:
                    block_tx_out_inp_user[output.sender] = output.sender
                    block_tx_out_out_user[output.receiver] = output.receiver

                # the transaction has not already been included on a block on the same blockchain as this block [test_double_tx_inclusion_same_chain]
                # (or twice in this block; you will have to check this manually) [test_double_tx_inclusion_same_block]
                # (you may find chain.get_chain_ending_with and chain.blocks_containing_tx and util.nonempty_intersection useful)
                # On failure: return False, "Double transaction inclusion"

                current_block = chain.blocks.get(self.parent_hash)

                if current_block:
                    trans_in_parent = False
                    while current_block is not None:
                        tx_to_check = current_block.is_tx_present(tx)
                        if tx_to_check:
                            return False, "Double transaction inclusion"
                        current_block = chain.blocks.get(
                            current_block.parent_hash)

                tx_count = 0
                for tx_in_block in self.transactions:
                    if tx_in_block.hash == tx.hash:
                        tx_count = tx_count + 1

                if tx_count > 1:
                    return False, "Double transaction inclusion"

                # for every input ref in the tx
                input_ref_transaction = {}

                total_input = 0

                for input_ref in tx.input_refs:

                    # (you may find the string split method for parsing the input into its components)
                    tx_hash, list_index_of_output = input_ref.split(':')

                    # each input_ref is valid (aka corresponding transaction can be looked up in its holding transaction) [test_failed_input_lookup]
                    # (you may find chain.all_transactions useful here)
                    # On failure: return False, "Required output not found"
                    tx_temp = chain.all_transactions.get(tx_hash)

                    if tx_temp is None:
                        tx_exists = False
                        for tx_in in self.transactions:
                            if tx_in.hash == tx_hash:
                                tx_exists = True

                        if not tx_exists:
                            return False, "Required output not found"
                    else:
                        if int(list_index_of_output) > len(tx.input_refs):
                            return False, "Required output not found"

                        total_input = total_input + tx_temp.get_output_at(
                            int(list_index_of_output))
                        tx_rec = tx_temp.get_receiver_at(
                            int(list_index_of_output))
                        block_tx_inp_receivers[tx_rec] = tx_rec

                    # every input was sent to the same user (would normally carry a signature from this user; we leave this out for simplicity) [test_user_consistency]
                    # On failure: return False, "User inconsistencies"

                    # no input_ref has been spent in a previous block on this chain [test_doublespent_input_same_chain]
                    # (or in this block; you will have to check this manually) [test_doublespent_input_same_block]
                    # (you may find nonempty_intersection and chain.blocks_spending_input helpful here)
                    # On failure: return False, "Double-spent input"
                    current_block = chain.blocks.get(self.parent_hash)

                    while current_block.is_genesis is False:
                        if current_block.is_inp_ref_present(input_ref):
                            return False, "Double-spent input"
                        current_block = chain.blocks.get(
                            current_block.parent_hash)

                    inp_ref_count = 0
                    for tx_in_block in self.transactions:
                        for tx_input_ref in tx_in_block.input_refs:
                            if input_ref == tx_input_ref:
                                inp_ref_count = inp_ref_count + 1

                    if inp_ref_count > 1:
                        return False, "Double-spent input"

                    # each input_ref points to a transaction on the same blockchain as this block [
                    # test_input_txs_on_chain] (or in this block; you will have to check this manually) [
                    # test_input_txs_in_block] (you may find chain.blocks_containing_tx.get and nonempty_intersection
                    # as above helpful) On failure: return False, "Input transaction not found"
                    current_block = chain.blocks.get(self.parent_hash)

                    if current_block:
                        trans_in_parent = False
                        while current_block is not None:
                            tx_to_check = chain.all_transactions.get(tx_hash)
                            if tx_to_check and current_block.is_tx_present(
                                    tx_to_check):
                                trans_in_parent = True
                            current_block = chain.blocks.get(
                                current_block.parent_hash)

                        if not trans_in_parent:
                            for tx_in_block in self.transactions:
                                if tx_in_block.hash == tx_hash:
                                    trans_in_parent = True

                        if not trans_in_parent:
                            return False, "Input transaction not found"

                # for every output in the tx
                # every output was sent from the same user (would normally carry a signature from this user; we leave this out for simplicity)
                # (this MUST be the same user as the outputs are locked to above) [test_user_consistency]
                # On failure: return False, "User inconsistencies"
                inp_users = {}
                for output in tx.outputs:
                    inp_users[output.sender] = output.sender

                total_output = tx.get_total_output()

                if len(inp_users) > 1:
                    return False, "User inconsistencies"

                if len(block_tx_inp_receivers) > 0 and len(
                        block_tx_out_inp_user) > 0:
                    if len(block_tx_inp_receivers) > 1:
                        return False, "User inconsistencies"

                    user_received_inp = block_tx_inp_receivers.popitem()
                    user_sending_out = block_tx_out_inp_user.popitem()

                    if user_received_inp != user_sending_out:
                        return False, "User inconsistencies"

                if total_input < total_output:
                    return False, 'Creating money'

                # the sum of the input values is at least the sum of the output values (no money created out of thin air) [test_no_money_creation]

                # On failure: return False, "Creating money"

        return True, "All checks passed"