UNPKG

bitcoin-tx-lib

Version:

A Typescript library for building and signing Bitcoin transactions

152 lines (131 loc) 4.97 kB
import { generateMnemonic, mnemonicToEntropy, entropyToMnemonic, mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; /** * Utility class for BIP-39 mnemonic generation and wordlist management. */ export class MnemonicUtils { /** * Generates a new BIP-39 mnemonic phrase. * * @param {number} [strength=128] - Entropy strength in bits. Common values: 128 (12 words), 256 (24 words). * @returns {string} A mnemonic phrase. * @throws {Error} If the provided strength is invalid or unsupported. */ public static generateMnemonic(strength: 128|256 = 128): string { const mnemonic = generateMnemonic(wordlist, strength) return mnemonic } /** * Retrieves the full BIP-39 wordlist or filters it by a search term. * * @param {string} [searchTerm] - Optional term to filter words. Case-insensitive and trimmed. * @returns {string[]} A list of matching words, or the entire list if no search term is given. */ public static getWords(searchTerm?: string): string[] { if(!searchTerm) return wordlist return wordlist.filter(w => { return w.includes(searchTerm.trim().toLowerCase()) }) } /** * Validates if the given mnemonic is valid according to BIP-39 rules. * * @param {string} mnemonic - The mnemonic phrase to validate. * @returns {boolean} True if valid, false otherwise. */ public static validateMnemonic(mnemonic: string): boolean { return validateMnemonic(mnemonic, wordlist); } /** * Converts a mnemonic phrase to its entropy (hex string). * * @param {string} mnemonic - The mnemonic to convert. * @returns {Uint8Array} The Uint8Array entropy. * @throws {Error} If the mnemonic is invalid. */ public static mnemonicToEntropy(mnemonic: string): Uint8Array { if (!this.validateMnemonic(mnemonic)) throw new Error('Invalid mnemonic.') return mnemonicToEntropy(mnemonic, wordlist) } /** * Converts entropy (hex string) to a BIP-39 mnemonic phrase. * * @param {Uint8Array} entropy - The entropy to convert. * @returns {string} The resulting mnemonic phrase. */ public static entropyToMnemonic(entropy: Uint8Array): string { this.validateEntropy(entropy) return entropyToMnemonic(entropy, wordlist) } /** * Converts a mnemonic phrase into a BIP-39 seed. * * @param {string} mnemonic - The mnemonic phrase. * @param {string} [passphrase] - Optional passphrase. * @returns {Uint8Array} The resulting seed as a byte array. */ public static mnemonicToSeed(mnemonic: string, passphrase: string = ''): Uint8Array { if (typeof passphrase !== 'string') throw new Error('Passphrase must be a string'); if (!this.validateMnemonic(mnemonic)) throw new Error('Invalid mnemonic') return mnemonicToSeedSync(mnemonic, passphrase) } /** * Returns a random word from the BIP-39 wordlist. * * @returns {string} A randomly selected word. */ public static getRandomWord(): string { const index = Math.floor(Math.random() * wordlist.length); return wordlist[index]; } private static validateEntropy(entropy: Uint8Array) { const validLengths: number[] = [16, 20, 24, 28, 32] if(!validLengths.includes(entropy.length)) throw new Error("Invalid size entropy") if(this.isHighlyRepetitive(entropy)) throw new Error("Low entropy, unsafe entropy level") // if(!this.isEntropyStrong(entropy)) // throw new Error("Low entropy, unsafe entropy level") } private static isHighlyRepetitive(entropy: Uint8Array): boolean { const counts = new Map<number, number>(); for (const byte of entropy) { counts.set(byte, (counts.get(byte) || 0) + 1); } // if 90% or more bytes are equals, is suspect return [...counts.values()].some(count => count / entropy.length >= 0.9); } private static isEntropyStrong(entropy: Uint8Array, minEntropyBits: number = 7.5): boolean { const perByteEntropy = this.calculateShannonEntropy(entropy) / entropy.length; return perByteEntropy >= minEntropyBits; // típico: >= 7.5 de 8 } private static calculateShannonEntropy(data: Uint8Array): number { const len = data.length; const freq: Record<number, number> = {}; for (const byte of data) { freq[byte] = (freq[byte] || 0) + 1; } let entropy = 0; for (const byte in freq) { const p = freq[byte] / len; entropy -= p * Math.log2(p); } return entropy * 8; // bits de entropia por bloco total } }