generate_wallet.ts
1 import { Command } from 'commander'; 2 import { isResultOk, resultError } from '../result'; 3 4 const DEFAULT_MAX_ATTEMPTS = 1_000_000; 5 const WORKER_PATH = '../workers/generate_wallet_worker.ts'; // Correct path to worker 6 7 // Define message types for worker communication 8 interface WorkerStartMessage { 9 type: 'start'; 10 prefix: string; 11 maxAttempts: number; 12 } 13 14 interface WorkerStopMessage { 15 type: 'stop'; 16 ok: boolean; 17 value?: { address: string; secretKey: string }; 18 error?: Error; 19 } 20 21 type WorkerMessage = WorkerStartMessage | WorkerStopMessage; 22 23 24 export function generateWalletCommand(): Command { 25 const command = new Command('generate_wallet'); 26 27 command 28 .argument('<prefix>', 'The prefix for the wallet') 29 .option('-m, --maxAttempts <number>', 'Maximum attempts', String(DEFAULT_MAX_ATTEMPTS)) 30 .option('-c, --cores <number>', 'Number of cores to use for parallel generation using Bun workers', '1') 31 .action(async (prefix: string, options) => { 32 console.log(`Prefix: ${prefix}`); 33 34 const maxAttempts = parseInt(options.maxAttempts, 10); 35 const cores = parseInt(options.cores, 10); 36 37 const attemptsPerCore = Math.floor(maxAttempts / cores); 38 const remainingAttempts = maxAttempts % cores; 39 40 const workers = Array.from({ length: cores }, (_, index) => { 41 const worker = new Worker(new URL(WORKER_PATH, import.meta.url)); // Use 'new Worker' 42 const workerAttempts = index === 0 ? attemptsPerCore + remainingAttempts : attemptsPerCore; 43 const startMessage: WorkerStartMessage = { 44 type: 'start', 45 prefix, 46 maxAttempts: workerAttempts, 47 }; 48 worker.postMessage(startMessage); 49 return worker; 50 }); 51 52 let foundWallet = false; 53 let workerPromisePairs = []; 54 const walletResponses = await Promise.all(workers.map(worker => { 55 return new Promise(resolve => { 56 workerPromisePairs.push({ worker, resolve }); 57 58 worker.onmessage = (event: MessageEvent) => { 59 console.debug(`yapped from ${worker.threadId}`); 60 if (!foundWallet) { 61 console.debug(`terminating workers because I found ${event.data}`); 62 const threadId = worker.threadId; 63 64 //filter own thread out of list before foreach. 65 workerPromisePairs.filter(p => p.worker.threadId !== threadId).forEach(p => { 66 console.debug(`stopping worker ${p.worker.threadId}`); 67 p.worker.terminate(); 68 p.resolve(resultError(new Error(`stopped ${p.worker.threadId}`))); 69 70 }); 71 foundWallet = true; 72 } 73 resolve(event.data); 74 worker.terminate(); 75 }; 76 worker.onerror = (error) => { // Handle worker errors 77 console.debug(`clapped from: ${worker.threadId}`); 78 worker.terminate(); 79 resolve({ ok: false, error: new Error(`Worker error: ${error.message}`) }); 80 }; 81 worker.addEventListener('open', () => { 82 console.debug(`worker started: ${worker.threadId}`) 83 }); 84 }); 85 })) as any[]; 86 87 walletResponses.forEach(response => { 88 89 if (isResultOk(response)) { 90 const wallet = response.value; 91 console.log(`\nAddress: ${wallet.address}`); 92 console.log(`Secret: ${wallet.secretKey}`); 93 } else { 94 if (response.error.message.startsWith('Error: stopped')) { 95 console.error(`\nError generating wallet: ${response.error}`); 96 console.error(`Details: ${response.error.message}`); // Log error details 97 } 98 } 99 }); 100 }); 101 102 return command; 103 }