Untitled

 avatar
unknown
plain_text
9 days ago
10 kB
7
Indexable
import sys
import argparse
import itertools
import multiprocessing
from tqdm import tqdm

# Keep the core Solana derivation and file reading functions
from bip_utils import Bip39SeedGenerator, Bip44, Bip44Coins
from solders.keypair import Keypair

# --- Core Derivation and File Functions (Unchanged) ---

def get_solana_address(mnemonic: str, passphrase: str = "") -> str:
    try:
        seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase)
        bip44_mst_ctx = Bip44.FromSeed(seed_bytes, Bip44Coins.SOLANA)
        bip44_acc_ctx = bip44_mst_ctx.Purpose().Coin().Account(0)
        private_key_bytes = bip44_acc_ctx.PrivateKey().Raw().ToBytes()
        keypair = Keypair.from_seed(private_key_bytes)
        return str(keypair.pubkey())
    except ValueError:
        return "invalid mnemonic"

def read_lines_to_list(filename: str) -> list[str]:
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return [line.strip() for line in f]
    except FileNotFoundError:
        print(f"āŒ Error: The file '{filename}' was not found.")
        sys.exit(1)

def read_lines_to_set(filename: str) -> set[str]:
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return {line.strip() for line in f if line.strip()}
    except FileNotFoundError:
        print(f"āŒ Error: The file '{filename}' was not found.")
        sys.exit(1)

def get_first_line(filename: str) -> str | None:
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            for line in f:
                if line.strip():
                    return line.strip()
        return None
    except FileNotFoundError:
        print(f"āŒ Error: The file '{filename}' was not found.")
        sys.exit(1)

# --- Typo Generation Logic (Unchanged) ---

class TypoGenerator:
    def __init__(self, args):
        self.max_typos = args.typos
        self.typo_types = []
        if args.typos_capslock: self.typo_types.append('capslock')
        if args.typos_swap: self.typo_types.append('swap')
        if args.typos_repeat: self.typo_types.append('repeat')
        if args.typos_delete: self.typo_types.append('delete')
        if args.typos_case: self.typo_types.append('case')
        if args.typos_map: self.typo_types.append('map')
        self.typos_map = self._parse_typos_map(args.typos_map) if args.typos_map else {}

    def _parse_typos_map(self, filename):
        typos_map = {}
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                for line in f:
                    parts = line.strip().split()
                    if len(parts) >= 2:
                        chars_to_replace = parts[0]
                        replacements = parts[1]
                        for char in chars_to_replace:
                            typos_map[char] = replacements
        except FileNotFoundError:
            print(f"āŒ Error: Typos map file '{filename}' not found.")
            sys.exit(1)
        return typos_map

    def _apply_typo(self, password, typo_type, index):
        if typo_type == 'capslock':
            return password.swapcase()
        if typo_type == 'swap':
            return password[:index] + password[index+1] + password[index] + password[index+2:]
        if typo_type == 'repeat':
             return password[:index] + password[index] + password[index:]
        if typo_type == 'delete':
            return password[:index] + password[index+1:]
        if typo_type == 'case':
            char = password[index]
            if char.islower(): return password[:index] + char.upper() + password[index+1:]
            if char.isupper(): return password[:index] + char.lower() + password[index+1:]
        if typo_type == 'map':
            char = password[index]
            if char in self.typos_map:
                for replacement in self.typos_map[char]:
                    yield password[:index] + replacement + password[index+1:]
                return
        yield password

    def generate(self, base_password):
        yield base_password
        if self.max_typos == 0 or not self.typo_types or not base_password:
            return
        possible_typos = []
        if 'capslock' in self.typo_types:
            possible_typos.append(('capslock', 0))
        char_op_types = [t for t in self.typo_types if t in ('delete', 'case', 'map', 'repeat')]
        for typo_type in char_op_types:
            for index in range(len(base_password)):
                possible_typos.append((typo_type, index))
        if 'swap' in self.typo_types and len(base_password) > 1:
            for index in range(len(base_password) - 1):
                possible_typos.append(('swap', index))
        generated = {base_password}
        for k in range(1, self.max_typos + 1):
            for combo in itertools.combinations(possible_typos, k):
                indices = [c[1] for c in combo if c[0] != 'capslock']
                if len(indices) != len(set(indices)):
                    continue
                temp_passwords = {base_password}
                for typo_type, index in combo:
                    new_temp_passwords = set()
                    for pwd in temp_passwords:
                        for result in self._apply_typo(pwd, typo_type, index):
                            if result is not None and result not in generated:
                                new_temp_passwords.add(result)
                                generated.add(result)
                                yield result
                    temp_passwords = new_temp_passwords


