Refactor of Ticket flow. Stable to get confirmed Result via CLI. Not yet GUI

This commit is contained in:
SimplifiedPrivacy 2026-10-23 19:32:12 -04:00
parent 8dca35d5c9
commit bf9063b1f8
12 changed files with 723 additions and 457 deletions

View file

@ -18,11 +18,14 @@ class Constants:
# ticketing group:
TICKET_API_BASE_URL: Final[str] = os.environ.get(
"TICKET_API_BASE_URL", "https://api.hydraveil.net"
# "TICKET_API_BASE_URL", "https://api.hydraveil.net"
"TICKET_API_BASE_URL", "http://localhost:8000"
)
SP_API_BASE_URL: Final[str] = os.environ.get('SP_API_BASE_URL', 'https://api.hydraveil.net/api/v1')
SP_BASE_URL: Final[str] = os.environ.get('SP_BASE_URL', 'https://api.hydraveil.net/api')
SP_API_BASE_URL: Final[str] = os.environ.get('SP_API_BASE_URL', 'http://localhost:8000/api/v1')
SP_BASE_URL: Final[str] = os.environ.get('SP_BASE_URL', 'http://localhost:8000/api')
# SP_API_BASE_URL: Final[str] = os.environ.get('SP_API_BASE_URL', 'https://api.hydraveil.net/api/v1')
# SP_BASE_URL: Final[str] = os.environ.get('SP_BASE_URL', 'https://api.hydraveil.net/api')
PING_URL: Final[str] = os.environ.get('PING_URL', 'https://api.hydraveil.net/api/v1/health')

View file

