UNPKG

lotus-sdk

Version:

Central repository for several classes of tools for integrating with, and building for, the Lotusia ecosystem

238 lines (237 loc) 7.75 kB
import { BN } from './crypto/bn.js'; import { Point } from './crypto/point.js'; import { Random } from './crypto/random.js'; import { Base58Check } from './encoding/base58check.js'; import { JSUtil } from './util/js.js'; import { get as getNetwork, defaultNetwork, } from './networks.js'; import { PublicKey } from './publickey.js'; import { Address } from './address.js'; export class PrivateKey { bn; compressed; network; _pubkey; constructor(data, network) { if (data instanceof PrivateKey) { return data; } const info = this._classifyArguments(data, network); if (!info.bn || info.bn.isZero()) { throw new TypeError('Number can not be equal to zero, undefined, null or false'); } if (!info.bn.lt(Point.getN())) { throw new TypeError('Number must be less than N'); } if (!info.network) { throw new TypeError('Must specify the network ("livenet" or "testnet")'); } JSUtil.defineImmutable(this, { bn: info.bn, compressed: info.compressed, network: info.network, }); } get publicKey() { return this.toPublicKey(); } _classifyArguments(data, network) { const info = { compressed: true, network: network ? getNetwork(network) || defaultNetwork : defaultNetwork, }; if (data === undefined || data === null) { info.bn = PrivateKey._getRandomBN(); } else if (data instanceof BN) { info.bn = data; } else if (Buffer.isBuffer(data)) { const bufferInfo = PrivateKey._transformBuffer(data, network); Object.assign(info, bufferInfo); } else if (typeof data === 'object' && data !== null && 'compressed' in data && 'buf' in data) { info.compressed = data.compressed; info.bn = new BN(data.buf, 'be'); } else if (typeof data === 'object' && data !== null && 'bn' in data && 'network' in data) { const objectInfo = PrivateKey._transformObject(data); Object.assign(info, objectInfo); } else if (!network && typeof data === 'string' && getNetwork(data)) { info.bn = PrivateKey._getRandomBN(); info.network = getNetwork(data); } else if (typeof data === 'string') { if (JSUtil.isHexa(data)) { info.bn = new BN(data, 16); } else { const wifInfo = PrivateKey._transformWIF(data, network); Object.assign(info, wifInfo); } } else { throw new TypeError('First argument is an unrecognized data type.'); } return info; } static _getRandomBN() { let bn; do { const privbuf = Random.getPseudoRandomBuffer(32); bn = new BN(privbuf, 'be'); } while (!bn.lt(Point.getN())); return bn; } static _transformBuffer(buf, network) { const info = {}; if (buf.length === 32) { return PrivateKey._transformBNBuffer(buf, network); } const detectedNetwork = getNetwork(buf[0], 'privatekey'); if (!detectedNetwork) { throw new Error('Invalid network'); } info.network = detectedNetwork; if (network) { const specifiedNetwork = getNetwork(network); if (specifiedNetwork && info.network !== specifiedNetwork) { const isCompatible = (info.network.name === 'testnet' && specifiedNetwork.name === 'regtest') || (info.network.name === 'regtest' && specifiedNetwork.name === 'testnet'); if (!isCompatible) { throw new TypeError('Private key network mismatch'); } info.network = specifiedNetwork; } } if (buf.length === 1 + 32 + 1 && buf[1 + 32 + 1 - 1] === 1) { info.compressed = true; } else if (buf.length === 1 + 32) { info.compressed = false; } else { throw new Error('Length of buffer must be 33 (uncompressed) or 34 (compressed)'); } info.bn = new BN(buf.subarray(1, 32 + 1), 'be'); return info; } static _transformBNBuffer(buf, network) { network ||= defaultNetwork; return { network: getNetwork(network), bn: new BN(buf, 'be'), compressed: true, }; } static _transformWIF(str, network) { return PrivateKey._transformBuffer(Base58Check.decode(str), network); } static _transformObject(json) { const bn = new BN(json.bn, 16); const network = getNetwork(json.network); return { bn: bn, network: network || defaultNetwork, compressed: json.compressed, }; } static fromBuffer(arg, network) { return new PrivateKey(arg, network); } static fromString(str, network) { if (typeof str !== 'string') { throw new Error('First argument is expected to be a string.'); } return new PrivateKey(str, network); } static fromWIF(str, network) { return PrivateKey.fromString(str, network ?? defaultNetwork); } static fromObject(obj, network) { if (typeof obj !== 'object') { throw new Error('First argument is expected to be an object.'); } return new PrivateKey(obj, network); } static fromRandom(network) { const bn = PrivateKey._getRandomBN(); return new PrivateKey(bn, network); } static getValidationError(data, network) { try { new PrivateKey(data, network); return null; } catch (e) { return e; } } static isValid(data, network) { if (!data) { return false; } return !PrivateKey.getValidationError(data, network); } toString() { return this.toBuffer().toString('hex'); } toWIF(compressed = true) { let buf; if (compressed) { buf = Buffer.concat([ Buffer.from([this.network.privatekey]), this.bn.toArrayLike(Buffer, 'be', 32), Buffer.from([0x01]), ]); } else { buf = Buffer.concat([ Buffer.from([this.network.privatekey]), this.bn.toArrayLike(Buffer, 'be', 32), ]); } return Base58Check.encode(buf); } toBigNumber() { return this.bn; } toBuffer() { return this.bn.toArrayLike(Buffer, 'be', 32); } toBufferNoPadding() { return this.bn.toArrayLike(Buffer, 'be'); } toPublicKey() { if (!this._pubkey) { this._pubkey = PublicKey.fromPrivateKey(this); } return this._pubkey; } toAddress(network) { const pubkey = this.toPublicKey(); return Address.fromPublicKey(pubkey, network ?? this.network.name); } toObject() { return { bn: this.bn.toString(16), compressed: this.compressed, network: this.network.toString(), }; } toJSON() { return this.toObject(); } inspect() { const uncompressed = !this.compressed ? ', uncompressed' : ''; return `<PrivateKey: ${this.toString()}, network: ${this.network}${uncompressed}>`; } }