def validate_proof(proof, target_hash, merkle_root, hash_f=sha256):
    """
    Takes a proof array, a target hash value, and a merkle root.
    Checks the validity of the proof and return true or false.
    :param proof:
    :param target_hash:
    :param merkle_root:
    :param hash_f:
    :return: whether proof is valid, Boolean
    """

    if not proof:
        # no siblings, single item tree, so the hash should also be the root
        return target_hash == merkle_root

    target_hash = get_buffer(target_hash)
    merkle_root = get_buffer(merkle_root)

    proof_hash = target_hash
    for x in proof:
        if 'left' in x:
            # then the sibling is a left node
            proof_hash = get_buffer(hash_f(get_buffer(x['left']) + proof_hash))
        elif 'right' in x:
            # then the sibling is a right node
            proof_hash = get_buffer(hash_f(proof_hash + get_buffer(x['right'])))
        else:
            # no left or right designation exists, proof is invalid
            return False

    return hexlify(proof_hash) == hexlify(merkle_root)