@ -4,9 +4,9 @@ if TYPE_CHECKING:
from core.essentials.observers.ConnectionObserver import ConnectionObserver
from core.observers.TicketObserver import TicketObserver
#
from core.services.prepare_tickets.get_public_key_by_config import (
get_public_key_from_LOCAL_files_only,
)
# from core.services.prepare_tickets.get_public_key_by_config import (
# get_public_key_from_LOCAL_files_only,
# )
from core.services.failed_verification.is_the_key_to_blame import is_the_key_to_blame
from core.services.failed_verification.prep_with_previously_saved_blind_sigs import (
prep_with_previously_saved_blind_sigs,

View file

@ -1,12 +1,8 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.essentials.observers.ConnectionObserver import ConnectionObserver
from core.observers.ConnectionObserver import ConnectionObserver
from core.observers.TicketObserver import TicketObserver
from core.services.payment_phase.payment_state import TicketPaymentData
# from core.services.prepare_tickets.get_pub_key import get_and_save_pub_key
from core.services.prepare_tickets.get_pub_key import get_and_save_pub_key
from core.utils.type_checker import type_checker
from core.services.prepare_tickets.ticket_prep_orchestrator import ticket_prep_orchestrator
from core.services.prepare_tickets.make_sure_pub_key_exists import make_sure_pub_key_exists
@ -14,7 +10,6 @@ from core.services.prepare_tickets import ticket_tracker
from core.services.helpers.get_how_many_profiles_were_ordered import (
get_how_many_profiles_were_ordered,
)
# from core.observers.BaseObserver import BaseObserver
from core.models.Result import Result, ResultError
from core.errors.logger import logger
@ -82,11 +77,12 @@ def prepare_tickets(
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
# got_key = get_and_save_pub_key(which_key)
# if not got_key.valid:
# logger.error(got_key.message)
# return got_key
# logger.info("Got public key..")
got_key = get_and_save_pub_key(which_key, connection_observer)
if not got_key.valid:
logger.error(got_key.message)
return got_key
public_key = got_key.data
logger.info("Got public key..")
notification = "Preparing Cryptography Locally"
if ticket_observer is not None:
@ -95,6 +91,7 @@ def prepare_tickets(
# ok now we have the pre-reqs, let's use this high level orchestrator,
prep_results = ticket_prep_orchestrator(
how_many_profiles=how_many_profiles,
public_key=public_key,
ticket_observer=ticket_observer,
connection_observer=connection_observer,
which_ticket=which_ticket, # only relevant for single ticket prep

View file

@ -7,7 +7,7 @@ if TYPE_CHECKING:
# services
from core.services.crypto.TicketCustomer import TicketCustomer
from core.services.helpers.validate_number_format import number_of_valid_length
from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
# from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
from core.services.prepare_tickets.get_pub_key import key_is_in_valid_format, get_pub_key
from core.services.failed_verification.test_if_new_key_works import test_if_new_key_works
from core.services.networking.make_url import make_url

View file

@ -0,0 +1,252 @@
# services
from core.services.networking.make_url import make_url
from core.services.networking.httpx import connect
from core.services.helpers.get_plan_data import get_plan_data
from core.services.payment_phase.payment_state import TicketPaymentData
# utils
from core.utils.get_data import read_and_convert_with_eval
from core.models.Result import Result, ResultError
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
from core.utils.basic_operations.does_file_exist import does_file_exist
from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
from core.errors.logger import logger
from core.Constants import Constants
# generic
from typing import Any, Optional
def key_is_in_valid_format(public_key: Any) -> bool:
"""
Purpose:
Confirms a Public Key's Format with X and Y in a dictionary.
Called by:
get_pub_key (this module)
validate_blind_signatures' make_sure_we_have_inputs (external modules)
"""
return set(public_key.keys()) == {"x", "y"}
def fetch_key_from_server(which_key: str) -> dict|bool:
"""
Purpose:
Gets the public key from the API.
Called by:
get_and_save_pub_key (this module)
get_public_key_used (this module)
"""
url = make_url(which_key)
public_key_results = connect.single_endpoint(
method="get",
url=url,
payload=None
)
if not public_key_results.valid:
error_msg = f"Error with API getting the Public Key: {public_key_results.error_type} and {public_key_results.message}"
logger.error(error_msg)
return False
# extract:
public_key = public_key_results.data
return public_key
def get_and_save_pub_key(which_key: str) -> Result:
"""
Purpose:
Fetches a public key and saves it. But you have to know which key you want.
Called by:
TicketPayController's initiate_payment
"""
from core.Constants import Constants
data_folder = Constants.HV_TICKETING_DATA_HOME
file_path = f"{data_folder}/{str(which_key)}.json"
if not which_key:
error_msg = "You have to know which key you want to use this function."
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
public_key = fetch_key_from_server(which_key)
if not public_key:
error_msg = "Failed to connect to the API Server to get the public key."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.CONNECTION, message=error_msg)
if not key_is_in_valid_format(public_key):
error_msg = f"The server gave the {which_key} public key in an Invalid Format."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=error_msg)
# save it:
if not write_string_to_text_file(public_key, file_path):
error_msg = f"Error with saving the public key {which_key}. Please check your file system permissions. It tried to use this path: {file_path}"
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg)
# finally:
return Result(valid=True, data=public_key)
def get_from_config(get_what: str) -> str|bool:
"""
Purpose:
Wrapper with config file structure for external loaders.
Called by:
actions.begin_ticket_prep
"""
from core.Constants import Constants
filepath = Constants.TICKET_BILLING
return _get_from_config(filepath, get_what)
def _get_from_config(filepath: str, what_want: str) -> str|bool:
"""
Get some data from a dict-based file, and handle errors.
It assumes you have loaded the path on your own.
"""
try:
return get_value_from_json_file(filepath, what_want)
except (ValueError, KeyError) as e:
logger.error(f"We could not get {what_want} from the config at {filepath}, because {str(e)}")
return False
def find_out_which_key_we_used() -> Result:
"""Returns which key plan was used based on the local config."""
billing_data = TicketPaymentData()
which_key = billing_data.which_key
if which_key:
return Result(valid=True, data=which_key)
# OLD DEAD CODE:
billing_folder = Constants.HV_TICKETING_CONFIG_HOME
filepath = f"{billing_folder}/billing_choices.json"
if not does_file_exist(filepath):
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="The Billing Choices file is missing, we can't figure out what key or plan was used.")
which_key = _get_from_config(filepath, "which_key")
if which_key:
return Result(valid=True, data=which_key)
# can't find plan? try to get temp billing id.
temp_billing_code = _get_from_config(filepath, "billing_code")
if not temp_billing_code:
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="The Billing Choices file does not have what key or plan was used.")
# fetch from server:
fetching_result = get_plan_data(temp_billing_code)
if not fetching_result.valid:
return fetching_result # this is already Result format with the error.
plan_data = fetching_result.data
which_key = plan_data.get("which_key", False)
if which_key:
return Result(valid=True, data=which_key)
else:
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message="The API gave us a reply, but it lacked the critical key data we needed.")
def get_local_copy_of_public_key(which_key: str) -> Result:
"""Once we know which key we're looking for, get and convert it from local files."""
from core.Constants import Constants
filepath = f"{Constants.HV_TICKETING_DATA_HOME}/{which_key}.json"
if not does_file_exist(filepath):
return Result(valid=False, error_type=ResultError.MISSING_DATA, message=f"The file doesn't exist, or you lack the permissions to open it, at {filepath}")
# Convert it to the format we need:
public_key = read_and_convert_with_eval(filepath)
if not public_key:
error_msg = f"The local key of {which_key} is in the wrong format, even though the file exists."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
# this just checks if it has an X and Y in a dictionary:
if not key_is_in_valid_format(public_key) or not isinstance(public_key, dict):
error_msg = f"The local key of {which_key} is in the wrong format, even though the file exists."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
return Result(valid=True, data=public_key)
def get_public_key_used(local_only: Optional[bool] = False, save_it: Optional[bool] = False) -> Result:
"""
1. Use the local config to figure out which key/plan
2. See if there's a local copy first, convert it.
3. If not fetch that from the API (unless local_only flag is on.)
Called by:
TicketPrepController's prepare_tickets
FailedVerificationController's evaluate_if_its_the_key
"""
# -------------------------
# Step 1. Which key was used?
# -------------------------
key_results = find_out_which_key_we_used()
if not key_results.valid:
return key_results
which_key = key_results.data
# -------------------------
# Step 2. Do we have it locally?
# -------------------------
local_copy_result = get_local_copy_of_public_key(which_key)
if local_only:
return local_copy_result
if local_copy_result.valid:
return local_copy_result
# -------------------------
# Step 3. Fetch it from the API
# -------------------------
public_key = fetch_key_from_server(which_key)
if not public_key:
error_msg = f"Failed to connect to the API Server to get the public key {which_key}."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.CONNECTION, message=error_msg)
if save_it:
return save_key(public_key, which_key)
# if they didn't flag on save_it, then we're done:
return Result(valid=True, data=public_key)
def save_key(public_key: dict, which_key: str) -> Result:
"""
Purpose:
Saves a public key locally, with a file path based on it's name.
Called by:
get_public_key_used (this module)
"""
from core.Constants import Constants
filepath = f"{Constants.HV_TICKETING_DATA_HOME}/{which_key}.json"
if write_string_to_text_file(public_key, filepath):
return Result(valid=True, data=public_key)
else:
error_msg = f"Error with saving the public key {which_key}. Please check your file system permissions. It tried to use this path: {filepath}"
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg)

