Skip to content

Commit

Permalink
clean custom exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
Ouziel committed Jan 16, 2025
1 parent 85eb071 commit 21ee60f
Show file tree
Hide file tree
Showing 10 changed files with 118 additions and 137 deletions.
2 changes: 1 addition & 1 deletion counterparty-core/counterpartycore/lib/api/apiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ def init_flask_app():
def check_database_version(state_db):
try:
check.database_version(state_db)
except check.DatabaseVersionError as e:
except exceptions.VersionError as e:
logger.info(str(e))
# rollback or reparse the database
if e.required_action in ["rollback", "reparse"]:
Expand Down
76 changes: 33 additions & 43 deletions counterparty-core/counterpartycore/lib/api/apiv1.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,35 +132,25 @@
CURRENT_API_STATUS_RESPONSE_JSON = None # is updated by the APIStatusPoller


class APIError(Exception):
pass


class BackendError(Exception):
pass


def check_backend_state():
f"""Checks blocktime of last block to see if {config.BTC_NAME} Core is running behind.""" # noqa: B021
block_count = backend.bitcoind.getblockcount()
block_hash = backend.bitcoind.getblockhash(block_count)
cblock = backend.bitcoind.getblock(block_hash, verbosity=1)
time_behind = time.time() - cblock["time"] # TODO: Block times are not very reliable.
if time_behind > 60 * 60 * 2: # Two hours.
raise BackendError(f"Bitcoind is running about {round(time_behind / 3600)} hours behind.")
raise exceptions.BackendError(
f"Bitcoind is running about {round(time_behind / 3600)} hours behind."
)

# check backend index
blocks_behind = backend.bitcoind.get_blocks_behind()
if blocks_behind > 5:
raise BackendError(f"Bitcoind is running {blocks_behind} blocks behind.")
raise exceptions.BackendError(f"Bitcoind is running {blocks_behind} blocks behind.")

logger.debug("API Status Poller - Backend state check passed.")


class DatabaseError(Exception):
pass


# TODO: ALL queries EVERYWHERE should be done with these methods
def db_query(db, statement, bindings=(), callback=None, **callback_args):
"""Allow direct access to the database in a parametrized manner."""
Expand All @@ -171,7 +161,7 @@ def db_query(db, statement, bindings=(), callback=None, **callback_args):
for word in forbidden_words:
# This will find if the forbidden word is in the statement as a whole word. For example, "transactions" will be allowed because the "s" at the end
if re.search(r"\b" + word + "\b", statement.lower()):
raise APIError(f"Forbidden word in query: '{word}'.")
raise exceptions.APIError(f"Forbidden word in query: '{word}'.")

if callable(callback):
cursor.execute(statement, bindings)
Expand Down Expand Up @@ -211,22 +201,22 @@ def value_to_marker(value):

# TODO: Document that op can be anything that SQLite3 accepts.
if not table or table.lower() not in API_TABLES:
raise APIError("Unknown table")
raise exceptions.APIError("Unknown table")
if filterop and filterop.upper() not in ["OR", "AND"]:
raise APIError("Invalid filter operator (OR, AND)")
raise exceptions.APIError("Invalid filter operator (OR, AND)")
if order_dir and order_dir.upper() not in ["ASC", "DESC"]:
raise APIError("Invalid order direction (ASC, DESC)")
raise exceptions.APIError("Invalid order direction (ASC, DESC)")
if not isinstance(limit, int):
raise APIError("Invalid limit")
raise exceptions.APIError("Invalid limit")
elif config.API_LIMIT_ROWS != 0 and limit > config.API_LIMIT_ROWS:
raise APIError(f"Limit should be lower or equal to {config.API_LIMIT_ROWS}")
raise exceptions.APIError(f"Limit should be lower or equal to {config.API_LIMIT_ROWS}")
elif config.API_LIMIT_ROWS != 0 and limit == 0:
raise APIError("Limit should be greater than 0")
raise exceptions.APIError("Limit should be greater than 0")
if not isinstance(offset, int):
raise APIError("Invalid offset")
raise exceptions.APIError("Invalid offset")
# TODO: accept an object: {'field1':'ASC', 'field2': 'DESC'}
if order_by and not re.compile("^[a-z0-9_]+$").match(order_by):
raise APIError("Invalid order_by, must be a field name")
raise exceptions.APIError("Invalid order_by, must be a field name")

