@bcpros/bitcore-wallet-client
Version:
Client for bitcore-wallet-service
446 lines • 20.9 kB
JavaScript
'use strict';
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _Key_xPrivKey, _Key_xPrivKeyEncrypted, _Key_version, _Key_mnemonic, _Key_mnemonicEncrypted, _Key_mnemonicHasPassphrase;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Key = void 0;
const bitcore_mnemonic_1 = __importDefault(require("@bcpros/bitcore-mnemonic"));
const crypto_wallet_core_1 = require("@bcpros/crypto-wallet-core");
const preconditions_1 = require("preconditions");
const sjcl_1 = __importDefault(require("sjcl"));
require("source-map-support/register");
const uuid_1 = __importDefault(require("uuid"));
const common_1 = require("./common");
const credentials_1 = require("./credentials");
const errors_1 = require("./errors");
const log_1 = __importDefault(require("./log"));
const $ = (0, preconditions_1.singleton)();
const wordsForLang = {
en: bitcore_mnemonic_1.default.Words.ENGLISH,
es: bitcore_mnemonic_1.default.Words.SPANISH,
ja: bitcore_mnemonic_1.default.Words.JAPANESE,
zh: bitcore_mnemonic_1.default.Words.CHINESE,
fr: bitcore_mnemonic_1.default.Words.FRENCH,
it: bitcore_mnemonic_1.default.Words.ITALIAN
};
const NETWORK = 'livenet';
class Key {
constructor(opts = { seedType: 'new' }) {
_Key_xPrivKey.set(this, void 0);
_Key_xPrivKeyEncrypted.set(this, void 0);
_Key_version.set(this, void 0);
_Key_mnemonic.set(this, void 0);
_Key_mnemonicEncrypted.set(this, void 0);
_Key_mnemonicHasPassphrase.set(this, void 0);
__classPrivateFieldSet(this, _Key_version, 1, "f");
this.id = opts.id || uuid_1.default.v4();
this.use0forBCH = opts.useLegacyCoinType;
this.use44forMultisig = opts.useLegacyPurpose;
this.compliantDerivation = !opts.nonCompliantDerivation;
let x = opts.seedData;
switch (opts.seedType) {
case 'new':
if (opts.language && !wordsForLang[opts.language])
throw new Error('Unsupported language');
let m = new bitcore_mnemonic_1.default(wordsForLang[opts.language]);
while (!bitcore_mnemonic_1.default.isValid(m.toString())) {
m = new bitcore_mnemonic_1.default(wordsForLang[opts.language]);
}
this.setFromMnemonic(m, opts);
break;
case 'mnemonic':
$.checkArgument(x, 'Need to provide opts.seedData');
$.checkArgument(typeof x === 'string', 'sourceData need to be a string');
this.setFromMnemonic(new bitcore_mnemonic_1.default(x), opts);
break;
case 'extendedPrivateKey':
$.checkArgument(x, 'Need to provide opts.seedData');
let xpriv;
try {
xpriv = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(x);
}
catch (e) {
throw new Error('Invalid argument');
}
this.fingerPrint = xpriv.fingerPrint.toString('hex');
if (opts.password) {
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, sjcl_1.default.encrypt(opts.password, xpriv.toString(), opts), "f");
if (!__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"))
throw new Error('Could not encrypt');
}
else {
__classPrivateFieldSet(this, _Key_xPrivKey, xpriv.toString(), "f");
}
__classPrivateFieldSet(this, _Key_mnemonic, null, "f");
__classPrivateFieldSet(this, _Key_mnemonicHasPassphrase, null, "f");
break;
case 'object':
$.shouldBeObject(x, 'Need to provide an object at opts.seedData');
$.shouldBeUndefined(opts.password, 'opts.password not allowed when source is object');
if (__classPrivateFieldGet(this, _Key_version, "f") != x.version) {
throw new Error('Bad Key version');
}
__classPrivateFieldSet(this, _Key_xPrivKey, x.xPrivKey, "f");
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, x.xPrivKeyEncrypted, "f");
__classPrivateFieldSet(this, _Key_mnemonic, x.mnemonic, "f");
__classPrivateFieldSet(this, _Key_mnemonicEncrypted, x.mnemonicEncrypted, "f");
__classPrivateFieldSet(this, _Key_mnemonicHasPassphrase, x.mnemonicHasPassphrase, "f");
__classPrivateFieldSet(this, _Key_version, x.version, "f");
this.fingerPrint = x.fingerPrint;
this.compliantDerivation = x.compliantDerivation;
this.BIP45 = x.BIP45;
this.id = x.id;
this.use0forBCH = x.use0forBCH;
this.use44forMultisig = x.use44forMultisig;
$.checkState(__classPrivateFieldGet(this, _Key_xPrivKey, "f") || __classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"), 'Failed state: #xPrivKey || #xPrivKeyEncrypted at Key constructor');
break;
case 'objectV1':
this.use0forBCH = false;
this.use44forMultisig = false;
this.compliantDerivation = true;
this.id = uuid_1.default.v4();
if (x.compliantDerivation != null)
this.compliantDerivation = x.compliantDerivation;
if (x.id != null)
this.id = x.id;
__classPrivateFieldSet(this, _Key_xPrivKey, x.xPrivKey, "f");
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, x.xPrivKeyEncrypted, "f");
__classPrivateFieldSet(this, _Key_mnemonic, x.mnemonic, "f");
__classPrivateFieldSet(this, _Key_mnemonicEncrypted, x.mnemonicEncrypted, "f");
__classPrivateFieldSet(this, _Key_mnemonicHasPassphrase, x.mnemonicHasPassphrase, "f");
__classPrivateFieldSet(this, _Key_version, x.version || 1, "f");
this.fingerPrint = x.fingerPrint;
this.use44forMultisig = x.n > 1 ? true : false;
this.use0forBCH = x.use145forBCH
? false
: x.coin == 'bch'
? true
: false;
this.BIP45 = x.derivationStrategy == 'BIP45';
break;
default:
throw new Error('Unknown seed source: ' + opts.seedType);
}
}
static match(a, b) {
return a.id == b.id || a.fingerPrint == b.fingerPrint;
}
setFromMnemonic(m, opts) {
const xpriv = m.toHDPrivateKey(opts.passphrase, NETWORK);
this.fingerPrint = xpriv.fingerPrint.toString('hex');
if (opts.password) {
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, sjcl_1.default.encrypt(opts.password, xpriv.toString(), opts.sjclOpts), "f");
if (!__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"))
throw new Error('Could not encrypt');
__classPrivateFieldSet(this, _Key_mnemonicEncrypted, sjcl_1.default.encrypt(opts.password, m.phrase, opts.sjclOpts), "f");
if (!__classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f"))
throw new Error('Could not encrypt');
}
else {
__classPrivateFieldSet(this, _Key_xPrivKey, xpriv.toString(), "f");
__classPrivateFieldSet(this, _Key_mnemonic, m.phrase, "f");
__classPrivateFieldSet(this, _Key_mnemonicHasPassphrase, !!opts.passphrase, "f");
}
}
toObj() {
const ret = {
xPrivKey: __classPrivateFieldGet(this, _Key_xPrivKey, "f"),
xPrivKeyEncrypted: __classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"),
mnemonic: __classPrivateFieldGet(this, _Key_mnemonic, "f"),
mnemonicEncrypted: __classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f"),
version: __classPrivateFieldGet(this, _Key_version, "f"),
mnemonicHasPassphrase: __classPrivateFieldGet(this, _Key_mnemonicHasPassphrase, "f"),
fingerPrint: this.fingerPrint,
compliantDerivation: this.compliantDerivation,
BIP45: this.BIP45,
use0forBCH: this.use0forBCH,
use44forMultisig: this.use44forMultisig,
id: this.id
};
return JSON.parse(JSON.stringify(ret));
}
;
isPrivKeyEncrypted() {
return !!__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f") && !__classPrivateFieldGet(this, _Key_xPrivKey, "f");
}
;
checkPassword(password) {
if (this.isPrivKeyEncrypted()) {
try {
sjcl_1.default.decrypt(password, __classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"));
}
catch (ex) {
return false;
}
return true;
}
return null;
}
;
get(password) {
let keys = {};
let fingerPrintUpdated = false;
if (this.isPrivKeyEncrypted()) {
$.checkArgument(password, 'Private keys are encrypted, a password is needed');
try {
keys.xPrivKey = sjcl_1.default.decrypt(password, __classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"));
if (!this.fingerPrint) {
let xpriv = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(keys.xPrivKey);
this.fingerPrint = xpriv.fingerPrint.toString('hex');
fingerPrintUpdated = true;
}
if (__classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f")) {
keys.mnemonic = sjcl_1.default.decrypt(password, __classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f"));
}
}
catch (ex) {
throw new Error('Could not decrypt');
}
}
else {
keys.xPrivKey = __classPrivateFieldGet(this, _Key_xPrivKey, "f");
keys.mnemonic = __classPrivateFieldGet(this, _Key_mnemonic, "f");
if (fingerPrintUpdated) {
keys.fingerPrintUpdated = true;
}
}
keys.mnemonicHasPassphrase = __classPrivateFieldGet(this, _Key_mnemonicHasPassphrase, "f") || false;
return keys;
}
;
encrypt(password, opts) {
if (__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"))
throw new Error('Private key already encrypted');
if (!__classPrivateFieldGet(this, _Key_xPrivKey, "f"))
throw new Error('No private key to encrypt');
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, sjcl_1.default.encrypt(password, __classPrivateFieldGet(this, _Key_xPrivKey, "f"), opts), "f");
if (!__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"))
throw new Error('Could not encrypt');
if (__classPrivateFieldGet(this, _Key_mnemonic, "f"))
__classPrivateFieldSet(this, _Key_mnemonicEncrypted, sjcl_1.default.encrypt(password, __classPrivateFieldGet(this, _Key_mnemonic, "f"), opts), "f");
__classPrivateFieldSet(this, _Key_xPrivKey, null, "f");
__classPrivateFieldSet(this, _Key_mnemonic, null, "f");
}
;
decrypt(password) {
if (!__classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f"))
throw new Error('Private key is not encrypted');
try {
__classPrivateFieldSet(this, _Key_xPrivKey, sjcl_1.default.decrypt(password, __classPrivateFieldGet(this, _Key_xPrivKeyEncrypted, "f")), "f");
if (__classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f")) {
__classPrivateFieldSet(this, _Key_mnemonic, sjcl_1.default.decrypt(password, __classPrivateFieldGet(this, _Key_mnemonicEncrypted, "f")), "f");
}
__classPrivateFieldSet(this, _Key_xPrivKeyEncrypted, null, "f");
__classPrivateFieldSet(this, _Key_mnemonicEncrypted, null, "f");
}
catch (ex) {
log_1.default.error('error decrypting:', ex);
throw new Error('Could not decrypt');
}
}
;
derive(password, path) {
$.checkArgument(path, 'no path at derive()');
const xPrivKey = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(this.get(password).xPrivKey, NETWORK);
const deriveFn = this.compliantDerivation
? xPrivKey.deriveChild.bind(xPrivKey)
: xPrivKey.deriveNonCompliantChild.bind(xPrivKey);
return deriveFn(path);
}
;
_checkChain(chain) {
if (!common_1.Constants.CHAINS.includes(chain))
throw new Error('Invalid chain');
}
;
_checkNetwork(network) {
if (!['livenet', 'testnet', 'regtest'].includes(network))
throw new Error('Invalid network ' + network);
}
;
getBaseAddressDerivationPath(opts) {
$.checkArgument(opts, 'Need to provide options');
$.checkArgument(opts.n >= 1, 'n need to be >=1');
const chain = opts.chain || common_1.Utils.getChain(opts.coin);
let purpose = opts.n == 1 || this.use44forMultisig ? '44' : '48';
let coinCode = '0';
if (['testnet', 'regtest]'].includes(opts.network) &&
common_1.Constants.UTXO_CHAINS.includes(chain)) {
coinCode = '1';
}
else if (chain == 'bch') {
if (this.use0forBCH || opts.use0forBCH) {
coinCode = '0';
}
else {
coinCode = '145';
}
}
else if (chain == 'btc') {
coinCode = '0';
}
else if (chain == 'eth') {
coinCode = '60';
}
else if (chain == 'matic') {
coinCode = '60';
}
else if (chain == 'arb') {
coinCode = '60';
}
else if (chain == 'op') {
coinCode = '60';
}
else if (chain == 'base') {
coinCode = '60';
}
else if (chain == 'xrp') {
coinCode = '144';
}
else if (chain == 'doge') {
coinCode = '3';
}
else if (chain == 'xec') {
coinCode = '899';
if (opts.isSlpToken) {
if (opts.isPath899) {
coinCode = '899';
}
else {
coinCode = '1899';
}
if (opts.isFromRaipay) {
coinCode = '145';
}
}
}
else if (chain == 'xpi') {
coinCode = '10605';
}
else if (chain == 'ltc') {
coinCode = '2';
}
else {
throw new Error('unknown chain: ' + chain);
}
return 'm/' + purpose + "'/" + coinCode + "'/" + opts.account + "'";
}
;
createCredentials(password, opts) {
var _a;
opts = opts || {};
opts.chain = opts.chain || common_1.Utils.getChain(opts.coin);
if (password)
$.shouldBeString(password, 'provide password');
this._checkNetwork(opts.network);
$.shouldBeNumber(opts.account, 'Invalid account');
$.shouldBeNumber(opts.n, 'Invalid n');
$.shouldBeUndefined(opts.useLegacyCoinType);
$.shouldBeUndefined(opts.useLegacyPurpose);
let path = this.getBaseAddressDerivationPath(opts);
let xPrivKey = this.derive(password, path);
let requestPrivKey = this.derive(password, common_1.Constants.PATHS.REQUEST_KEY).privateKey.toString();
if (['testnet', 'regtest'].includes(opts.network)) {
let x = xPrivKey.toObject();
x.network = opts.network;
delete x.xprivkey;
delete x.checksum;
x.privateKey = x.privateKey.padStart(64, '0');
xPrivKey = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(x);
}
return credentials_1.Credentials.fromDerivedKey({
xPubKey: xPrivKey.hdPublicKey.toString(),
coin: opts.coin,
chain: ((_a = opts.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || common_1.Utils.getChain(opts.coin),
network: opts.network,
account: opts.account,
n: opts.n,
rootPath: path,
keyId: this.id,
requestPrivKey,
addressType: opts.addressType,
walletPrivKey: opts.walletPrivKey,
isSlpToken: !!opts.isSlpToken,
isFromRaipay: !!opts.isFromRaipay,
isPath899: !!opts.isPath899
});
}
;
createAccess(password, opts) {
opts = opts || {};
$.shouldBeString(opts.path);
var requestPrivKey = new crypto_wallet_core_1.BitcoreLib.PrivateKey(opts.requestPrivKey || null);
var requestPubKey = requestPrivKey.toPublicKey().toString();
var xPriv = this.derive(password, opts.path);
var signature = common_1.Utils.signRequestPubKey(requestPubKey, xPriv);
requestPrivKey = requestPrivKey.toString();
return {
signature,
requestPrivKey
};
}
;
sign(rootPath, txp, password, cb) {
var _a;
$.shouldBeString(rootPath);
if (this.isPrivKeyEncrypted() && !password) {
return cb(new errors_1.Errors.ENCRYPTED_PRIVATE_KEY());
}
var privs = [];
var derived = {};
var derived = this.derive(password, rootPath);
var xpriv = new crypto_wallet_core_1.BitcoreLib.HDPrivateKey(derived);
var t = common_1.Utils.buildTx(txp);
var chain = ((_a = txp.chain) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || common_1.Utils.getChain(txp.coin);
if (common_1.Constants.UTXO_CHAINS.includes(chain)) {
for (const i of txp.inputs) {
$.checkState(i.path, 'Input derivation path not available (signing transaction)');
if (!derived[i.path]) {
derived[i.path] = xpriv.deriveChild(i.path).privateKey;
privs.push(derived[i.path]);
}
}
;
var signatures = privs.map(function (priv, i) {
return t.getSignatures(priv, undefined, txp.signingMethod);
});
signatures = signatures.flat().sort((a, b) => a.inputIndex - b.inputIndex);
signatures = signatures.map(sig => sig.signature.toDER().toString('hex'));
return signatures;
}
else {
let tx = t.uncheckedSerialize();
tx = typeof tx === 'string' ? [tx] : tx;
const txArray = Array.isArray(tx) ? tx : [tx];
const isChange = false;
const addressIndex = 0;
const { privKey, pubKey } = crypto_wallet_core_1.Deriver.derivePrivateKey(chain.toUpperCase(), txp.network, derived, addressIndex, isChange);
let signatures = [];
for (const rawTx of txArray) {
const signed = crypto_wallet_core_1.Transactions.getSignature({
chain: chain.toUpperCase(),
tx: rawTx,
key: { privKey, pubKey }
});
signatures.push(signed);
}
return signatures;
}
}
;
}
exports.Key = Key;
_Key_xPrivKey = new WeakMap(), _Key_xPrivKeyEncrypted = new WeakMap(), _Key_version = new WeakMap(), _Key_mnemonic = new WeakMap(), _Key_mnemonicEncrypted = new WeakMap(), _Key_mnemonicHasPassphrase = new WeakMap();
//# sourceMappingURL=key.js.map