View file

@ -1,120 +1,244 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from essentials.observers.ConnectionObserver import ConnectionObserver
from typing import Any
# services
from core.services.networking.make_url import make_url
from core.services.networking.httpx import connect
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.helpers.get_plan_data import get_plan_data
# utils
from core.utils.get_data import read_and_convert_with_eval
from core.models.Result import Result, ResultError
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
from core.utils.basic_operations.does_file_exist import does_file_exist
from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
# errors & constants
from core.Constants import Constants
from core.errors.logger import logger
from core.Constants import Constants
from core.observers.ConnectionObserver import ConnectionObserver
# generic
import ast
from typing import Any, Optional
def key_is_in_valid_format(public_key: Any) -> bool:
"""
Purpose:
Confirms a Public Key's Format with X and Y in a dictionary.
Called by:
get_pub_key (this module)
validate_blind_signatures' make_sure_we_have_inputs (external modules)
"""
return set(public_key.keys()) == {"x", "y"}
def get_from_server_and_save(
string_form_of_key_name: str,
file_path: str,
connection_observer: ConnectionObserver,
) -> dict:
url = make_url(string_form_of_key_name)
def fetch_key_from_server(which_key: str, connection_observer: ConnectionObserver) -> dict|bool:
"""
Purpose:
Gets the public key from the API.
Called by:
get_and_save_pub_key (this module)
get_public_key_used (this module)
"""
url = make_url(which_key)
public_key_results = connect.single_endpoint(
method="get",
url=url,
observer=connection_observer,
payload=None
payload=None,
observer=connection_observer
)
if not public_key_results.valid:
error_msg = f"Error with API getting the Public Key: {public_key_results.error_type} and {public_key_results.message}"
logger.error(error_msg)
return {"valid": False, "message": error_msg}
return False
# extract:
public_key = public_key_results.data
print(f"public_key is {public_key}")
return public_key
def get_and_save_pub_key(which_key: str, connection_observer: ConnectionObserver) -> Result:
"""
Purpose:
Fetches a public key and saves it. But you have to know which key you want.
Called by:
TicketPayController's initiate_payment
"""
data_folder = Constants.HV_TICKETING_DATA_HOME
file_path = f"{data_folder}/{str(which_key)}.json"
if not which_key:
error_msg = "You have to know which key you want to use this function."
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
public_key = fetch_key_from_server(which_key, connection_observer)
if not public_key:
error_msg = "Failed to connect to the API Server to get the public key."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.CONNECTION, message=error_msg)
if not key_is_in_valid_format(public_key):
error_msg = f"The server gave the {which_key} public key in an Invalid Format."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message=error_msg)
# save it:
did_it_save = write_string_to_text_file(public_key, file_path)
if not write_string_to_text_file(public_key, file_path):
error_msg = f"Error with saving the public key {which_key}. Please check your file system permissions. It tried to use this path: {file_path}"
log_and_notify(error_msg)
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg)
if not did_it_save:
logger.error(f"Error with saving the public key {public_key} with write_string_to_text_file, but still returning data..")
# regardless of saving:
return {"valid": True, "data": public_key}
# finally:
return Result(valid=True, data=public_key)
def get_pub_key(
which_key: str,
connection_observer: ConnectionObserver,
check_locally: str,
) -> dict:
def get_from_config(get_what: str) -> str|bool:
"""
Purpose:
Wrapper with config file structure for external loaders.
Called by:
actions.begin_ticket_prep
"""
from core.Constants import Constants
filepath = Constants.TICKET_BILLING
return _get_from_config(filepath, get_what)
# prep file path for either saving or using the existing one:
data_folder = Constants.HV_TICKETING_DATA_HOME
string_form_of_key_name = str(which_key)
file_path = f"{data_folder}/{string_form_of_key_name}.json"
# do we want to use the key if it's already stored locally?
if check_locally == "local":
# does the public key already exist locally?
exists_already = does_file_exist(file_path)
else:
logger.debug("Getting Public Key from API..", exc_info=True)
# setting this to false bypasses it being local for the next section, and skips to the end
exists_already = False
# Next Section
if exists_already == True:
# we're going to try to get it from the file locally:
def _get_from_config(filepath: str, what_want: str) -> str|bool:
"""
Get some data from a dict-based file, and handle errors.
It assumes you have loaded the path on your own.
"""
try:
with open(file_path, "r") as file:
public_key_as_string = file.read()
return get_value_from_json_file(filepath, what_want)
except (ValueError, KeyError) as e:
logger.error(f"We could not get {what_want} from the config at {filepath}, because {str(e)}")
return False
# this gets in pyth dict with a single ', which is how the crypto functions need it.
public_key = ast.literal_eval(public_key_as_string)
if key_is_in_valid_format(public_key):
return {"valid": True, "data": public_key}
def find_out_which_key_we_used() -> Result:
"""Returns which key plan was used based on the local config."""
from core.Constants import Constants
billing_folder = Constants.HV_TICKETING_CONFIG_HOME
filepath = f"{billing_folder}/billing_choices.json"
if not does_file_exist(filepath):
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="The Billing Choices file is missing, we can't figure out what key or plan was used.")
which_key = _get_from_config(filepath, "which_key")
if which_key:
return Result(valid=True, data=which_key)
# can't find plan? try to get temp billing id.
temp_billing_code = _get_from_config(filepath, "temp_billing_code")
if not temp_billing_code:
return Result(valid=False, error_type=ResultError.MISSING_DATA, message="The Billing Choices file does not have what key or plan was used.")
# fetch from server:
fetching_result = get_plan_data(temp_billing_code)
if not fetching_result.valid:
return fetching_result # this is already Result format with the error.
plan_data = fetching_result.data
which_key = plan_data.get("which_key", False)
if which_key:
return Result(valid=True, data=which_key)
else:
print(
"Key format is Invalid! Let's replace it by getting a new one from the server,"
)
public_key_results = get_from_server_and_save(
string_form_of_key_name, file_path, connection_observer
)
return public_key_results
return Result(valid=False, error_type=ResultError.INVALID_API_REPLY, message="The API gave us a reply, but it lacked the critical key data we needed.")
# but if the local key doesn't format properly, then get it from the server,
except:
public_key_results = get_from_server_and_save(
string_form_of_key_name, file_path, connection_observer
)
return public_key_results
def get_local_copy_of_public_key(which_key: str) -> Result:
"""Once we know which key we're looking for, get and convert it from local files."""
# no local key? get from server,
from core.Constants import Constants
filepath = f"{Constants.HV_TICKETING_DATA_HOME}/{which_key}.json"
if not does_file_exist(filepath):
return Result(valid=False, error_type=ResultError.MISSING_DATA, message=f"The file doesn't exist, or you lack the permissions to open it, at {filepath}")
# Convert it to the format we need:
public_key = read_and_convert_with_eval(filepath)
if not public_key:
error_msg = f"The local key of {which_key} is in the wrong format, even though the file exists."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
# this just checks if it has an X and Y in a dictionary:
if not key_is_in_valid_format(public_key) or not isinstance(public_key, dict):
error_msg = f"The local key of {which_key} is in the wrong format, even though the file exists."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg)
return Result(valid=True, data=public_key)
def get_public_key_used(local_only: Optional[bool] = False, save_it: Optional[bool] = False) -> Result:
"""
1. Use the local config to figure out which key/plan
2. See if there's a local copy first, convert it.
3. If not fetch that from the API (unless local_only flag is on.)
Called by:
TicketPrepController's prepare_tickets
FailedVerificationController's evaluate_if_its_the_key
"""
# -------------------------
# Step 1. Which key was used?
# -------------------------
key_results = find_out_which_key_we_used()
if not key_results.valid:
return key_results
which_key = key_results.data
# -------------------------
# Step 2. Do we have it locally?
# -------------------------
local_copy_result = get_local_copy_of_public_key(which_key)
if local_only:
return local_copy_result
if local_copy_result.valid:
return local_copy_result
# -------------------------
# Step 3. Fetch it from the API
# -------------------------
public_key = fetch_key_from_server(which_key)
if not public_key:
error_msg = f"Failed to connect to the API Server to get the public key {which_key}."
logger.error(error_msg)
return Result(valid=False, error_type=ResultError.CONNECTION, message=error_msg)
if save_it:
return save_key(public_key, which_key)
# if they didn't flag on save_it, then we're done:
return Result(valid=True, data=public_key)
def save_key(public_key: dict, which_key: str) -> Result:
"""
Purpose:
Saves a public key locally, with a file path based on it's name.
Called by:
get_public_key_used (this module)
"""
from core.Constants import Constants
filepath = f"{Constants.HV_TICKETING_DATA_HOME}/{which_key}.json"
if write_string_to_text_file(public_key, filepath):
return Result(valid=True, data=public_key)
else:
logger.debug(
"You do not have the public key locally. So we are getting it from the server.."
)
public_key_results = get_from_server_and_save(
string_form_of_key_name, file_path, connection_observer
)
error_msg = f"Error with saving the public key {which_key}. Please check your file system permissions. It tried to use this path: {filepath}"
log_and_notify(error_msg)
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message=error_msg)
return public_key_results