if isinstance(filters, dict): # single filter entry, convert to a one entry list
filters = [
Expand All @@ -246,21 +236,21 @@ def value_to_marker(value):
elif type(filter_) == dict: # noqa: E721
new_filters.append(filter_)
else:
raise APIError("Unknown filter type")
raise exceptions.APIError("Unknown filter type")
filters = new_filters

# validate filter(s)
for filter_ in filters:
for field in ["field", "op", "value"]: # should have all fields
if field not in filter_:
raise APIError(f"A specified filter is missing the '{field}' field")
raise exceptions.APIError(f"A specified filter is missing the '{field}' field")
if not isinstance(filter_["value"], (str, int, float, list)):
raise APIError(f"Invalid value for the field '{filter_['field']}'")
raise exceptions.APIError(f"Invalid value for the field '{filter_['field']}'")
if isinstance(filter_["value"], list) and filter_["op"].upper() not in [
"IN",
"NOT IN",
]:
raise APIError(f"Invalid value for the field '{filter_['field']}'")
raise exceptions.APIError(f"Invalid value for the field '{filter_['field']}'")
if filter_["op"].upper() not in [
"=",
"==",
Expand All @@ -274,9 +264,9 @@ def value_to_marker(value):
"NOT IN",
"NOT LIKE",
]:
raise APIError(f"Invalid operator for the field '{filter_['field']}'")
raise exceptions.APIError(f"Invalid operator for the field '{filter_['field']}'")
if "case_sensitive" in filter_ and not isinstance(filter_["case_sensitive"], bool):
raise APIError("case_sensitive must be a boolean")
raise exceptions.APIError("case_sensitive must be a boolean")

# special case for memo and memo_hex field searches
if table == "sends":
Expand Down Expand Up @@ -424,7 +414,7 @@ def adjust_get_sends_memo_filters(filters):
try:
filter_["value"] = bytes.fromhex(filter_["value"])
except ValueError as e: # noqa: F841
raise APIError("Invalid memo_hex value") # noqa: B904
raise exceptions.APIError("Invalid memo_hex value") # noqa: B904


def adjust_get_sends_results(query_result):
Expand Down Expand Up @@ -519,7 +509,7 @@ def run(self):
ledger_db, backend.bitcoind.getblockcount()
)
interval = interval_if_ready
except (BackendError, exceptions.DatabaseError) as e:
except (exceptions.BackendError, exceptions.DatabaseError) as e:
interval = interval_if_not_ready
exception_name = e.__class__.__name__
exception_text = str(e)
Expand Down Expand Up @@ -572,7 +562,7 @@ def get_method(**kwargs):
try:
return get_rows(table=table, **kwargs)
except TypeError as e: # TODO: generalise for all API methods
raise APIError(str(e)) # noqa: B904
raise exceptions.APIError(str(e)) # noqa: B904

return get_method

Expand Down Expand Up @@ -660,7 +650,7 @@ def create_method(**kwargs):
@dispatcher.add_method
def get_messages(block_index):
if not isinstance(block_index, int):
raise APIError("block_index must be an integer.")
raise exceptions.APIError("block_index must be an integer.")
with LedgerDBConnectionPool().connection() as db:
messages = ledger.ledger.get_messages(db, block_index=block_index)
return messages
Expand All @@ -677,7 +667,7 @@ def get_messages_by_index(message_indexes):
]
for idx in message_indexes: # make sure the data is clean
if not isinstance(idx, int):
raise APIError("All items in message_indexes are not integers")
raise exceptions.APIError("All items in message_indexes are not integers")
with LedgerDBConnectionPool().connection() as db:
messages = ledger.ledger.get_messages(db, message_index_in=message_indexes)
return messages
Expand Down Expand Up @@ -705,7 +695,7 @@ def get_asset_info(assets=None, asset=None):
assets = [asset]

