@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
201 lines • 8 kB
JavaScript
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