View file

@ -1,186 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from essentials.observers.ConnectionObserver import ConnectionObserver
# services & helpers
from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.prepare_tickets.get_pub_key import get_pub_key
from core.services.prepare_tickets.get_pub_key import key_is_in_valid_format
from core.services.helpers.get_plan_data import get_plan_data
# utils
from core.utils.basic_operations.filter_data import filter_data
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
from core.utils.basic_operations.does_file_exist import does_file_exist
# constants
from core.Constants import Constants
# generic
import ast
"""
This service tries to get a public key, based on the config file.
It first checks if the config file has which key it wants.
Then it checks for a temp billing code, and if there is one, asks the server what the key is.
"""
# Get which key plan they have.
def get_public_key_FROM_config(filepath) -> str | None:
try:
which_key = get_value_from_json_file(filepath, "which_key")
return which_key
except:
return None
def get_public_key_by_config(connection_observer: ConnectionObserver) -> dict:
list_of_failures = [None, False, ""]
billing_folder = Constants.HV_TICKETING_CONFIG_HOME
filepath = f"{billing_folder}/billing_choices.json"
which_key = get_public_key_FROM_config(filepath)
# if there is no key in the config?
if which_key is None:
# then try to get the temp billing id.
temp_billing_code = get_value_from_json_file(filepath, "temp_billing_code")
# but what if there is not even a temp billing ID?
if temp_billing_code in list_of_failures:
return {
"status": False,
"message": f"Issue with finding key plan or billing code. Please check {filepath}",
}
# Now we're assuming we have a temp billing code,
# So we can use that to get the key from the server,
api_reply_object = get_plan_data(temp_billing_code, connection_observer)
complete_failure_msg = {
"status": False,
"message": f"Issues with both finding your local key plan and even connecting to the server for it. Please check {filepath}",
}
if not api_reply_object.valid:
return complete_failure_msg
reply = api_reply_object.data
if reply in list_of_failures:
return complete_failure_msg
if not isinstance(reply, dict):
return {
"status": False,
"message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}",
}
# from the server's reply, get the key
which_key = filter_data(reply, "which_key")
if which_key in list_of_failures:
return {
"status": False,
"message": f"Issues with both finding your local key plan and getting it from the server. Please check {filepath}",
}
# The reason we convert it to integer, only to convert it back to a string in the next functions,
# is because those functions can be accessed directly in other ways that would have the key as an integer.
# which_key = int(which_key)
# send to service:
public_key_results = get_pub_key(which_key, connection_observer, "local")
# if public_key_results is None:
# return {'status': False, 'message': "no_public_key"}
# assert public_key_results is not None
return public_key_results
"""
This seems like a duplicate function, but it's used in situations where there was a failed validation.
The other functions get the key from local files OR the api. And they save it, which would replace the key we want to study.
Instead, this function gets it only from local files.
"""
def get_public_key_from_LOCAL_files_only(
connection_observer: ConnectionObserver,
) -> dict:
billing_folder = Constants.HV_TICKETING_CONFIG_HOME
filepath = f"{billing_folder}/billing_choices.json"
try:
which_key = get_value_from_json_file(filepath, "which_key")
except:
which_key = None
finally:
list_of_failures = [None, False, ""]
if which_key in list_of_failures:
# can't find plan? try to get temp billing id.
temp_billing_code = get_value_from_json_file(filepath, "temp_billing_code")
if temp_billing_code in list_of_failures:
return {
"status": False,
"message": f"Issue with finding key plan or billing code. Please check {filepath}",
}
api_reply_object = get_plan_data(temp_billing_code, connection_observer)
complete_failure_msg = {
"status": False,
"message": f"Issues with both finding your local key plan and even connecting to the server for it. Please check {filepath}",
}
if not api_reply_object.valid:
return complete_failure_msg
reply = api_reply_object.data
if reply in list_of_failures:
return complete_failure_msg
if not isinstance(reply, dict):
return {
"status": False,
"message": f"Server returned an invalid format, and even accessing via local files. Please check {filepath}",
}
which_key = filter_data(reply, "which_key")
if which_key in list_of_failures:
return {
"status": False,
"message": f"Issues with both finding your local key plan and getting it from the server. Please check {filepath}",
}
# now that we know the key plan, move on to getting the data:
data_folder = Constants.HV_TICKETING_DATA_HOME
string_form_of_key_name = str(which_key)
file_path = f"{data_folder}/{string_form_of_key_name}.json"
# does it exist?
exists_already = does_file_exist(file_path)
if not exists_already:
return {"valid": False, "message": "Local key file doesn't exist"}
try:
with open(file_path, "r") as file:
public_key_as_string = file.read()
# this gets in pyth dict with a single ', which is how the crypto functions need it.
public_key = ast.literal_eval(public_key_as_string)
# this only checks if the key has 'x' and 'y' keys:
if key_is_in_valid_format(public_key):
return {"valid": True, "public_key": public_key}
else:
return {
"valid": False,
"message": "Key file exists but is in an invalid format.",
}
except:
return {
"valid": False,
"message": "Key file exists but is in an invalid format or unreadable.",
}