if not isinstance(assets, list):
raise APIError(
raise exceptions.APIError(
"assets must be a list of asset names, even if it just contains one entry"
)
assets_info = []
Expand Down Expand Up @@ -793,16 +783,16 @@ def get_blocks(block_indexes, min_message_index=None):
must_be_non_empty_list_int = "block_indexes must be a non-empty list of integers"

if not isinstance(block_indexes, (list, tuple)):
raise APIError(must_be_non_empty_list_int)
raise exceptions.APIError(must_be_non_empty_list_int)

if len(block_indexes) == 0:
raise APIError(must_be_non_empty_list_int)
raise exceptions.APIError(must_be_non_empty_list_int)

if len(block_indexes) >= 250:
raise APIError("can only specify up to 250 indexes at a time.")
raise exceptions.APIError("can only specify up to 250 indexes at a time.")
for block_index in block_indexes:
if not isinstance(block_index, int):
raise APIError(must_be_non_empty_list_int)
raise exceptions.APIError(must_be_non_empty_list_int)

with LedgerDBConnectionPool().connection() as db:
cursor = db.cursor()
Expand Down Expand Up @@ -1015,7 +1005,7 @@ def unpack(data_hex):
elif message_type_id == enhancedsend.ID:
unpacked = enhancedsend.unpack(message, CurrentState().current_block_index())
else:
raise APIError("unsupported message type")
raise exceptions.APIError("unsupported message type")
return message_type_id, unpacked

@dispatcher.add_method
Expand All @@ -1026,7 +1016,7 @@ def search_pubkey(pubkeyhash, provided_pubkeys=None):
@dispatcher.add_method
def get_dispenser_info(tx_hash=None, tx_index=None):
if tx_hash is None and tx_index is None:
raise APIError("You must provided a tx hash or a tx index")
raise exceptions.APIError("You must provided a tx hash or a tx index")

dispensers = []
with LedgerDBConnectionPool().connection() as db:
Expand Down Expand Up @@ -1057,7 +1047,7 @@ def get_dispenser_info(tx_hash=None, tx_index=None):
if oracle_price > 0:
satoshi_price = math.ceil((fiat_price / oracle_price) * config.UNIT)
else:
raise APIError("Last oracle price is zero")
raise exceptions.APIError("Last oracle price is zero")

return {
"tx_index": dispenser["tx_index"],
Expand Down Expand Up @@ -1264,7 +1254,7 @@ def handle_rest(path_args, flask_request):
filters=data_filter,
filterop=operator,
)
except APIError as error: # noqa: F841
except exceptions.APIError as error: # noqa: F841
return flask.Response("API Error", 400, mimetype="application/json")

# See which encoding to choose from.
Expand Down
4 changes: 0 additions & 4 deletions counterparty-core/counterpartycore/lib/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,6 @@ def welcome_message(action, server_configfile):
cprint(f"\n{'-' * 30} {action.upper()} {'-' * 30}\n", "green")


class VersionError(Exception):
pass


def main():
sentry.init()
# Post installation tasks
Expand Down
24 changes: 10 additions & 14 deletions counterparty-core/counterpartycore/lib/cli/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@
SPINNER_STYLE = "bouncingBar"


class ConfigurationError(Exception):
pass


def initialise(*args, **kwargs):
initialise_log_config(
verbose=kwargs.pop("verbose", 0),
Expand Down Expand Up @@ -277,9 +273,9 @@ def initialise_config(
try:
config.BACKEND_PORT = int(config.BACKEND_PORT)
if not (int(config.BACKEND_PORT) > 1 and int(config.BACKEND_PORT) < 65535):
raise ConfigurationError("invalid backend API port number")
raise exceptions.ConfigurationError("invalid backend API port number")
except: # noqa: E722
raise ConfigurationError( # noqa: B904
raise exceptions.ConfigurationError( # noqa: B904

Check warning

Code scanning / pylint

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number backend-port configuration parameter') from exc'. Warning

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number backend-port configuration parameter') from exc'.
"Please specific a valid port number backend-port configuration parameter"
)

Expand All @@ -293,7 +289,7 @@ def initialise_config(
if backend_password:
config.BACKEND_PASSWORD = backend_password
else:
raise ConfigurationError(
raise exceptions.ConfigurationError(
"Please specific a valid password backend-password configuration parameter"
)

Expand Down Expand Up @@ -366,9 +362,9 @@ def initialise_config(
try:
config.RPC_PORT = int(config.RPC_PORT)
if not (int(config.RPC_PORT) > 1 and int(config.RPC_PORT) < 65535):
raise ConfigurationError("invalid server API port number")
raise exceptions.ConfigurationError("invalid server API port number")
except: # noqa: E722
raise ConfigurationError( # noqa: B904
raise exceptions.ConfigurationError( # noqa: B904

Check warning

Code scanning / pylint

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'. Warning

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'.
"Please specific a valid port number rpc-port configuration parameter"
)

Expand Down Expand Up @@ -416,9 +412,9 @@ def initialise_config(
try:
config.API_PORT = int(config.API_PORT)
if not (int(config.API_PORT) > 1 and int(config.API_PORT) < 65535):
raise ConfigurationError("invalid server API port number")
raise exceptions.ConfigurationError("invalid server API port number")
except: # noqa: E722
raise ConfigurationError( # noqa: B904
raise exceptions.ConfigurationError( # noqa: B904

Check warning

Code scanning / pylint

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'. Warning

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'.
"Please specific a valid port number rpc-port configuration parameter"
)

Expand All @@ -439,9 +435,9 @@ def initialise_config(
try:
config.ZMQ_PUBLISHER_PORT = int(config.ZMQ_PUBLISHER_PORT)
if not (int(config.ZMQ_PUBLISHER_PORT) > 1 and int(config.ZMQ_PUBLISHER_PORT) < 65535):
raise ConfigurationError("invalid ZeroMQ publisher port number")
raise exceptions.ConfigurationError("invalid ZeroMQ publisher port number")
except: # noqa: E722
raise ConfigurationError( # noqa: B904
raise exceptions.ConfigurationError( # noqa: B904

Check warning

Code scanning / pylint

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'. Warning

Consider explicitly re-raising using 'except Exception as exc' and 'raise exceptions.ConfigurationError('Please specific a valid port number rpc-port configuration parameter') from exc'.
"Please specific a valid port number rpc-port configuration parameter"
)

Expand Down Expand Up @@ -526,7 +522,7 @@ def initialise_config(

if electrs_url:
if not helpers.is_url(electrs_url):
raise ConfigurationError("Invalid Electrs URL")
raise exceptions.ConfigurationError("Invalid Electrs URL")
config.ELECTRS_URL = electrs_url
else:
if config.NETWORK_NAME == "testnet":
Expand Down
52 changes: 45 additions & 7 deletions counterparty-core/counterpartycore/lib/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#! /usr/bin/python3


class DatabaseError(Exception):
pass

Expand Down Expand Up @@ -53,10 +50,8 @@ class PushDataDecodeError(DecodeError):
pass


class BTCOnlyError(MessageError):
def __init__(self, msg, decoded_tx=None):
super(BTCOnlyError, self).__init__(msg)
self.decoded_tx = decoded_tx
class BTCOnlyError(Exception):
pass


class BalanceError(Exception):
Expand Down Expand Up @@ -177,3 +172,46 @@ class QuantityError(Exception):

class RPCError(Exception):
pass


class APIError(Exception):
pass


class BackendError(Exception):
pass


class ConsensusError(Exception):
pass


class SanityError(Exception):
pass


class VersionError(Exception):
def __init__(self, message, required_action, from_block_index=None):
super(VersionError, self).__init__(message)

Check warning

Code scanning / pylint

Consider using Python 3 style super() without arguments. Warning

Consider using Python 3 style super() without arguments.
self.required_action = required_action
self.from_block_index = from_block_index


class VersionUpdateRequiredError(Exception):
pass


class ConfigurationError(Exception):
pass


class DebitError(Exception):
pass


class CreditError(Exception):
pass


class ServerNotReady(Exception):
pass
Loading

0 comments on commit 21ee60f

Please sign in to comment.