UNPKG

@radixdlt/account

Version:

A JavaScript client library for interacting with the Radix Distributed Ledger.

277 lines (276 loc) 12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SigningKeychain = exports.isSwitchToIndex = void 0; const rxjs_1 = require("rxjs"); const signingKey_1 = require("./signingKey"); const operators_1 = require("rxjs/operators"); const crypto_1 = require("@radixdlt/crypto"); const prelude_ts_1 = require("prelude-ts"); const util_1 = require("@radixdlt/util"); const neverthrow_1 = require("neverthrow"); const stringifySigningKeysArray = (signingKeys) => signingKeys.map(a => a.toString()).join(',\n'); const stringifySigningKeys = (signingKeys) => { const allSigningKeysString = stringifySigningKeysArray(signingKeys.all); return ` size: ${signingKeys.size()}, #hdSigningKeys: ${signingKeys.hdSigningKeys().length}, #nonHDSigningKeys: ${signingKeys.nonHDSigningKeys().length}, #localHDSigningKeys: ${signingKeys.localHDSigningKeys().length}, #hardwareHDSigningKeys: ${signingKeys.hardwareHDSigningKeys().length}, all: ${allSigningKeysString} `; }; const createSigningKeys = (_all) => { const all = []; const getHDSigningKeyByHDPath = (hdPath) => { const signingKey = all .filter(a => a.isHDSigningKey) .find(a => a.hdPath.equals(hdPath)); return prelude_ts_1.Option.of(signingKey); }; const getAnySigningKeyByPublicKey = (publicKey) => { const signingKey = all.find(a => a.publicKey.equals(publicKey)); return prelude_ts_1.Option.of(signingKey); }; const localHDSigningKeys = () => all.filter(a => a.isLocalHDSigningKey); const hardwareHDSigningKeys = () => all.filter(a => a.isHardwareSigningKey); const nonHDSigningKeys = () => all.filter(a => !a.isHDSigningKey); const hdSigningKeys = () => all.filter(a => a.isHDSigningKey); const add = (signingKey) => { if (all.find(a => a.type.uniqueKey === signingKey.type.uniqueKey) !== undefined) { // already there return; } // new all.push(signingKey); }; const signingKeys = { toString: () => { throw new Error('Overriden below'); }, equals: (other) => (0, util_1.arraysEqual)(other.all, all), add, localHDSigningKeys, hardwareHDSigningKeys, nonHDSigningKeys, hdSigningKeys, all, size: () => all.length, getHDSigningKeyByHDPath, getAnySigningKeyByPublicKey, }; return Object.assign(Object.assign({}, signingKeys), { toString: () => stringifySigningKeys(signingKeys) }); }; const isSwitchToIndex = (something) => { const inspection = something; return inspection.toIndex !== undefined; }; exports.isSwitchToIndex = isSwitchToIndex; const MutableSigningKeys = { create: createSigningKeys, }; const create = (input) => { var _a; const subs = new rxjs_1.Subscription(); const { mnemonic } = input; const startWithInitialSigningKey = (_a = input.startWithInitialSigningKey) !== null && _a !== void 0 ? _a : true; const masterSeed = crypto_1.HDMasterSeed.fromMnemonic({ mnemonic }); const hdNodeDeriverWithBip32Path = masterSeed.masterNode().derive; let unsafeActiveSigningKey = undefined; const activeSigningKeySubject = new rxjs_1.ReplaySubject(); const setActiveSigningKey = (newSigningKey) => { activeSigningKeySubject.next(newSigningKey); unsafeActiveSigningKey = newSigningKey; }; const signingKeysSubject = new rxjs_1.BehaviorSubject(MutableSigningKeys.create([])); const revealMnemonic = () => mnemonic; const numberOfAllSigningKeys = () => signingKeysSubject.getValue().size(); const numberOfLocalHDSigningKeys = () => signingKeysSubject.getValue().localHDSigningKeys().length; const numberOfHWSigningKeys = () => signingKeysSubject.getValue().hardwareHDSigningKeys().length; const _addAndMaybeSwitchToNewSigningKey = (newSigningKey, alsoSwitchTo) => { const alsoSwitchTo_ = alsoSwitchTo !== null && alsoSwitchTo !== void 0 ? alsoSwitchTo : false; const signingKeys = signingKeysSubject.getValue(); signingKeys.add(newSigningKey); signingKeysSubject.next(signingKeys); if (alsoSwitchTo_) { setActiveSigningKey(newSigningKey); } return newSigningKey; }; const deriveHWSigningKey = (input) => { const nextPath = () => { const index = numberOfHWSigningKeys(); return crypto_1.HDPathRadix.create({ address: { index, isHardened: true }, }); }; const hdPath = input.keyDerivation === 'next' ? nextPath() : input.keyDerivation; return input.hardwareWalletConnection.pipe((0, operators_1.take)(1), (0, operators_1.mergeMap)((hardwareWallet) => hardwareWallet.makeSigningKey(hdPath, input.verificationPrompt)), (0, operators_1.map)((hardwareSigningKey) => { const signingKey = signingKey_1.SigningKey.fromHDPathWithHWSigningKey({ hdPath, hardwareSigningKey, }); _addAndMaybeSwitchToNewSigningKey(signingKey, input.alsoSwitchTo); return signingKey; })); }; const _deriveLocalHDSigningKeyWithPath = (input) => { const { hdPath } = input; const newSigningKey = _addAndMaybeSwitchToNewSigningKey(signingKey_1.SigningKey.byDerivingNodeAtPath({ hdPath, deriveNodeAtPath: () => hdNodeDeriverWithBip32Path(hdPath), }), input.alsoSwitchTo); return (0, rxjs_1.of)(newSigningKey); }; const _deriveNextLocalHDSigningKeyAtIndex = (input) => _deriveLocalHDSigningKeyWithPath({ hdPath: crypto_1.HDPathRadix.create({ address: input.addressIndex, }), alsoSwitchTo: input.alsoSwitchTo, }); const deriveNextLocalHDSigningKey = (input) => { var _a; const index = numberOfLocalHDSigningKeys(); return _deriveNextLocalHDSigningKeyAtIndex({ addressIndex: { index, isHardened: (_a = input === null || input === void 0 ? void 0 : input.isHardened) !== null && _a !== void 0 ? _a : true, }, alsoSwitchTo: input === null || input === void 0 ? void 0 : input.alsoSwitchTo, }); }; const switchSigningKey = (input) => { const isSwitchToSigningKey = (something) => { const inspection = input; return (inspection.toSigningKey !== undefined && (0, signingKey_1.isSigningKey)(inspection.toSigningKey)); }; if (input === 'last') { const lastIndex = numberOfAllSigningKeys() - 1; return switchSigningKey({ toIndex: lastIndex }); } else if (input === 'first') { return switchSigningKey({ toIndex: 0 }); } else if (isSwitchToSigningKey(input)) { const toSigningKey = input.toSigningKey; setActiveSigningKey(toSigningKey); util_1.log.info(`Active signingKey switched to: ${toSigningKey.toString()}`); return toSigningKey; } else if ((0, exports.isSwitchToIndex)(input)) { const unsafeTargetIndex = input.toIndex; const signingKeys = signingKeysSubject.getValue(); const safeTargetIndex = Math.min(unsafeTargetIndex, signingKeys.size()); const firstSigningKey = Array.from(signingKeys.all)[safeTargetIndex]; if (!firstSigningKey) { const err = `No signingKeys.`; util_1.log.error(err); throw new Error(err); } return switchSigningKey({ toSigningKey: firstSigningKey }); } else { const err = `Incorrect implementation, failed to type check 'input' of switchSigningKey. Probably is 'isSigningKey' typeguard wrong.`; util_1.log.error(err); throw new Error(err); } }; if (startWithInitialSigningKey) { subs.add(deriveNextLocalHDSigningKey({ alsoSwitchTo: true, }).subscribe()); } const activeSigningKey$ = activeSigningKeySubject.asObservable(); const signingKeys$ = signingKeysSubject.asObservable().pipe((0, operators_1.shareReplay)()); const restoreLocalHDSigningKeysUpToIndex = (index) => { if (index < 0) { const errMsg = `targetIndex must not be negative`; console.error(errMsg); return (0, rxjs_1.throwError)(new Error(errMsg)); } const localHDSigningKeysSize = numberOfLocalHDSigningKeys(); const numberOfSigningKeysToCreate = index - localHDSigningKeysSize; if (numberOfSigningKeysToCreate < 0) { return signingKeys$; } const signingKeysObservableList = Array(numberOfSigningKeysToCreate) .fill(undefined) .map((_, index) => _deriveNextLocalHDSigningKeyAtIndex({ addressIndex: { index: localHDSigningKeysSize + index }, })); return (0, rxjs_1.combineLatest)(signingKeysObservableList).pipe((0, operators_1.mergeMap)(_ => signingKeys$), (0, operators_1.take)(1)); }; const addSigningKeyFromPrivateKey = (input) => { const signingKey = signingKey_1.SigningKey.fromPrivateKey(input); _addAndMaybeSwitchToNewSigningKey(signingKey, input.alsoSwitchTo); return signingKey; }; return { revealMnemonic, // should only be used for testing __unsafeGetSigningKey: () => unsafeActiveSigningKey, deriveNextLocalHDSigningKey, deriveHWSigningKey, switchSigningKey, restoreLocalHDSigningKeysUpToIndex, addSigningKeyFromPrivateKey, observeSigningKeys: () => signingKeys$, observeActiveSigningKey: () => activeSigningKey$, sign: (tx, nonXrdHRP) => activeSigningKey$.pipe((0, operators_1.mergeMap)(a => a.sign(tx, nonXrdHRP))), signHash: (hashedMessage) => activeSigningKey$.pipe((0, operators_1.mergeMap)(a => a.signHash(hashedMessage))), }; }; const byLoadingAndDecryptingKeystore = (input) => { const loadKeystore = () => neverthrow_1.ResultAsync.fromPromise(input.load(), (e) => { const underlyingError = (0, util_1.msgFromError)(e); const errMsg = `Failed to load keystore, underlying error: '${underlyingError}'`; util_1.log.error(errMsg); return new Error(errMsg); }); return loadKeystore() .map((keystore) => { util_1.log.info('Keystore successfully loaded.'); return Object.assign(Object.assign({}, input), { keystore }); }) .andThen(exports.SigningKeychain.fromKeystore); }; const fromKeystore = (input) => crypto_1.Keystore.decrypt(input) .map(entropy => ({ entropy })) .andThen(crypto_1.Mnemonic.fromEntropy) .map(mnemonic => ({ mnemonic, startWithInitialSigningKey: input.startWithInitialSigningKey, })) .map(create); const byEncryptingMnemonicAndSavingKeystore = (input) => { const { mnemonic, password, startWithInitialSigningKey } = input; const save = (keystoreToSave) => neverthrow_1.ResultAsync.fromPromise(input.save(keystoreToSave), (e) => { const underlyingError = (0, util_1.msgFromError)(e); const errMsg = `Failed to save keystore, underlying error: '${underlyingError}'`; util_1.log.error(errMsg); return new Error(errMsg); }).map(() => { util_1.log.info('Keystore successfully saved.'); return keystoreToSave; }); return crypto_1.Keystore.encryptSecret({ secret: mnemonic.entropy, password, }) .andThen(save) .map((keystore) => ({ keystore, password, startWithInitialSigningKey, })) .andThen(exports.SigningKeychain.fromKeystore); }; exports.SigningKeychain = { create, fromKeystore, byLoadingAndDecryptingKeystore, byEncryptingMnemonicAndSavingKeystore, }; //# sourceMappingURL=signingKeychain.js.map