@radixdlt/account
Version:
A JavaScript client library for interacting with the Radix Distributed Ledger.
277 lines (276 loc) • 12 kB
JavaScript
;
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