/ agent / commands / generate_wallet.ts
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  }