From 5c493f05ef8043cbb0e1060ab672a540c2445dfc Mon Sep 17 00:00:00 2001 From: grdddj Date: Wed, 13 Apr 2022 10:35:43 +0200 Subject: [PATCH] snippet: unused Monero mapping --- tools/snippets/README.md | 7 +- tools/snippets/monero_unused_functions.py | 110 ++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 tools/snippets/monero_unused_functions.py diff --git a/tools/snippets/README.md b/tools/snippets/README.md index 16ef369fd..29e2180fd 100644 --- a/tools/snippets/README.md +++ b/tools/snippets/README.md @@ -5,12 +5,15 @@ This folder is storing non-essential scripts (snippets, gists), which may howeve These scripts do not need to have a high standard, but each of those should have a short description of what is it doing. -## sign_tx.py - [file](./sign_tx.py) +## [sign_tx.py](./sign_tx.py) - signing of BTC transactions that can be spent with currently connected device - need to specify the input UTXOs and the output address(es) - generates a serialized transaction that can be then announced to the network -## unify_test_files.py - [file](./unify_test_files.py) +## [unify_test_files.py](./unify_test_files.py) - enforcing some readability practices in test files and possibly adding information about paths and addresses used - need to specify all the files to be modified and optionally the path-to-address translation file - rewrites the files in place, or just prints the intended changes when run with `-c` + +## [monero_unused_functions](./monero_unused_functions.py) +- Find functions from Monero cryptography that are not used. diff --git a/tools/snippets/monero_unused_functions.py b/tools/snippets/monero_unused_functions.py new file mode 100644 index 000000000..bd6358289 --- /dev/null +++ b/tools/snippets/monero_unused_functions.py @@ -0,0 +1,110 @@ +""" +Find out which functions are unused in the Monero app - based on +`monero.pyi` file. +""" + +import subprocess +import sys +from pathlib import Path +from typing import Any, Dict, List, Set + +CURRENT_DIR = Path(__file__).resolve().parent +ROOT_DIR = CURRENT_DIR.parent.parent + +HELPER_FILE = ROOT_DIR / "core/src/apps/monero/xmr/crypto_helpers.py" +MOCK_FILE = ROOT_DIR / "core/mocks/generated/trezorcrypto/monero.pyi" + + +def generate_function_mapping() -> Dict[str, List[str]]: + """Look at all Monero functions and generate a mapping of their usage""" + + # Load all the function names in .pyi file + pyi_functions: Set[str] = set() + with open(MOCK_FILE, "r") as f: + lines = f.readlines() + for line in lines: + if line.startswith("def"): + f_name = line.split("(")[0].split(" ")[1] + pyi_functions.add(f_name) + + # Load definitions of helper functions + helper_func_defs: Dict[str, str] = {} + with open(HELPER_FILE, "r") as f: + lines = f.readlines() + current_func = "" + for line in lines: + if line.startswith("def"): + current_func = line.split("(")[0].split(" ")[1] + helper_func_defs[current_func] = line + elif not current_func: + continue + else: + helper_func_defs[current_func] += line + + # Try to connect function names with helper definitions + func_mapping: Dict[str, List[str]] = {} + for func_name in pyi_functions: + func_mapping[func_name] = [] + for func_def_name, func_code in helper_func_defs.items(): + if f".{func_name}(" in func_code: + func_mapping[func_name].append(func_def_name) + + # Functions may not be used in helper file, alias them to themselves + if not func_mapping[func_name]: + func_mapping[func_name] = [func_name] + + return func_mapping + + +def check_usage_of_functions(func_mapping: Dict[str, List[str]]) -> None: + """Go through all the functions and check if they are used in the Monero app. + + Generates a report and exits with an appropriate exit code. + """ + + # Include boolean field to know what is used + is_used_mappings: Dict[str, Dict[str, Any]] = {} + for func_name, mapping in func_mapping.items(): + is_used_mappings[func_name] = {"mapping": mapping, "is_used": False} + + # Check if any of the mapping names is used - and mark it as used if so + for func_name in is_used_mappings: + for mapping in is_used_mappings[func_name]["mapping"]: + is_there = _is_used(mapping) + if is_there: + is_used_mappings[func_name]["is_used"] = True + break + + # Find unused functions and generate a report + unused_functions = { + fc: val for fc, val in is_used_mappings.items() if not val["is_used"] + } + if not unused_functions: + print("SUCCESS: no functions are unused") + sys.exit(0) + else: + print(f"{len(unused_functions)} unused functions:") + for func, values in unused_functions.items(): + print(func, values) + sys.exit(1) + + +def _is_used(func_name: str) -> bool: + """Find function usage in the Monero app or in test files""" + + cmds = [ + rf'grep -r ".{func_name}\b" {ROOT_DIR}/core/src/apps/monero', + rf'grep -r ".{func_name}\b" {ROOT_DIR}/core/tests', + ] + + for cmd in cmds: + grep_result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE) + if grep_result.returncode == 0: + return True + + return False + + +if "__main__" == __name__: + func_mapping = generate_function_mapping() + check_usage_of_functions(func_mapping)