Advertisement
Guest User

Untitled

a guest
Apr 12th, 2025
133
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 26.43 KB | None | 0 0
  1. DERIVATION_PATHS = [
  2.     "m/44'/0'/0'/0/0",  # BIP44 legacy
  3.     "m/84'/0'/0'/0/0",  # BIP84 native segwit
  4.     "m/49'/0'/0'/0/0",  # BIP49 segwit
  5.     "m/86'/0'/0'/0/0"   # BIP86 taproot
  6. ]
  7.  
  8. import itertools
  9. import hashlib
  10. import binascii
  11. import re
  12. import time
  13. import sys
  14. import os
  15. import json
  16. import pickle
  17. from mnemonic import Mnemonic
  18. from loguru import logger
  19. import concurrent.futures
  20. import argparse
  21. from tqdm import tqdm
  22. import asyncio
  23. import aiohttp
  24. from bitcoin import *
  25. from bip32utils import BIP32Key
  26. from multiprocessing import cpu_count, Manager, Pool, shared_memory
  27. import numpy as np
  28.  
  29. class StateManager:
  30.     """Manages the state persistence of the application"""
  31.    
  32.     def __init__(self, state_file="mnemonic_scanner_state.pkl"):
  33.         self.state_file = state_file
  34.         self.state = {
  35.             "processed_permutations": set(),
  36.             "valid_mnemonics": set(),
  37.             "active_wallets": set(),
  38.             "current_paragraph_index": 0,
  39.             "current_batch_index": 0,
  40.             "total_processed": 0
  41.         }
  42.        
  43.     def save_state(self):
  44.         """Save current state to a file"""
  45.         try:
  46.             with open(self.state_file, 'wb') as f:
  47.                 pickle.dump(self.state, f)
  48.             logger.info(f"State saved to {self.state_file}")
  49.             return True
  50.         except Exception as e:
  51.             logger.error(f"Error saving state: {e}")
  52.             return False
  53.            
  54.     def load_state(self):
  55.         """Load state from a file if it exists"""
  56.         if os.path.exists(self.state_file):
  57.             try:
  58.                 with open(self.state_file, 'rb') as f:
  59.                     self.state = pickle.load(f)
  60.                 logger.info(f"State loaded from {self.state_file}")
  61.                 logger.info(f"Resuming from paragraph {self.state['current_paragraph_index']}, "
  62.                            f"batch {self.state['current_batch_index']}")
  63.                 logger.info(f"Previously found {len(self.state['valid_mnemonics'])} valid mnemonics "
  64.                            f"and {len(self.state['active_wallets'])} active wallets")
  65.                 return True
  66.             except Exception as e:
  67.                 logger.error(f"Error loading state: {e}")
  68.                 return False
  69.         return False
  70.        
  71.     def update_state(self, key, value):
  72.         """Update a specific state key"""
  73.         if key in self.state:
  74.             self.state[key] = value
  75.            
  76.     def add_processed_permutation(self, perm_hash):
  77.         """Add a processed permutation hash to the state"""
  78.         self.state["processed_permutations"].add(perm_hash)
  79.        
  80.     def add_valid_mnemonic(self, mnemonic):
  81.         """Add a valid mnemonic to the state"""
  82.         self.state["valid_mnemonics"].add(mnemonic)
  83.        
  84.     def add_active_wallet(self, mnemonic, address, path):
  85.         """Add an active wallet to the state"""
  86.         self.state["active_wallets"].add((mnemonic, address, path))
  87.        
  88.     def get_state(self):
  89.         """Get the current state"""
  90.         return self.state
  91.        
  92.     def is_permutation_processed(self, perm_hash):
  93.         """Check if a permutation hash has been processed"""
  94.         return perm_hash in self.state["processed_permutations"]
  95.        
  96.     def increment_processed(self):
  97.         """Increment the total processed counter"""
  98.         self.state["total_processed"] += 1
  99.  
  100. def hash_permutation(perm):
  101.     """Generate a hash for a permutation to track processed items"""
  102.     perm_str = ' '.join(perm)
  103.     return hashlib.md5(perm_str.encode()).hexdigest()
  104.  
  105. def is_valid_mnemonic(words, lang='english'):
  106.     """Check if a mnemonic phrase is valid per BIP39 specs"""
  107.     try:
  108.         mnemo = Mnemonic(lang)
  109.         return mnemo.check(' '.join(words))
  110.     except Exception as e:
  111.         logger.error(f"Error checking mnemonic: {e}")
  112.         return False
  113.  
  114. def get_address_from_mnemonic(mnemonic, path="m/44'/0'/0'/0/0"):
  115.     """Generate Bitcoin address from mnemonic"""
  116.     try:
  117.         seed = Mnemonic.to_seed(mnemonic)
  118.         root_key = BIP32Key.fromEntropy(seed)
  119.        
  120.         # Parse derivation path
  121.         parts = path.split('/')
  122.         child_key = root_key
  123.        
  124.         for part in parts[1:]:
  125.             if part.endswith("'"):
  126.                 # Hardened derivation
  127.                 index = int(part[:-1]) + 0x80000000
  128.             else:
  129.                 # Normal derivation
  130.                 index = int(part)
  131.             child_key = child_key.ChildKey(index)
  132.        
  133.         # Get address
  134.         address = child_key.Address()
  135.         return address
  136.     except Exception as e:
  137.         logger.error(f"Error generating address: {e}")
  138.         return None
  139.  
  140. def extract_bip39_words(text):
  141.     """Extract words that are in the BIP39 wordlist from text"""
  142.     mnemo = Mnemonic('english')
  143.     words = re.findall(r'\b[a-zA-Z]+\b', text.lower())
  144.     return [word for word in words if word in mnemo.wordlist]
  145.  
  146. async def check_address_balance_async(session, address):
  147.     """Check the balance of a Bitcoin address using various APIs with fallback"""
  148.     apis = [
  149.         f"https://mempool.space/api/address/{address}",
  150.     ]
  151.    
  152.     for api_url in apis:
  153.         try:
  154.             async with session.get(api_url, timeout=5) as response:
  155.                 if response.status == 200:
  156.                     data = await response.json()
  157.                    
  158.                     # Different APIs might have different response formats
  159.                     if "chain_stats" in data:
  160.                         # mempool.space format
  161.                         chain_stats = data.get('chain_stats', {})
  162.                         mempool_stats = data.get('mempool_stats', {})
  163.                        
  164.                         tx_count = chain_stats.get('tx_count', 0) + mempool_stats.get('tx_count', 0)
  165.                         funded_sum = chain_stats.get('funded_txo_sum', 0) + mempool_stats.get('funded_txo_sum', 0)
  166.                         spent_sum = chain_stats.get('spent_txo_sum', 0) + mempool_stats.get('spent_txo_sum', 0)
  167.                        
  168.                         balance = funded_sum - spent_sum
  169.                        
  170.                         return True, {
  171.                             "balance": balance,
  172.                             "tx_count": tx_count,
  173.                             "data": data
  174.                         }
  175.                     elif "funded_txo_sum" in data:
  176.                         # blockstream.info format
  177.                         balance = data.get('chain_stats', {}).get('funded_txo_sum', 0) - data.get('chain_stats', {}).get('spent_txo_sum', 0)
  178.                         tx_count = data.get('chain_stats', {}).get('tx_count', 0)
  179.                        
  180.                         return True, {
  181.                             "balance": balance,
  182.                             "tx_count": tx_count,
  183.                             "data": data
  184.                         }
  185.                     elif "balance" in data:
  186.                         # Generic format
  187.                         return True, {
  188.                             "balance": data.get('balance', 0),
  189.                             "tx_count": data.get('tx_count', 0),
  190.                             "data": data
  191.                         }
  192.                    
  193.                     return True, {"data": data}
  194.         except asyncio.TimeoutError:
  195.             logger.debug(f"Timeout with API {api_url}")
  196.             continue
  197.         except Exception as e:
  198.             logger.debug(f"Error with API {api_url}: {e}")
  199.             continue
  200.    
  201.     return False, None
  202.  
  203. def factorial(n):
  204.     """Calculate factorial of n"""
  205.     if n <= 1:
  206.         return 1
  207.     result = 1
  208.     for i in range(2, n + 1):
  209.         result *= i
  210.     return result
  211.  
  212. # Function to process a batch of permutations in parallel
  213. def process_batch_permutations(batch, state_manager=None):
  214.     """Process a batch of permutations in parallel, returning valid mnemonics"""
  215.     valid_mnemonics = []
  216.     processed_hashes = []
  217.    
  218.     # Local mnemonic validator for efficiency
  219.     mnemo = Mnemonic('english')
  220.    
  221.     for perm in batch:
  222.         perm_str = ' '.join(perm)
  223.         perm_hash = hashlib.md5(perm_str.encode()).hexdigest()
  224.         processed_hashes.append(perm_hash)
  225.        
  226.         # Check if valid mnemonic
  227.         if mnemo.check(perm_str):
  228.             valid_mnemonics.append(perm_str)
  229.    
  230.     return valid_mnemonics, processed_hashes
  231.  
  232. def process_permutation(perm, state_manager=None):
  233.     """Process a single permutation of words, returning True if valid"""
  234.     perm_hash = hash_permutation(perm)
  235.    
  236.     # Check if already processed using state manager
  237.     if state_manager and state_manager.is_permutation_processed(perm_hash):
  238.         return False, None, perm_hash
  239.    
  240.     if is_valid_mnemonic(perm):
  241.         mnemonic = ' '.join(perm)
  242.         return True, mnemonic, perm_hash
  243.    
  244.     return False, None, perm_hash
  245.  
  246. class PermutationGenerator:
  247.     """Memory-efficient generator of permutations with state tracking"""
  248.    
  249.     def __init__(self, elements, batch_size=10000, start_batch=0, state_manager=None):
  250.         self.elements = elements
  251.         self.batch_size = batch_size
  252.         self.state_manager = state_manager
  253.         self.current_batch = start_batch
  254.         self.total_perms = factorial(len(elements))
  255.        
  256.     def __iter__(self):
  257.         indices = list(range(len(self.elements)))
  258.         cycles = list(range(len(self.elements), 0, -1))
  259.        
  260.         # Skip to the current batch if needed
  261.         if self.current_batch > 0:
  262.             skip_count = self.current_batch * self.batch_size
  263.             logger.info(f"Skipping {skip_count:,} permutations to resume from batch {self.current_batch}")
  264.            
  265.             # Efficiently skip permutations
  266.             count = 0
  267.             while count < skip_count and count < self.total_perms:
  268.                 # Find the largest mobile element
  269.                 for i in range(len(self.elements)-1, -1, -1):
  270.                     cycles[i] -= 1
  271.                     if cycles[i] == 0:
  272.                         indices[i:] = indices[i+1:] + indices[i:i+1]
  273.                         cycles[i] = len(self.elements) - i
  274.                     else:
  275.                         j = cycles[i]
  276.                         indices[i], indices[-j] = indices[-j], indices[i]
  277.                         count += 1
  278.                         break
  279.                 else:
  280.                     break
  281.        
  282.         batch = []
  283.         remain_count = 0
  284.        
  285.         while remain_count < self.total_perms:
  286.             perm = tuple(self.elements[i] for i in indices)
  287.             batch.append(perm)
  288.             remain_count += 1
  289.            
  290.             if len(batch) >= self.batch_size:
  291.                 self.current_batch += 1
  292.                 if self.state_manager:
  293.                     self.state_manager.update_state("current_batch_index", self.current_batch)
  294.                 yield batch
  295.                 batch = []
  296.            
  297.             # Get next permutation
  298.             for i in range(len(self.elements)-1, -1, -1):
  299.                 cycles[i] -= 1
  300.                 if cycles[i] == 0:
  301.                     indices[i:] = indices[i+1:] + indices[i:i+1]
  302.                     cycles[i] = len(self.elements) - i
  303.                 else:
  304.                     j = cycles[i]
  305.                     indices[i], indices[-j] = indices[-j], indices[i]
  306.                     break
  307.             else:
  308.                 break
  309.        
  310.         if batch:
  311.             yield batch
  312.  
  313. async def check_addresses_batch_async(mnemonics, session, derive_paths=None, state_manager=None):
  314.     """Check a batch of mnemonics against blockchain APIs"""
  315.     if derive_paths is None:
  316.         derive_paths = DERIVATION_PATHS
  317.    
  318.     active_wallets = []
  319.    
  320.     # Create tasks for each mnemonic and path combination
  321.     tasks = []
  322.     for mnemonic in mnemonics:
  323.         for path in derive_paths:
  324.             address = get_address_from_mnemonic(mnemonic, path)
  325.             if address:
  326.                 tasks.append((mnemonic, address, path))
  327.    
  328.     # Process in smaller batches to avoid overwhelming APIs
  329.     batch_size = 20  # Adjust based on API rate limits
  330.     for i in range(0, len(tasks), batch_size):
  331.         current_batch = tasks[i:i+batch_size]
  332.         subtasks = []
  333.        
  334.         for mnemonic, address, path in current_batch:
  335.             subtasks.append(check_address_balance_async(session, address))
  336.        
  337.         # Wait for all subtasks to complete
  338.         results = await asyncio.gather(*subtasks)
  339.        
  340.         # Process results
  341.         for idx, (success, data) in enumerate(results):
  342.             mnemonic, address, path = current_batch[idx]
  343.            
  344.             if success and data:
  345.                 balance = data.get("balance", 0)
  346.                 tx_count = data.get("tx_count", 0)
  347.                
  348.                 if balance > 0 or tx_count > 0:
  349.                     logger.success(f"FOUND ACTIVE WALLET! Address: {address}, Path: {path}")
  350.                     logger.success(f"Mnemonic: {mnemonic}")
  351.                     logger.success(f"Balance: {balance} satoshis, Transactions: {tx_count}")
  352.                    
  353.                     active_wallets.append((mnemonic, address, path))
  354.                    
  355.                     if state_manager:
  356.                         state_manager.add_active_wallet(mnemonic, address, path)
  357.                         state_manager.save_state()
  358.    
  359.     return active_wallets
  360.  
  361. async def process_permutations_async(words, num_procs=None, batch_size=10000,
  362.                                     max_permutations=None, state_manager=None):
  363.     """Find valid mnemonics from permutations and check addresses in batches"""
  364.     if num_procs is None:
  365.         num_procs = cpu_count()
  366.    
  367.     total_permutations = factorial(len(words))
  368.     if max_permutations and total_permutations > max_permutations:
  369.         logger.warning(f"Limiting to {max_permutations:,} permutations out of {total_permutations:,}")
  370.         total_permutations = max_permutations
  371.    
  372.     logger.info(f"Total possible permutations: {total_permutations:,}")
  373.     logger.info(f"Using {num_procs} processes for computation")
  374.    
  375.     active_wallets = []
  376.     valid_mnemonics_total = 0
  377.     processed = 0
  378.    
  379.     # Initialize progress bar
  380.     pbar = tqdm(total=total_permutations, desc="Validating permutations")
  381.    
  382.     # Load the current batch index from state if available
  383.     start_batch = 0
  384.     if state_manager:
  385.         start_batch = state_manager.get_state()["current_batch_index"]
  386.         processed = state_manager.get_state()["total_processed"]
  387.         pbar.update(processed)
  388.    
  389.     # Initialize permutation generator with state
  390.     perm_gen = PermutationGenerator(words, batch_size, start_batch, state_manager)
  391.    
  392.     # Create a process pool for batch processing
  393.     with Pool(processes=num_procs) as pool:
  394.         # Start an HTTP session for checking addresses
  395.         connector = aiohttp.TCPConnector(limit=100)  # Increased connection limit
  396.         async with aiohttp.ClientSession(connector=connector) as session:
  397.             # Process permutations in batches
  398.             for batch in perm_gen:
  399.                 batch_start_time = time.time()
  400.                
  401.                 # Track processed permutations
  402.                 if max_permutations and processed >= max_permutations:
  403.                     logger.info(f"Reached maximum permutation count: {max_permutations:,}")
  404.                     break
  405.                
  406.                 # Skip this batch if we've already reached max permutations
  407.                 if max_permutations and processed + len(batch) > max_permutations:
  408.                     batch = batch[:max_permutations - processed]
  409.                
  410.                 # Split the batch into chunks for parallel processing
  411.                 chunk_size = max(1, len(batch) // num_procs)
  412.                 chunks = [batch[i:i + chunk_size] for i in range(0, len(batch), chunk_size)]
  413.                
  414.                 # Process all chunks in parallel
  415.                 batch_results = pool.map(process_batch_permutations, chunks)
  416.                
  417.                 # Collect valid mnemonics and process hashes
  418.                 batch_valid_mnemonics = []
  419.                 batch_processed_hashes = []
  420.                
  421.                 for valid_mnems, proc_hashes in batch_results:
  422.                     batch_valid_mnemonics.extend(valid_mnems)
  423.                     batch_processed_hashes.extend(proc_hashes)
  424.                
  425.                 # Update state
  426.                 if state_manager:
  427.                     for perm_hash in batch_processed_hashes:
  428.                         state_manager.add_processed_permutation(perm_hash)
  429.                    
  430.                     state_manager.update_state("total_processed",
  431.                                               state_manager.get_state()["total_processed"] + len(batch))
  432.                
  433.                 processed += len(batch)
  434.                 pbar.update(len(batch))
  435.                
  436.                 # Save state periodically
  437.                 if state_manager and processed % (batch_size * 5) == 0:
  438.                     state_manager.save_state()
  439.                
  440.                 # Check valid mnemonics in this batch
  441.                 if batch_valid_mnemonics:
  442.                     valid_mnemonics_total += len(batch_valid_mnemonics)
  443.                     logger.info(f"Found {len(batch_valid_mnemonics)} valid mnemonics in this batch")
  444.                    
  445.                     # Register valid mnemonics in state
  446.                     if state_manager:
  447.                         for mnemonic in batch_valid_mnemonics:
  448.                             state_manager.add_valid_mnemonic(mnemonic)
  449.                    
  450.                     # Check addresses for valid mnemonics
  451.                     batch_active_wallets = await check_addresses_batch_async(
  452.                         batch_valid_mnemonics, session, DERIVATION_PATHS, state_manager
  453.                     )
  454.                    
  455.                     if batch_active_wallets:
  456.                         logger.success(f"Found {len(batch_active_wallets)} active wallets in this batch!")
  457.                         active_wallets.extend(batch_active_wallets)
  458.                
  459.                 batch_end_time = time.time()
  460.                 batch_duration = batch_end_time - batch_start_time
  461.                 perms_per_second = len(batch) / batch_duration if batch_duration > 0 else 0
  462.                
  463.                 logger.debug(f"Batch processed in {batch_duration:.2f} seconds ({perms_per_second:.2f} perms/sec)")
  464.                
  465.                 # If we have any active wallets, save state
  466.                 if active_wallets and state_manager:
  467.                     state_manager.save_state()
  468.    
  469.     # Complete the progress bar
  470.     pbar.close()
  471.    
  472.     # Report statistics
  473.     logger.info(f"Processed {processed:,} permutations")
  474.     if valid_mnemonics_total > 0:
  475.         percentage = (valid_mnemonics_total / processed) * 100 if processed > 0 else 0
  476.         logger.success(f"Found {valid_mnemonics_total} valid mnemonics out of {processed:,} permutations")
  477.         logger.success(f"Valid permutations: {percentage:.6f}%")
  478.    
  479.     return active_wallets, valid_mnemonics_total, processed
  480.  
  481. async def process_paragraph_async(paragraph, max_combinations=None, num_procs=None, batch_size=10000,
  482.                                 state_manager=None):
  483.     """Process a paragraph: find valid permutations and check addresses in batches"""
  484.     bip39_words = extract_bip39_words(paragraph)
  485.     logger.info(f"Found {len(bip39_words)} BIP39 words: {', '.join(bip39_words)}")
  486.    
  487.     if len(bip39_words) < 12:
  488.         logger.warning("Not enough BIP39 words in this paragraph")
  489.         return []
  490.    
  491.     # Special case: exactly 12 words
  492.     if len(bip39_words) == 12:
  493.         logger.info("Testing words in original order first")
  494.         mnemonic = ' '.join(bip39_words)
  495.         if is_valid_mnemonic(bip39_words):
  496.             logger.success(f"Original order is valid mnemonic: {mnemonic}")
  497.            
  498.             async with aiohttp.ClientSession() as session:
  499.                 for path in DERIVATION_PATHS:
  500.                     addr = get_address_from_mnemonic(mnemonic, path)
  501.                     logger.info(f"Bitcoin address ({path}): {addr}")
  502.                    
  503.                     # Check if address has transaction history
  504.                     success, data = await check_address_balance_async(session, addr)
  505.                    
  506.                     if success and data:
  507.                         balance = data.get("balance", 0)
  508.                         tx_count = data.get("tx_count", 0)
  509.                        
  510.                         if balance > 0 or tx_count > 0:
  511.                             logger.success(f"FOUND ACTIVE WALLET! Address: {addr}")
  512.                             logger.success(f"Mnemonic: {mnemonic}")
  513.                             logger.success(f"Balance: {balance} satoshis, Transactions: {tx_count}")
  514.                            
  515.                             if state_manager:
  516.                                 state_manager.add_active_wallet(mnemonic, addr, path)
  517.                                 state_manager.save_state()
  518.                            
  519.                             return [(mnemonic, addr, path)]
  520.    
  521.     # Process word combinations
  522.     if len(bip39_words) > 12 and max_combinations:
  523.         logger.info(f"Testing combinations of 12 words from {len(bip39_words)} words")
  524.         # If more than 12 words, select combinations of 12
  525.         total_combinations = factorial(len(bip39_words)) // (factorial(len(bip39_words) - 12) * factorial(12))
  526.         logger.info(f"Total possible combinations: {total_combinations:,}")
  527.        
  528.         if max_combinations and total_combinations > max_combinations:
  529.             logger.warning(f"Too many combinations, limiting to {max_combinations}")
  530.             # Just use the first 12 words instead of random combinations
  531.             bip39_words = bip39_words[:12]
  532.             logger.info(f"Using first 12 words: {bip39_words}")
  533.    
  534.     # Start finding and checking valid permutations in batches
  535.     start_time = time.time()
  536.     logger.info(f"Finding and validating permutations with {num_procs or cpu_count()} parallel processes")
  537.    
  538.     active_wallets, valid_count, total_count = await process_permutations_async(
  539.         bip39_words, num_procs, batch_size, max_combinations, state_manager
  540.     )
  541.    
  542.     end_time = time.time()
  543.     duration = end_time - start_time
  544.    
  545.     # Report final statistics
  546.     logger.info(f"Processing completed in {duration:.2f} seconds")
  547.     if active_wallets:
  548.         logger.success(f"Found {len(active_wallets)} active wallets!")
  549.         for mnemonic, address, path in active_wallets:
  550.             logger.success(f"Mnemonic: {mnemonic}")
  551.             logger.success(f"Address: {address} (Path: {path})")
  552.     else:
  553.         logger.info("No active wallets found")
  554.    
  555.     return active_wallets
  556.  
  557. async def main_async():
  558.     # Parse command line arguments
  559.     parser = argparse.ArgumentParser(description='Bitcoin Mnemonic Scanner with Batch Processing')
  560.     parser.add_argument('--threads', type=int, default=None, help='Number of threads/processes to use (default: auto)')
  561.     parser.add_argument('--max-combinations', type=int, default=None, help='Maximum number of combinations to test')
  562.     parser.add_argument('--batch-size', type=int, default=10000, help='Batch size for permutation processing (default: 10000)')
  563.     parser.add_argument('--state-file', type=str, default="mnemonic_scanner_state.pkl", help='State file for resuming (default: mnemonic_scanner_state.pkl)')
  564.     parser.add_argument('--force-restart', action='store_true', help='Force restart and ignore existing state file')
  565.     parser.add_argument('--paragraphs-file', type=str, help='File containing paragraphs to scan, one per line')
  566.     args = parser.parse_args()
  567.    
  568.     # Set number of threads if not specified
  569.     if args.threads is None:
  570.         args.threads = cpu_count()
  571.    
  572.     logger.info(f"Starting BIP39 mnemonic search with {args.threads} threads...")
  573.    
  574.     # Initialize state manager
  575.     state_manager = StateManager(args.state_file)
  576.    
  577.     # Load state if not forcing restart
  578.     resumed = False
  579.     if not args.force_restart:
  580.         resumed = state_manager.load_state()
  581.    
  582.     # Load paragraphs
  583.     paragraphs = []
  584.     if args.paragraphs_file and os.path.exists(args.paragraphs_file):
  585.         with open(args.paragraphs_file, 'r') as f:
  586.             paragraphs = [line.strip() for line in f if line.strip()]
  587.         logger.info(f"Loaded {len(paragraphs)} paragraphs from {args.paragraphs_file}")
  588.     else:
  589.         # Default test paragraph
  590.         paragraphs = [
  591.             "This is a very long journey, don't expect that in few years everybody will achieve it. Have patience, perseverance and dedication into fighting this war against tyranny."
  592.         ]
  593.  
  594.     logger.info(f"Testing {len(paragraphs)} paragraphs for valid BIP39 mnemonics...")
  595.  
  596.     # Start from the last paragraph if resuming
  597.     start_index = 0
  598.     if resumed:
  599.         start_index = state_manager.get_state()["current_paragraph_index"]
  600.  
  601.     for i in range(start_index, len(paragraphs)):
  602.         p = paragraphs[i]
  603.         logger.info(f"\n=== Testing Paragraph {i+1}/{len(paragraphs)} ===")
  604.         logger.info(p)
  605.        
  606.         # Update current paragraph index in state
  607.         state_manager.update_state("current_paragraph_index", i)
  608.         state_manager.update_state("current_batch_index", 0)  # Reset batch index for new paragraph
  609.         state_manager.save_state()
  610.        
  611.         start_time = time.time()
  612.         active_wallets = await process_paragraph_async(
  613.             p, max_combinations=args.max_combinations,
  614.             num_procs=args.threads, batch_size=args.batch_size,
  615.             state_manager=state_manager
  616.         )
  617.        
  618.         end_time = time.time()
  619.         duration = end_time - start_time
  620.        
  621.         logger.info(f"Paragraph processing completed in {duration:.2f} seconds")
  622.         if active_wallets:
  623.             logger.success(f"Found {len(active_wallets)} active wallets in paragraph {i+1}")
  624.         else:
  625.             logger.info(f"No active wallets found in paragraph {i+1}")
  626.  
  627. def main():
  628.     # Handle keyboard interrupts gracefully
  629.     try:
  630.         asyncio.run(main_async())
  631.     except KeyboardInterrupt:
  632.         logger.warning("Process interrupted by user. State has been saved.")
  633.         logger.warning("Resume by running the same command without --force-restart flag.")
  634.         sys.exit(1)
  635.  
  636. if __name__ == "__main__":
  637.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement