UNPKG

edge-core-js

Version:

Edge account & wallet management library

156 lines (134 loc) 4.52 kB
function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { base64 } from 'rfc4648' import { decryptText } from '../../util/crypto/crypto' import { makeCreateKit } from '../login/create' import { decryptKeyInfos, findFirstKey, makeAccountType, makeKeyInfo, makeKeysKit } from '../login/keys' import { applyKit, decryptChildKey, searchTree } from '../login/login' import { getStashById } from '../login/login-selectors' import { createStorageKeys, wasEdgeStorageKeys } from '../login/storage-keys' /** * Creates a child login under the provided login, with the given appId. */ async function createChildLogin( ai, stashTree, sessionKey, appId ) { let pin if (stashTree.pin2TextBox != null) { pin = decryptText(stashTree.pin2TextBox, sessionKey.loginKey) } const { kit } = await makeCreateKit(ai, sessionKey, appId, { keyInfo: makeKeyInfo( makeAccountType(appId), wasEdgeStorageKeys(createStorageKeys(ai)) ), pin, username: stashTree.username }) const parentKit = { loginId: sessionKey.loginId, server: kit.server, serverPath: kit.serverPath, stash: { children: [kit.stash ] } } return await applyKit(ai, sessionKey, parentKit) } /** * Ensures that the loginTree contains an account for the specified appId. * @return A `Promise`, which will resolve to a loginTree that does have * the requested account. */ export async function ensureAccountExists( ai, stashTree, sessionKey, appId ) { const { log } = ai.props // If there is no app login, make that: const appStash = searchTree(stashTree, stash => stash.appId === appId) if (appStash == null) { // For crash errors: ai.props.log.breadcrumb('createChildLogin', {}) return await createChildLogin(ai, stashTree, sessionKey, appId) } // Decrypt the wallet keys: // TODO: Once we cache public keys, use those instead: const appKey = decryptChildKey(stashTree, sessionKey, appStash.loginId) const keyInfos = decryptKeyInfos(appStash, appKey.loginKey) log.warn( `Login: decrypted keys for user ${base64.stringify(stashTree.loginId)}` ) // If the account has no repo, make one: const accountType = makeAccountType(appId) if (findFirstKey(keyInfos, accountType) == null) { // For crash errors: ai.props.log.breadcrumb('createAccountRepo', {}) const keyInfo = makeKeyInfo( accountType, wasEdgeStorageKeys(createStorageKeys(ai)) ) const keysKit = makeKeysKit(ai, appKey, [keyInfo]) return await applyKit(ai, sessionKey, keysKit) } // Everything is fine, so do nothing: return stashTree } /** * Creates an `EdgeAccount` API object. */ export async function makeAccount( ai, sessionKey, loginType, opts ) { const { pauseWallets = false } = opts // Override the appId if duress mode is enabled: const appId = opts.duressMode === true ? ai.props.state.login.contextAppId + '.duress' : ai.props.state.login.contextAppId const { log } = ai.props // For crash errors: ai.props.log.breadcrumb('makeAccount', {}) // Create the loginTree: const { stashTree } = getStashById(ai, sessionKey.loginId) await ensureAccountExists(ai, stashTree, sessionKey, appId) log.warn('Login: account exists for appId') // Add the login to redux: ai.props.dispatch({ type: 'LOGIN', payload: { appId, loginType, pauseWallets, rootLoginId: stashTree.loginId, sessionKey } }) return await waitForAccount(ai, ai.props.state.lastAccountId) } /** * Waits for the account API to appear and returns it. */ function waitForAccount(ai, accountId) { const out = ai.waitFor( (props) => { const accountState = props.state.accounts[accountId] if (accountState.loadFailure != null) throw accountState.loadFailure const accountOutput = props.output.accounts[accountId] if (_optionalChain([accountOutput, 'optionalAccess', _ => _.accountApi]) != null) { return accountOutput.accountApi } } ) return out }