# --- NEW: Initializer and Worker Functions for Multiprocessing ---

def init_worker(mnemonic_arg, addresses_arg, typo_gen_arg):
    """
    Initializer for the worker pool. Sets up global variables for each worker process.
    """
    global static_mnemonic, addresses_to_find, typo_gen
    static_mnemonic = mnemonic_arg
    addresses_to_find = addresses_arg
    typo_gen = typo_gen_arg

def worker(base_pass):
    """
    This function is executed by each worker process.
    It checks one base passphrase and all its typo variations.
    """
    # These globals are set by the init_worker function
    for final_pass in typo_gen.generate(base_pass):
        derived_address = get_solana_address(static_mnemonic, final_pass)
        if derived_address in addresses_to_find:
            return (base_pass, final_pass, derived_address)
    return None

# --- Main Execution Block (Refactored for Multiprocessing) ---

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description="A tool to find a Solana address by testing passphrases and typo variations against a mnemonic.",
        formatter_class=argparse.RawTextHelpFormatter
    )
    # Add all arguments as before...
    parser.add_argument('--seed-file', default='seed_phrases.txt', help='File with the mnemonic phrase.')
    parser.add_argument('--pass-file', default='passphrases.txt', help='File with the list of base passphrases.')
    parser.add_argument('--address-file', default='addresses_to_find.txt', help='File with target Solana addresses.')
    parser.add_argument('--typos', type=int, default=0, help='Maximum number of typos to generate per passphrase.')
    parser.add_argument('--typos-capslock', action='store_true', help='Enable capslock typo (whole word).')
    parser.add_argument('--typos-swap', action='store_true', help='Enable adjacent character swap typo.')
    parser.add_argument('--typos-repeat', action='store_true', help='Enable character repeat typo.')
    parser.add_argument('--typos-delete', action='store_true', help='Enable character delete typo.')
    parser.add_argument('--typos-case', action='store_true', help='Enable single character case change typo.')
    parser.add_argument('--typos-map', help='Path to a typos map file (e.g., "sS $5").')
    parser.add_argument('--workers', type=int, default=multiprocessing.cpu_count(), help='Number of CPU cores to use.')


    args = parser.parse_args()

    print("šŸ” Solana Address Finder with Typo Generation (Multithreaded)")
    print("-" * 60)

    # Load data that all processes will need
    main_static_mnemonic = get_first_line(args.seed_file)
    if not main_static_mnemonic: sys.exit(f"āŒ Error: No mnemonic found in '{args.seed_file}'.")
    if get_solana_address(main_static_mnemonic) == "invalid mnemonic": sys.exit("āŒ Error: The mnemonic phrase is not valid.")
    
    main_base_passphrases = read_lines_to_list(args.pass_file)
    main_addresses_to_find = read_lines_to_set(args.address_file)
    main_typo_gen = TypoGenerator(args)

    print(f"Mnemonic Loaded: '{' '.join(main_static_mnemonic.split()[:3])}...'")
    print(f"Base Passphrases: {len(main_base_passphrases):,}")
    print(f"Target Addresses: {len(main_addresses_to_find):,}")
    if args.typos > 0: print(f"Max Typos: {args.typos}")
    print(f"Using {args.workers} CPU cores for processing...")
    print("-" * 60)
    
    found_match = None

    # Define the arguments for the initializer
    init_args = (main_static_mnemonic, main_addresses_to_find, main_typo_gen)

    # Create a pool of worker processes
    try:
        with multiprocessing.Pool(processes=args.workers, initializer=init_worker, initargs=init_args) as pool:
            results = pool.imap_unordered(worker, main_base_passphrases)
            
            with tqdm(total=len(main_base_passphrases), desc="Processing Passphrases") as pbar:
                for result in results:
                    pbar.update(1)
                    if result:
                        found_match = result
                        pool.terminate()
                        break
    except KeyboardInterrupt:
        print("\nšŸ›‘ Search interrupted by user.")
    
    print("-" * 60)
    if found_match:
        base_p, final_p, address = found_match
        print("\n" + "šŸŽ‰" * 20)
        print("šŸŽ‰ MATCH FOUND! šŸŽ‰".center(40))
        print("šŸŽ‰" * 20)
        print(f"šŸ”‘ Mnemonic : {' '.join(main_static_mnemonic.split()[:3])}...")
        print(f"šŸ”’ Passphrase: '{final_p}' (derived from '{base_p}')")
        print(f"šŸ“ Address  : {address}")
        print("āœ… Search complete. A matching passphrase was found!")
    else:
        print("āŒ Search complete. No matching passphrase was found.")
        
Editor is loading...
Leave a Comment