UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

201 lines 8 kB
import {} from '@btc-vision/ecpair'; import { EcKeyPair } from './EcKeyPair.js'; import { fromHex, networks, toHex, toXOnly, } from '@btc-vision/bitcoin'; import { Address } from './Address.js'; import { BitcoinUtils } from '../utils/BitcoinUtils.js'; import { getMLDSAConfig, MLDSASecurityLevel, QuantumBIP32Factory, } from '@btc-vision/bip32'; /** * Wallet class for managing both classical and quantum-resistant keys */ export class Wallet { network; _keypair; _mldsaKeypair; _securityLevel; _chainCode; _p2wpkh; _p2tr; _p2wda; _legacy; _segwitLegacy; _bufferPubKey; _tweakedKey; _address; constructor(privateKeyOrWif, mldsaPrivateKeyOrBase58, network = networks.bitcoin, securityLevel = MLDSASecurityLevel.LEVEL2, chainCode) { this.network = network; this._securityLevel = securityLevel; const parsedPrivateKey = privateKeyOrWif.startsWith('0x') ? privateKeyOrWif.slice(2) : privateKeyOrWif; if (BitcoinUtils.isValidHex(parsedPrivateKey)) { this._keypair = EcKeyPair.fromPrivateKey(fromHex(parsedPrivateKey), this.network); } else { this._keypair = EcKeyPair.fromWIF(parsedPrivateKey, this.network); } const parsedMLDSAKey = mldsaPrivateKeyOrBase58.startsWith('0x') ? mldsaPrivateKeyOrBase58.slice(2) : mldsaPrivateKeyOrBase58; if (BitcoinUtils.isValidHex(parsedMLDSAKey)) { const mldsaBuffer = fromHex(parsedMLDSAKey); const config = getMLDSAConfig(securityLevel, this.network); const privateKeySize = config.privateKeySize; const publicKeySize = config.publicKeySize; const combinedSize = privateKeySize + publicKeySize; let mldsaPrivateKeyBuffer; if (mldsaBuffer.length === privateKeySize) { mldsaPrivateKeyBuffer = mldsaBuffer; } else if (mldsaBuffer.length === combinedSize) { mldsaPrivateKeyBuffer = mldsaBuffer.subarray(0, privateKeySize); } else { throw new Error(`Invalid ML-DSA key length for security level ${securityLevel}. Expected ${privateKeySize} bytes (private only) or ${combinedSize} bytes (private+public), got ${mldsaBuffer.length} bytes.`); } if (chainCode && chainCode.length !== 32) { throw new Error('Chain code must be 32 bytes'); } this._chainCode = chainCode || new Uint8Array(32); this._mldsaKeypair = QuantumBIP32Factory.fromPrivateKey(mldsaPrivateKeyBuffer, this._chainCode, this.network, securityLevel); } else { this._mldsaKeypair = QuantumBIP32Factory.fromBase58(parsedMLDSAKey); this._chainCode = new Uint8Array(this._mldsaKeypair.chainCode); this._securityLevel = this._mldsaKeypair.securityLevel; } this._bufferPubKey = this._keypair.publicKey; this._address = new Address(this._mldsaKeypair.publicKey, this._keypair.publicKey); this._p2tr = this._address.p2tr(this.network); this._p2wpkh = this._address.p2wpkh(this.network); this._legacy = this._address.p2pkh(this.network); this._segwitLegacy = this._address.p2shp2wpkh(this.network); this._p2wda = this._address.p2wda(this.network); this._tweakedKey = this._address.tweakedPublicKeyToBuffer(); } get address() { return this._address; } get tweakedPubKeyKey() { return this._tweakedKey; } get keypair() { if (!this._keypair) throw new Error('Keypair not set'); return this._keypair; } get mldsaKeypair() { return this._mldsaKeypair; } get securityLevel() { return this._securityLevel; } get chainCode() { return this._chainCode; } get p2wpkh() { return this._p2wpkh; } get p2tr() { return this._p2tr; } get p2wda() { return this._p2wda; } get legacy() { return this._legacy; } get addresses() { return [this.p2wpkh, this.p2tr, this.legacy, this.segwitLegacy]; } get segwitLegacy() { return this._segwitLegacy; } get publicKey() { if (!this._bufferPubKey) throw new Error('Public key not set'); return this._bufferPubKey; } get quantumPublicKey() { return new Uint8Array(this._mldsaKeypair.publicKey); } get quantumPrivateKey() { if (!this._mldsaKeypair.privateKey) { throw new Error('Quantum private key not set'); } return new Uint8Array(this._mldsaKeypair.privateKey); } get quantumPublicKeyHex() { return toHex(new Uint8Array(this._mldsaKeypair.publicKey)); } get quantumPrivateKeyHex() { if (!this._mldsaKeypair.privateKey) { throw new Error('Quantum private key not set'); } return toHex(new Uint8Array(this._mldsaKeypair.privateKey)); } get xOnly() { if (!this.keypair) throw new Error('Keypair not set'); return toXOnly(this._bufferPubKey); } static fromWif(wif, quantumPrivateKeyHex, network = networks.bitcoin, securityLevel = MLDSASecurityLevel.LEVEL2, chainCode) { return new Wallet(wif, quantumPrivateKeyHex, network, securityLevel, chainCode); } static generate(network = networks.bitcoin, securityLevel = MLDSASecurityLevel.LEVEL2) { const walletData = EcKeyPair.generateWallet(network, securityLevel); if (!walletData.quantumPrivateKey) { throw new Error('Failed to generate quantum keys'); } return new Wallet(walletData.privateKey, walletData.quantumPrivateKey, network, securityLevel); } static fromPrivateKeys(privateKeyHexOrWif, mldsaPrivateKeyOrBase58, network = networks.bitcoin, securityLevel = MLDSASecurityLevel.LEVEL2, chainCode) { return new Wallet(privateKeyHexOrWif, mldsaPrivateKeyOrBase58, network, securityLevel, chainCode); } toWIF() { return this._keypair.toWIF(); } toPrivateKeyHex() { if (!this._keypair.privateKey) { throw new Error('Private key not available'); } return toHex(this._keypair.privateKey); } toPublicKeyHex() { return toHex(this._bufferPubKey); } toQuantumBase58() { return this._mldsaKeypair.toBase58(); } /** * Best-effort zeroing of private key material held by this wallet. * * Zeros classical and quantum private key buffers and the chain code in-place. * This cannot guarantee all copies are erased (the JS runtime may have copied * buffers internally, and string representations cannot be zeroed), but it * eliminates the primary references. */ zeroize() { this._keypair.privateKey?.fill(0); this._mldsaKeypair.privateKey?.fill(0); this._chainCode.fill(0); } [Symbol.dispose]() { this.zeroize(); } derivePath(path) { const derivedQuantum = this._mldsaKeypair.derivePath(path); if (!this._keypair.privateKey) { throw new Error('Cannot derive from a watch-only wallet (no private key available)'); } const bip32Root = EcKeyPair.BIP32.fromPrivateKey(this._keypair.privateKey, this._chainCode, this.network); const derivedClassical = bip32Root.derivePath(path); if (!derivedClassical.privateKey) { throw new Error('Failed to derive classical private key'); } if (!derivedClassical.chainCode) { throw new Error('Failed to derive classical chain code'); } return new Wallet(toHex(new Uint8Array(derivedClassical.privateKey)), derivedQuantum.toBase58(), this.network, this._securityLevel, new Uint8Array(derivedClassical.chainCode)); } } //# sourceMappingURL=Wallet.js.map