View file

@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from essentials.observers.ConnectionObserver import ConnectionObserver
#
from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
# from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
from core.utils.basic_operations.does_file_exist import does_file_exist
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
from core.Constants import Constants

View file

@ -0,0 +1,181 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.observers.TicketObserver import TicketObserver
from essentials.observers.ConnectionObserver import ConnectionObserver
# services
from core.services.crypto.TicketCustomer import TicketCustomer
from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
from core.services.prepare_tickets.get_pub_key import key_is_in_valid_format, get_pub_key
# utils
from core.utils.save_data import save_data
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
# observers, constants, errors
from core.observers.BaseObserver import BaseObserver
from core.models.Event import Event
from core.Constants import Constants
from core.errors.exceptions import *
from core.errors.logger import logger
# generic
import traceback
import json
from typing import Optional
def make_sure_we_have_inputs(
ALL_signed_blind_signatures: list,
profile_object: TicketCustomer,
ticket_observer: TicketObserver,
connection_observer: ConnectionObserver,
) -> dict:
try:
if ALL_signed_blind_signatures == False or ALL_signed_blind_signatures == None:
error_msg = "The Server gave back no data or it's missing for ALL_signed_blind_signatures in the unblind_and_prove_ALL_profiles function . This can't unblind blank data."
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
# get the public key for entire loop:
public_key_results = get_public_key_by_config(connection_observer)
# did we actually get the public key?
if "data" not in public_key_results:
error_msg = (
"The validation function can't even get the public key to do it's loop"
)
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
public_key = public_key_results.get("data")
rejected_reasons = [None, False, ""]
if public_key in rejected_reasons:
error_msg = "The validation function can't get the public key"
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
# this just checks if it has an X and Y in a dictionary:
if not key_is_in_valid_format(public_key) or not isinstance(public_key, dict):
return {"valid": False, "error_msg": "invalid_key_format"}
logger.debug("Checking if the public key is even able to be loaded...")
valid_key = profile_object.load_key(public_key)
if valid_key:
logger.debug("Public Key can be loaded")
return {"valid": True, "public_key": public_key}
else:
error_msg = "The validation function is working with an invalid public key"
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
except:
error_msg = (
"We lack the ability to get all the inputs for the verification loop"
)
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
"""
Technically this entire module is optional, as it isn't used for the actual ticket preparation.
This module exists so the customer can be sure that the server did not maliciously sign with a fake new key,
to try to secretly identify users (by using different keys for each one).
"""
def validate_blind_signatures(
ALL_signed_blind_signatures: list,
ticket_observer: TicketObserver,
connection_observer: ConnectionObserver,
which_ticket: Optional[int] = None,
):
try:
# Setup the entire class object of "profile_object" for using all these other functions,
profile_object = TicketCustomer()
# start: confirm inputs
inputs = make_sure_we_have_inputs(
ALL_signed_blind_signatures,
profile_object,
ticket_observer,
connection_observer,
)
if not inputs["valid"]:
logger.debug("ending the validation process early.")
return inputs
public_key = inputs["public_key"]
# end: confirm inputs
# setup basic counters, flags, and lists for the loop below:
if which_ticket is None:
which_ticket = 1
did_we_answer_the_key_question_yet = "no"
list_of_failed_verifications = []
"""
LOOP STARTS:
Looping through each signature, verifying it against the single public key.
"""
for each_blind_signature in ALL_signed_blind_signatures:
# prep from server's format:
blind_signature = json.loads(each_blind_signature)
#### Check blind signature validity for THAT signature:
validity_data = profile_object.test_blind_signature_validity(
which_ticket, blind_signature, public_key
)
if validity_data["valid"] == True:
# verification went fine:
notification = f"Verified {which_ticket}'s blind signature"
ticket_observer.notify("preparing", subject=notification)
else:
# is it an invalid pub key?
if validity_data["message"] == "invalid_key":
notification = f"Verification of all blind signatures is halted, because you have an invalid public key!"
ticket_observer.notify("preparing", subject=notification)
return [which_ticket]
# otherwise:
list_of_failed_verifications.append(which_ticket)
notification = f"Invalid {which_ticket} blind signature"
ticket_observer.notify("preparing", subject=notification)
# bump loop for next round:
which_ticket = which_ticket + 1
### LOOP DONE
logger.debug(
f"Exiting the validation loop with the list_of_failed_verifications: {list_of_failed_verifications}"
)
return list_of_failed_verifications
except FailedToSave as e:
error_msg = f"FailedToSave: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except InvalidData as e:
error_msg = f"InvalidData error: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except MissingData as e:
error_msg = f"MissingData: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except Exception as e:
tb = traceback.extract_tb(e.__traceback__)[-1]
error_msg = f"error type {type(e).__name__} happened on line {tb.lineno}, in function {tb.name}, giving error: {e}. The actual code in question is: {tb.line}. in this file: {tb.filename}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}

