Source code for lrclib.cryptographic_challenge_solver

""" A module that provides a class to solve a cryptographic challenge. """

# https://github.com/tranxuanthang/lrcget/blob/main/src-tauri/src/lrclib/challenge_solver.rs

import hashlib
import threading
from dataclasses import dataclass
from typing import List, Optional


[docs]@dataclass class Solution: """Class for storing the solution of a cryptographic challenge.""" prefix: str """The prefix string of the challenge.""" target_hex: str """The target hash in hexadecimal format.""" nonce: Optional[int] = None """The nonce that satisfies the target hash.""" @property def is_solved(self) -> bool: """Check if the challenge is solved.""" return self.nonce is not None
[docs]def is_nonce_valid(prefix: str, nonce: "int | str", target: bytes) -> bool: """Check if the given nonce satisfies the target hash.""" message = f"{prefix}{nonce}".encode() hash_value = hashlib.sha256(message).digest() return hash_value < target
[docs]def find_nonce( prefix: str, target: bytes, solution: Optional[Solution] = None, start: int = 0, step: int = 1, ) -> Solution: """Find the nonce that satisfies the target hash such that the hash of \ the prefix concatenated with the nonce is less than the target hash. """ if solution is None: solution = Solution(prefix, target.hex()) nonce = start while not solution.is_solved: if is_nonce_valid(prefix, nonce, target): solution.nonce = nonce print(f"Found nonce: {nonce}") break nonce += step return solution
[docs]class CryptoChallengeSolver: """Class for solving cryptographic challenges."""
[docs] @staticmethod def solve(prefix: str, target_hex: str, num_threads: int = 1): """Solve the cryptographic challenge. Parameters ---------- prefix : str The prefix string of the challenge. target_hex : str The target hash in hexadecimal format. num_threads : int The number of threads to use for solving the challenge. Returns ------- nonce : str The nonce that satisfies the target hash. """ target = bytes.fromhex(target_hex) step = num_threads threads: List[threading.Thread] = [] solution = Solution(prefix, target.hex()) for i in range(num_threads): start = i thread = threading.Thread( target=find_nonce, args=(prefix, target, solution, start, step), ) threads.append(thread) for thread in threads: thread.start() for thread in threads: thread.join() return str(solution.nonce)