View file

@ -6,22 +6,18 @@ if TYPE_CHECKING:
from core.observers.TicketObserver import TicketObserver
# services
# from core.services.networking.httpx import connect
# from core.services.networking.api_requests.ApiResponseModel import ApiResponse, ErrorType
from core.services.networking.make_url import make_url
# from core.services.payment_phase.ticket_config_tools import get_which_billing_key
# models & their controllers
from core.models.manage.server_reply_tools import api_call_and_extraction, ApiCall, translate_server_failure
from core.models.pydantic_models.ServerReply import ServerReply, ServerError
# from core.models.session.SessionProfile import SessionProfile
# from core.models.system.SystemProfile import SystemProfile
from core.services.payment_phase.payment_state import TicketPaymentData
# utils
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
# constants & errors
from core.Constants import Constants
# from core.errors.exceptions import InvalidData, ServerSideError
from core.models.Result import Result, ResultError
from core.errors.logger import logger
@ -40,12 +36,15 @@ def prep_payload_for_bulk(list_of_all_blinded_data: list) -> dict:
Rank:
Mini-Helper
"""
# get temp billing id from local storage:
temp_billing_code = get_value_from_json_file(billing_path, "temp_billing_code")
# billing code
billing_data = TicketPaymentData()
billing_code = billing_data.billing_code
# temp_billing_code = get_value_from_json_file(billing_path, "temp_billing_code")
# prep the JSON payload, it doesn't need the public key, since the server can look that up,
payload = {
"temp_billing_code": temp_billing_code,
"billing_code": billing_code,
"blinded_data": list_of_all_blinded_data,
}

View file

@ -2,9 +2,9 @@ from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.observers.TicketObserver import TicketObserver
from essentials.observers.ConnectionObserver import ConnectionObserver
from core.observers.ConnectionObserver import ConnectionObserver
from core.observers.TicketObserver import TicketObserver
from core.services.crypto.make_commitments import make_ALL_commitments
from core.services.prepare_tickets.unblind_all_tickets import unblind_ALL_tickets
@ -46,6 +46,7 @@ Called by:
@type_checker
def ticket_prep_orchestrator(
how_many_profiles: int,
public_key: dict,
ticket_observer: TicketObserver,
connection_observer: ConnectionObserver,
which_ticket: Optional[int] = None,
@ -106,11 +107,14 @@ def ticket_prep_orchestrator(
# verify the server's blind signatures against the public key,
notification = "Evaluating the Server's Blind Replies"
ticket_observer.notify("preparing", subject=notification)
failed_validations = validate_blind_signatures(
blind_signatures, ticket_observer, connection_observer, which_ticket
validations = validate_blind_signatures(
ALL_signed_blind_signatures=blind_signatures,
public_key=public_key,
ticket_observer=ticket_observer,
which_ticket=which_ticket
)
logger.debug(f"failed_validations is {failed_validations}")
failed_validations = validations.data
#####################################################
# FAILED BLIND SIG VERIFICATION
@ -129,14 +133,6 @@ def ticket_prep_orchestrator(
message=notification
)
#####################################################
# PAID
#####################################################
# saving on config that it's paid:
config_marked = ConfigurationController.mark_as_paid(True)
if not config_marked:
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message="Failed to mark as paid in the config. You still have the tickets locally, just can't use them till this file system error is resolved.")
#####################################################
# UNBLIND
#####################################################
@ -149,6 +145,7 @@ def ticket_prep_orchestrator(
connection_observer=connection_observer,
which_ticket=which_ticket
)
logger.info(f"Unblind completed. Was it a success? {did_prep_work}")
#####################################################
# TICKET TRACKER & APP-WIDE CONFIG
@ -157,6 +154,7 @@ def ticket_prep_orchestrator(
setup_tracker = setup_ticket_tracker(how_many_profiles)
if not setup_tracker:
return Result(valid=False, error_type=ResultError.FILE_SYSTEM, message="Failed to setup the ticket tracker. You still have the tickets locally, just can't use them till this file system error is resolved.")
logger.info("Ticket tracker setup completed.")
#####################################################
# FINAL EVALUATION
@ -165,6 +163,7 @@ def ticket_prep_orchestrator(
# this means it unblinded, & setup the tracker without erroring out,
# but it does NOT mean that verification of the blind sigs worked.
ConfigurationController.tickets_setup(True)
logger.info("Setup Complete.")
return Result(valid=True, message="Setup worked.")

View file

@ -1,85 +1,17 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from core.observers.TicketObserver import TicketObserver
from essentials.observers.ConnectionObserver import ConnectionObserver
# services
from core.services.crypto.TicketCustomer import TicketCustomer
from core.services.prepare_tickets.get_public_key_by_config import get_public_key_by_config
from core.services.prepare_tickets.get_pub_key import key_is_in_valid_format, get_pub_key
# utils
from core.utils.save_data import save_data
from core.utils.basic_operations.write_or_read_from_json import get_value_from_json_file
# observers, constants, errors
from core.observers.BaseObserver import BaseObserver
from core.models.Event import Event
from core.Constants import Constants
from core.errors.exceptions import *
from core.models.Result import Result #, ResultError
from core.errors.logger import logger
# generic
import traceback
import json
from typing import Optional
def make_sure_we_have_inputs(
ALL_signed_blind_signatures: list,
profile_object: TicketCustomer,
ticket_observer: TicketObserver,
connection_observer: ConnectionObserver,
) -> dict:
try:
if ALL_signed_blind_signatures == False or ALL_signed_blind_signatures == None:
error_msg = "The Server gave back no data or it's missing for ALL_signed_blind_signatures in the unblind_and_prove_ALL_profiles function . This can't unblind blank data."
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
# get the public key for entire loop:
public_key_results = get_public_key_by_config(connection_observer)
# did we actually get the public key?
if "data" not in public_key_results:
error_msg = (
"The validation function can't even get the public key to do it's loop"
)
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
public_key = public_key_results.get("data")
rejected_reasons = [None, False, ""]
if public_key in rejected_reasons:
error_msg = "The validation function can't get the public key"
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
# this just checks if it has an X and Y in a dictionary:
if not key_is_in_valid_format(public_key) or not isinstance(public_key, dict):
return {"valid": False, "error_msg": "invalid_key_format"}
logger.debug("Checking if the public key is even able to be loaded...")
valid_key = profile_object.load_key(public_key)
if valid_key:
logger.debug("Public Key can be loaded")
return {"valid": True, "public_key": public_key}
else:
error_msg = "The validation function is working with an invalid public key"
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
except:
error_msg = (
"We lack the ability to get all the inputs for the verification loop"
)
logger.error(error_msg, exc_info=True)
logger.debug(error_msg)
return {"valid": False, "error_msg": error_msg}
REJECTED_INPUTS = [False, None, "error"]
"""
Technically this entire module is optional, as it isn't used for the actual ticket preparation.
@ -91,40 +23,28 @@ to try to secretly identify users (by using different keys for each one).
def validate_blind_signatures(
ALL_signed_blind_signatures: list,
public_key: dict,
ticket_observer: TicketObserver,
connection_observer: ConnectionObserver,
which_ticket: Optional[int] = None,
):
try:
which_ticket: Optional[int] = None, # Single or Multiple Ticket Prep. If there's a value here, it's a single ticket.
) -> Result:
# Setup the entire class object of "profile_object" for using all these other functions,
profile_object = TicketCustomer()
# start: confirm inputs
inputs = make_sure_we_have_inputs(
ALL_signed_blind_signatures,
profile_object,
ticket_observer,
connection_observer,
)
if not inputs["valid"]:
logger.debug("ending the validation process early.")
return inputs
public_key = inputs["public_key"]
# end: confirm inputs
#-----------------------------------
# PREPARE FOR UPCOMING LOOP
#-----------------------------------
# setup basic counters, flags, and lists for the loop below:
if which_ticket is None:
if which_ticket is None: # this is if it's a single or multiple ticket prep. And not just basic filtering
which_ticket = 1
did_we_answer_the_key_question_yet = "no"
list_of_failed_verifications = []
"""
LOOP STARTS:
Looping through each signature, verifying it against the single public key.
"""
#-----------------------------------
# LOOP:
#-----------------------------------
# Looping through each signature, verifying it against the single public key.
for each_blind_signature in ALL_signed_blind_signatures:
# prep from server's format:
blind_signature = json.loads(each_blind_signature)
@ -136,46 +56,23 @@ def validate_blind_signatures(
if validity_data["valid"] == True:
# verification went fine:
notification = f"Verified {which_ticket}'s blind signature"
ticket_observer.notify("preparing", subject=notification)
logger.info(f"Verified {which_ticket}'s blind signature")
else:
# is it an invalid pub key?
if validity_data["message"] == "invalid_key":
notification = f"Verification of all blind signatures is halted, because you have an invalid public key!"
ticket_observer.notify("preparing", subject=notification)
return [which_ticket]
error_msg = "Verification of all blind signatures is halted, because you have an invalid public key!"
return Result(valid=True, data=[which_ticket], message=error_msg)
# otherwise:
list_of_failed_verifications.append(which_ticket)
notification = f"Invalid {which_ticket} blind signature"
ticket_observer.notify("preparing", subject=notification)
logger.error(f"Invalid {which_ticket} blind signature")
# bump loop for next round:
which_ticket = which_ticket + 1
### LOOP DONE
logger.debug(
f"Exiting the validation loop with the list_of_failed_verifications: {list_of_failed_verifications}"
)
return list_of_failed_verifications
except FailedToSave as e:
error_msg = f"FailedToSave: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except InvalidData as e:
error_msg = f"InvalidData error: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except MissingData as e:
error_msg = f"MissingData: {e}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
except Exception as e:
tb = traceback.extract_tb(e.__traceback__)[-1]
error_msg = f"error type {type(e).__name__} happened on line {tb.lineno}, in function {tb.name}, giving error: {e}. The actual code in question is: {tb.line}. in this file: {tb.filename}"
logger.error(error_msg, exc_info=True)
return {"valid": False, "message": "error", "error": error_msg}
return Result(valid=True, data=list_of_failed_verifications)