derive 3 keys, consolidate sync redux
This commit is contained in:
parent
ded2992e44
commit
6b21df2c17
12 changed files with 271 additions and 446 deletions
|
@ -22,20 +22,21 @@ export function checkHmac(serverWalletState, hmacKey, hmac) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deriveSecrets(rootPassword, email, saltSeed, callback) {
|
export function deriveSecrets(rootPassword, email, saltSeed, callback) {
|
||||||
const encodedPassword = Buffer.from(rootPassword.normalize('NFKC'))
|
const encodedPassword = Buffer.from(rootPassword.normalize('NFKC'));
|
||||||
const encodedEmail = Buffer.from(email)
|
const encodedEmail = Buffer.from(email);
|
||||||
const SCRYPT_N = 1 << 20;
|
const SCRYPT_N = 1 << 20;
|
||||||
const SCRYPT_R = 8;
|
const SCRYPT_R = 8;
|
||||||
const SCRYPT_P = 1;
|
const SCRYPT_P = 1;
|
||||||
const KEY_LENGTH = 32;
|
const KEY_LENGTH = 32;
|
||||||
const NUM_KEYS = 2;
|
const NUM_KEYS = 3;
|
||||||
const MAXMEM_MULTIPLIER = 256;
|
const MAXMEM_MULTIPLIER = 256;
|
||||||
const DEFAULT_MAXMEM = MAXMEM_MULTIPLIER * SCRYPT_N * SCRYPT_R;
|
const DEFAULT_MAXMEM = MAXMEM_MULTIPLIER * SCRYPT_N * SCRYPT_R;
|
||||||
|
|
||||||
function getKeyParts(key) {
|
function getKeyParts(key) {
|
||||||
const lbryIdPassword = key.slice(0, KEY_LENGTH).toString('base64');
|
const lbryIdPassword = key.slice(0, KEY_LENGTH).toString('base64');
|
||||||
const hmacKey = key.slice(KEY_LENGTH).toString('base64');
|
const hmacKey = key.slice(KEY_LENGTH, KEY_LENGTH * 2).toString('base64');
|
||||||
return { lbryIdPassword, hmacKey }; // Buffer aa bb cc 6c
|
const dataKey = key.slice(KEY_LENGTH * 2).toString('base64');
|
||||||
|
return { lbryIdPassword, hmacKey, dataKey }; // Buffer aa bb cc 6c
|
||||||
}
|
}
|
||||||
|
|
||||||
const salt = generateSalt(encodedEmail, saltSeed);
|
const salt = generateSalt(encodedEmail, saltSeed);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import test from 'tape';
|
import test from 'tape';
|
||||||
// import sync from '../sync.js';
|
// import sync from '../sync.js';
|
||||||
import { generateSalt, generateSaltSeed, deriveSecrets, walletHmac } from './sync.js';
|
import { generateSalt, generateSaltSeed, deriveSecrets, walletHmac } from './sync.js';
|
||||||
export default function doTest () {
|
export default function doTest() {
|
||||||
|
|
||||||
test('Generate sync seed', (assert) => {
|
test('Generate sync seed', (assert) => {
|
||||||
const seed = generateSaltSeed();
|
const seed = generateSaltSeed();
|
||||||
|
@ -29,12 +29,11 @@ export default function doTest () {
|
||||||
|
|
||||||
const expectedHmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8='; //base64
|
const expectedHmacKey = 'bCxUIryLK0Lf9nKg9yiZDlGleMuGJkadLzTje1PAI+8='; //base64
|
||||||
const expectedLbryIdPassword = 'HKo/J+x4Hsy2NkMvj2JB9RI0yrvEiB4QSA/NHPaT/cA=';
|
const expectedLbryIdPassword = 'HKo/J+x4Hsy2NkMvj2JB9RI0yrvEiB4QSA/NHPaT/cA=';
|
||||||
let result;
|
|
||||||
|
|
||||||
function cb(e, r) {
|
function cb(e, r) {
|
||||||
console.log('result', r)
|
console.log('result', r);
|
||||||
assert.equal(r.keys.hmacKey, expectedHmacKey, 'hmac is expected value');
|
assert.equal(r.hmacKey, expectedHmacKey, 'hmac is expected value');
|
||||||
assert.equal(r.keys.lbryIdPassword, expectedLbryIdPassword, 'lbryid password is expected value');
|
assert.equal(r.lbryIdPassword, expectedLbryIdPassword, 'lbryid password is expected value');
|
||||||
assert.end();
|
assert.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +50,7 @@ export default function doTest () {
|
||||||
const hmacHex = walletHmac(input_str);
|
const hmacHex = walletHmac(input_str);
|
||||||
assert.equal(hmacHex, expectedHmacHex);
|
assert.equal(hmacHex, expectedHmacHex);
|
||||||
assert.end();
|
assert.end();
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
doTest()
|
doTest();
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { selectGetSyncErrorMessage } from 'redux/selectors/sync';
|
||||||
import { selectHasNavigated } from 'redux/selectors/app';
|
import { selectHasNavigated } from 'redux/selectors/app';
|
||||||
import { selectTotalBalance, selectBalance } from 'redux/selectors/wallet';
|
import { selectTotalBalance, selectBalance } from 'redux/selectors/wallet';
|
||||||
import * as SETTINGS from 'constants/settings';
|
import * as SETTINGS from 'constants/settings';
|
||||||
import { doLbrysyncRegister } from 'redux/actions/lbrysync';
|
import { doLbrysyncRegister } from 'redux/actions/sync';
|
||||||
import Header from './view';
|
import Header from './view';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
selectLbrySyncEncryptedHmacKey,
|
selectLbrySyncEncryptedHmacKey,
|
||||||
selectLbrySyncEncryptedRoot,
|
selectLbrySyncEncryptedRoot,
|
||||||
selectLbrySyncEncryptedProviderPass,
|
selectLbrySyncEncryptedProviderPass,
|
||||||
} from 'redux/selectors/lbrysync';
|
} from 'redux/selectors/sync';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
doLbrysyncGetSalt,
|
doLbrysyncGetSalt,
|
||||||
|
@ -25,10 +25,10 @@ import {
|
||||||
doGenerateSaltSeed,
|
doGenerateSaltSeed,
|
||||||
doDeriveSecrets,
|
doDeriveSecrets,
|
||||||
doLbrysyncAuthenticate,
|
doLbrysyncAuthenticate,
|
||||||
} from 'redux/actions/lbrysync';
|
} from 'redux/actions/sync';
|
||||||
|
|
||||||
const select = (state) => ({
|
const select = (state) => ({
|
||||||
walletEncrypted: selectWalletIsEncrypted(state),
|
isWalletEncrypted: selectWalletIsEncrypted(state),
|
||||||
registering: selectLbrySyncRegistering(state),
|
registering: selectLbrySyncRegistering(state),
|
||||||
registeredEmail: selectLbrySyncEmail(state),
|
registeredEmail: selectLbrySyncEmail(state),
|
||||||
registerError: selectLbrySyncRegisterError(state),
|
registerError: selectLbrySyncRegisterError(state),
|
||||||
|
|
|
@ -5,7 +5,6 @@ import Card from 'component/common/card';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { Form, FormField } from 'component/common/form';
|
import { Form, FormField } from 'component/common/form';
|
||||||
import I18nMessage from 'component/i18nMessage';
|
import I18nMessage from 'component/i18nMessage';
|
||||||
import Spinner from 'component/spinner';
|
|
||||||
import * as ICONS from 'constants/icons';
|
import * as ICONS from 'constants/icons';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
@ -55,6 +54,9 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
register,
|
register,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Register / auth
|
||||||
|
*/
|
||||||
const SIGN_IN_MODE = 'sign_in';
|
const SIGN_IN_MODE = 'sign_in';
|
||||||
const SIGN_UP_MODE = 'sign_up';
|
const SIGN_UP_MODE = 'sign_up';
|
||||||
const VERIFY_MODE = 'verify';
|
const VERIFY_MODE = 'verify';
|
||||||
|
@ -192,7 +194,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
label={__('Password Again')}
|
label={__('Password')}
|
||||||
value={pass}
|
value={pass}
|
||||||
onChange={(e) => setPass(e.target.value)}
|
onChange={(e) => setPass(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
@ -222,7 +224,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
<p>
|
<p>
|
||||||
<I18nMessage
|
<I18nMessage
|
||||||
tokens={{
|
tokens={{
|
||||||
sign_in: <Button button="link" onClick={() => setMode(SIGN_UP_MODE)} label={__('Sign in')} />,
|
sign_in: <Button button="link" onClick={() => setMode(SIGN_IN_MODE)} label={__('Sign in')} />,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Sign up for a sync account. Or %sign_in%.
|
Sign up for a sync account. Or %sign_in%.
|
||||||
|
@ -254,7 +256,7 @@ export default function NotificationSettingsPage(props: Props) {
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
label={__('Password Again')}
|
label={__('Password')}
|
||||||
value={pass}
|
value={pass}
|
||||||
onChange={(e) => setPass(e.target.value)}
|
onChange={(e) => setPass(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { combineReducers } from 'redux';
|
import { combineReducers } from 'redux';
|
||||||
import { connectRouter } from 'connected-react-router';
|
import { connectRouter } from 'connected-react-router';
|
||||||
import { costInfoReducer, blacklistReducer, filteredReducer, statsReducer } from 'lbryinc';
|
import { costInfoReducer, blacklistReducer, filteredReducer, statsReducer } from 'lbryinc';
|
||||||
import { lbrysyncReducer } from 'redux/reducers/lbrysync';
|
|
||||||
import { claimsReducer } from 'redux/reducers/claims';
|
import { claimsReducer } from 'redux/reducers/claims';
|
||||||
import { fileInfoReducer } from 'redux/reducers/file_info';
|
import { fileInfoReducer } from 'redux/reducers/file_info';
|
||||||
import { walletReducer } from 'redux/reducers/wallet';
|
import { walletReducer } from 'redux/reducers/wallet';
|
||||||
|
@ -44,5 +43,4 @@ export default (history) =>
|
||||||
wallet: walletReducer,
|
wallet: walletReducer,
|
||||||
sync: syncReducer,
|
sync: syncReducer,
|
||||||
collections: collectionsReducer,
|
collections: collectionsReducer,
|
||||||
lbrysync: lbrysyncReducer,
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,302 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import { safeStoreEncrypt, safeStoreDecrypt } from 'util/saved-passwords';
|
|
||||||
|
|
||||||
import * as Lbrysync from 'lbrysync';
|
|
||||||
import Lbry from 'lbry';
|
|
||||||
import { Lbryio } from "lbryinc";
|
|
||||||
import { selectSyncHash } from '../selectors/sync';
|
|
||||||
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
|
||||||
const { fetchSaltSeed } = Lbrysync;
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const saltOrError = await fetchSaltSeed(email);
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
|
||||||
data: { email: email, saltSeed: saltOrError},
|
|
||||||
});
|
|
||||||
return saltOrError;
|
|
||||||
} catch (e) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
|
||||||
data: { email: email, saltError: 'Not Found'},
|
|
||||||
});
|
|
||||||
return 'not found';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// register an email (eventually username)
|
|
||||||
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
|
||||||
const { register } = Lbrysync;
|
|
||||||
// started
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
|
||||||
});
|
|
||||||
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
|
||||||
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
|
||||||
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
|
||||||
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
|
||||||
const registerData = {
|
|
||||||
email,
|
|
||||||
saltSeed,
|
|
||||||
providerPass: encProviderPass,
|
|
||||||
hmacKey: encHmacKey,
|
|
||||||
rootPass: enctyptedRoot,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!resultIfError) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
|
||||||
data: registerData,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
|
||||||
data: resultIfError,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// get token given username/password
|
|
||||||
export const doLbrysyncAuthenticate =
|
|
||||||
() => async (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_AUTH_STARTED,
|
|
||||||
});
|
|
||||||
const state = getState();
|
|
||||||
const { lbrysync } = state;
|
|
||||||
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
|
||||||
const status = await Lbry.status();
|
|
||||||
const { installation_id: deviceId } = status;
|
|
||||||
const password = safeStoreDecrypt(encryptedProviderPass);
|
|
||||||
|
|
||||||
const { getAuthToken } = Lbrysync;
|
|
||||||
|
|
||||||
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
|
||||||
|
|
||||||
if (result.token) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
|
||||||
data: result.token,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_AUTH_FAILED,
|
|
||||||
data: result.error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
|
||||||
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
|
||||||
{
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
hmacKey: result.hmacKey,
|
|
||||||
rootPassword,
|
|
||||||
providerPass: result.lbryIdPassword,
|
|
||||||
};
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
return data;
|
|
||||||
} catch (e) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
|
||||||
data: {
|
|
||||||
error: e,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return { error: e.message };
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function doSetSync() {
|
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
const state = getState();
|
|
||||||
const localHash = selectSyncHash(state);
|
|
||||||
const { lbrysync } = state;
|
|
||||||
const { authToken, encryptedRoot } = lbrysync;
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SET_SYNC_STARTED,
|
|
||||||
});
|
|
||||||
let error;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const status = Lbry.wallet_status();
|
|
||||||
if (status.is_locked) {
|
|
||||||
throw new Error('Error parsing i18n messages file: ' + messagesFilePath + ' err: ' + err);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
error = e.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error) {
|
|
||||||
const syncData = await Lbry.sync_apply({ password: , data: response.data, blocking: true })
|
|
||||||
}
|
|
||||||
// return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
|
|
||||||
return pushWallet(authToken)
|
|
||||||
.then((response) => {
|
|
||||||
if (!response.hash) {
|
|
||||||
throw Error('No hash returned for sync/set.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return dispatch({
|
|
||||||
type: ACTIONS.SET_SYNC_COMPLETED,
|
|
||||||
data: { syncHash: response.hash },
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SET_SYNC_FAILED,
|
|
||||||
data: { error },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doGetSync(passedPassword?: string, callback?: (any, ?boolean) => void) {
|
|
||||||
|
|
||||||
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
|
||||||
|
|
||||||
function handleCallback(error, hasNewData) {
|
|
||||||
if (callback) {
|
|
||||||
if (typeof callback !== 'function') {
|
|
||||||
throw new Error('Second argument passed to "doGetSync" must be a function');
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(error, hasNewData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
const state = getState();
|
|
||||||
const localHash = selectSyncHash(state);
|
|
||||||
const { lbrysync } = state;
|
|
||||||
const { authToken, encryptedRoot } = lbrysync;
|
|
||||||
const { pullWallet } = Lbrysync;
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.GET_SYNC_STARTED,
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = {};
|
|
||||||
|
|
||||||
Lbry.wallet_status()
|
|
||||||
.then((status) => {
|
|
||||||
if (status.is_locked) {
|
|
||||||
return Lbry.wallet_unlock({ password });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wallet is already unlocked
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.then((isUnlocked) => {
|
|
||||||
if (isUnlocked) {
|
|
||||||
return Lbry.sync_hash(); //unnec
|
|
||||||
}
|
|
||||||
data.unlockFailed = true;
|
|
||||||
throw new Error();
|
|
||||||
})
|
|
||||||
// .then((hash?: string) => Lbryio.call('sync', 'get', { hash }, 'post'))
|
|
||||||
.then((hash?: string) => pullWallet(authToken))
|
|
||||||
.then((response: any) => {
|
|
||||||
// get data, put it in sync apply.
|
|
||||||
const syncHash = response.hash;
|
|
||||||
data.syncHash = syncHash;
|
|
||||||
data.syncData = response.data;
|
|
||||||
data.changed = response.changed || syncHash !== localHash;
|
|
||||||
data.hasSyncedWallet = true;
|
|
||||||
|
|
||||||
if (response.changed) {
|
|
||||||
return Lbry.sync_apply({ password, data: response.data, blocking: true });
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
if (!response) {
|
|
||||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
|
||||||
handleCallback(null, data.changed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { hash: walletHash, data: walletData } = response;
|
|
||||||
|
|
||||||
if (walletHash !== data.syncHash) {
|
|
||||||
// different local hash, need to synchronise
|
|
||||||
dispatch(doSetSync(data.syncHash, walletHash, walletData));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
|
|
||||||
handleCallback(null, data.changed);
|
|
||||||
})
|
|
||||||
.catch((syncAttemptError) => {
|
|
||||||
const badPasswordError =
|
|
||||||
syncAttemptError && syncAttemptError.data && syncAttemptError.data.name === BAD_PASSWORD_ERROR_NAME;
|
|
||||||
|
|
||||||
if (data.unlockFailed) {
|
|
||||||
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
|
|
||||||
|
|
||||||
if (badPasswordError) {
|
|
||||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCallback(syncAttemptError);
|
|
||||||
} else if (data.hasSyncedWallet) {
|
|
||||||
const error = (syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.GET_SYNC_FAILED,
|
|
||||||
data: {
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (badPasswordError) {
|
|
||||||
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCallback(error);
|
|
||||||
} else {
|
|
||||||
const noWalletError = syncAttemptError && syncAttemptError.message === NO_WALLET_ERROR;
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.GET_SYNC_COMPLETED,
|
|
||||||
data: {
|
|
||||||
hasSyncedWallet: false,
|
|
||||||
syncHash: null,
|
|
||||||
// If there was some unknown error, bail
|
|
||||||
fatalError: !noWalletError,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// user doesn't have a synced wallet
|
|
||||||
// call sync_apply to get data to sync
|
|
||||||
// first time sync. use any string for old hash
|
|
||||||
if (noWalletError) {
|
|
||||||
Lbry.sync_apply({ password })
|
|
||||||
.then(({ hash: walletHash, data: syncApplyData }) => {
|
|
||||||
dispatch(doSetSync('', walletHash, syncApplyData));
|
|
||||||
handleCallback();
|
|
||||||
})
|
|
||||||
.catch((syncApplyError) => {
|
|
||||||
handleCallback(syncApplyError);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,10 @@ import * as SETTINGS from 'constants/settings';
|
||||||
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
||||||
import { Lbryio } from 'lbryinc';
|
import { Lbryio } from 'lbryinc';
|
||||||
import Lbry from 'lbry';
|
import Lbry from 'lbry';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import * as Lbrysync from 'lbrysync';
|
||||||
|
import { safeStoreEncrypt, safeStoreDecrypt, getSavedPassword } from 'util/saved-passwords';
|
||||||
|
|
||||||
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
|
||||||
import {
|
import {
|
||||||
selectSyncHash,
|
selectSyncHash,
|
||||||
|
@ -12,8 +16,7 @@ import {
|
||||||
selectSyncIsLocked,
|
selectSyncIsLocked,
|
||||||
} from 'redux/selectors/sync';
|
} from 'redux/selectors/sync';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { getSavedPassword } from 'util/saved-passwords';
|
import { doHandleSyncComplete } from 'redux/actions/app';
|
||||||
import { doAnalyticsTagSync, doHandleSyncComplete } from 'redux/actions/app';
|
|
||||||
import Comments from 'comments';
|
import Comments from 'comments';
|
||||||
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
import { getSubsetFromKeysArray } from 'util/sync-settings';
|
||||||
|
|
||||||
|
@ -71,6 +74,124 @@ export function doSetDefaultAccount(success: () => void, failure: (string) => vo
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const doLbrysyncGetSalt = (email: string) => async (dispatch: Dispatch) => {
|
||||||
|
const { fetchSaltSeed } = Lbrysync;
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_GET_SALT_STARTED,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const saltOrError = await fetchSaltSeed(email);
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_GET_SALT_COMPLETED,
|
||||||
|
data: { email: email, saltSeed: saltOrError},
|
||||||
|
});
|
||||||
|
return saltOrError;
|
||||||
|
} catch (e) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_GET_SALT_FAILED,
|
||||||
|
data: { email: email, saltError: 'Not Found'},
|
||||||
|
});
|
||||||
|
return 'not found';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// register an email (eventually username)
|
||||||
|
export const doLbrysyncRegister = (email: string, secrets: any, saltSeed: string) => async (dispatch: Dispatch) => {
|
||||||
|
const { register } = Lbrysync;
|
||||||
|
// started
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_REGISTER_STARTED,
|
||||||
|
});
|
||||||
|
const resultIfError = await register(email, secrets.providerPass, saltSeed);
|
||||||
|
const encProviderPass = safeStoreEncrypt(secrets.providerPass);
|
||||||
|
const encHmacKey = safeStoreEncrypt(secrets.hmacKey);
|
||||||
|
const enctyptedRoot = safeStoreEncrypt(secrets.rootPassword);
|
||||||
|
const registerData = {
|
||||||
|
email,
|
||||||
|
saltSeed,
|
||||||
|
providerPass: encProviderPass,
|
||||||
|
hmacKey: encHmacKey,
|
||||||
|
rootPass: enctyptedRoot,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!resultIfError) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_REGISTER_COMPLETED,
|
||||||
|
data: registerData,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_REGISTER_FAILED,
|
||||||
|
data: resultIfError,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// get token given username/password
|
||||||
|
export const doLbrysyncAuthenticate =
|
||||||
|
() => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_AUTH_STARTED,
|
||||||
|
});
|
||||||
|
const state = getState();
|
||||||
|
const { lbrysync } = state;
|
||||||
|
const { registeredEmail: email, encryptedProviderPass } = lbrysync;
|
||||||
|
const status = await Lbry.status();
|
||||||
|
const { installation_id: deviceId } = status;
|
||||||
|
const password = safeStoreDecrypt(encryptedProviderPass);
|
||||||
|
|
||||||
|
const { getAuthToken } = Lbrysync;
|
||||||
|
|
||||||
|
const result: { token?: string, error?: string } = await getAuthToken(email, password, deviceId);
|
||||||
|
|
||||||
|
if (result.token) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_AUTH_COMPLETED,
|
||||||
|
data: result.token,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_AUTH_FAILED,
|
||||||
|
data: result.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doGenerateSaltSeed = () => async (dispatch: Dispatch) => {
|
||||||
|
const result = await ipcRenderer.invoke('invoke-get-salt-seed');
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doDeriveSecrets = (rootPassword: string, email: string, saltSeed: string) => async (dispatch: Dispatch) =>
|
||||||
|
{
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_DERIVE_STARTED,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const result = await ipcRenderer.invoke('invoke-get-secrets', rootPassword, email, saltSeed);
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
hmacKey: result.hmacKey,
|
||||||
|
rootPassword,
|
||||||
|
providerPass: result.lbryIdPassword,
|
||||||
|
};
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_DERIVE_COMPLETED,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
} catch (e) {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.LSYNC_DERIVE_FAILED,
|
||||||
|
data: {
|
||||||
|
error: e,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { error: e.message };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
export function doSetSync(oldHash: string, newHash: string, data: any) {
|
||||||
return (dispatch: Dispatch) => {
|
return (dispatch: Dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -97,10 +218,18 @@ export function doSetSync(oldHash: string, newHash: string, data: any) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
make sure
|
||||||
|
- enabled
|
||||||
|
- not pending
|
||||||
|
- wallet not locked
|
||||||
|
-
|
||||||
|
get password
|
||||||
|
doGetSync(password, cb)
|
||||||
|
*/
|
||||||
export const doGetSyncDesktop =
|
export const doGetSyncDesktop =
|
||||||
(cb?: (any, any) => void, password?: string) => (dispatch: Dispatch, getState: GetState) => {
|
(cb?: (any, any) => void, password?: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
|
||||||
const getSyncPending = selectGetSyncIsPending(state);
|
const getSyncPending = selectGetSyncIsPending(state);
|
||||||
const setSyncPending = selectSetSyncIsPending(state);
|
const setSyncPending = selectSetSyncIsPending(state);
|
||||||
const syncLocked = selectSyncIsLocked(state);
|
const syncLocked = selectSyncIsLocked(state);
|
||||||
|
@ -108,31 +237,36 @@ export const doGetSyncDesktop =
|
||||||
return getSavedPassword().then((savedPassword) => {
|
return getSavedPassword().then((savedPassword) => {
|
||||||
const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
|
const passwordArgument = password || password === '' ? password : savedPassword === null ? '' : savedPassword;
|
||||||
|
|
||||||
if (syncEnabled && !getSyncPending && !setSyncPending && !syncLocked) {
|
if (!getSyncPending && !setSyncPending && !syncLocked) {
|
||||||
return dispatch(doGetSync(passwordArgument, cb));
|
return dispatch(doGetSync(passwordArgument, cb));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
start regularly polling sync
|
||||||
|
- start loop if should.
|
||||||
|
*/
|
||||||
export function doSyncLoop(noInterval?: boolean) {
|
export function doSyncLoop(noInterval?: boolean) {
|
||||||
return (dispatch: Dispatch, getState: GetState) => {
|
return (dispatch: Dispatch, getState: GetState) => {
|
||||||
if (!noInterval && syncTimer) clearInterval(syncTimer);
|
if (!noInterval && syncTimer) clearInterval(syncTimer);
|
||||||
const state = getState();
|
const state = getState();
|
||||||
// SHOULD SYNC
|
// SHOULD SYNC?
|
||||||
// syncSignedIn = selectLbrySyncSignedIn(state);
|
// syncSignedIn = selectLbrySyncSignedIn(state);
|
||||||
const syncSignedIn = false;
|
const syncSignedIn = false;
|
||||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
||||||
const syncLocked = selectSyncIsLocked(state);
|
const syncLocked = selectSyncIsLocked(state);
|
||||||
|
// if shouldSync
|
||||||
if (syncSignedIn && syncEnabled && !syncLocked) {
|
if (syncSignedIn && syncEnabled && !syncLocked) {
|
||||||
|
// doSync
|
||||||
|
//
|
||||||
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
||||||
dispatch(doAnalyticsTagSync());
|
|
||||||
if (!noInterval) {
|
if (!noInterval) {
|
||||||
syncTimer = setInterval(() => {
|
syncTimer = setInterval(() => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
const syncEnabled = makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state);
|
||||||
if (syncEnabled) {
|
if (syncEnabled) {
|
||||||
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
dispatch(doGetSyncDesktop((error, hasNewData) => dispatch(doHandleSyncComplete(error, hasNewData))));
|
||||||
dispatch(doAnalyticsTagSync());
|
|
||||||
}
|
}
|
||||||
}, SYNC_INTERVAL);
|
}, SYNC_INTERVAL);
|
||||||
}
|
}
|
||||||
|
@ -140,6 +274,9 @@ export function doSyncLoop(noInterval?: boolean) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
stop regularly polling sync
|
||||||
|
*/
|
||||||
export function doSyncUnsubscribe() {
|
export function doSyncUnsubscribe() {
|
||||||
return () => {
|
return () => {
|
||||||
if (syncTimer) {
|
if (syncTimer) {
|
||||||
|
@ -148,6 +285,10 @@ export function doSyncUnsubscribe() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
make sure not locked
|
||||||
|
|
||||||
|
*/
|
||||||
export function doGetSync(passedPassword?: string, callback?: (any, ?boolean) => void) {
|
export function doGetSync(passedPassword?: string, callback?: (any, ?boolean) => void) {
|
||||||
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
|
||||||
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
import { handleActions } from 'util/redux-utils';
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
syncProvider: null,
|
|
||||||
// reg
|
|
||||||
registering: false,
|
|
||||||
registeredEmail: null,
|
|
||||||
registerError: null,
|
|
||||||
// authtoken
|
|
||||||
isAuthenticating: false,
|
|
||||||
authError: null,
|
|
||||||
authToken: null, // store this elsewhere
|
|
||||||
// keys
|
|
||||||
derivingKeys: false,
|
|
||||||
encryptedHmacKey: null,
|
|
||||||
encryptedRoot: null,
|
|
||||||
encryptedProviderPass: null,
|
|
||||||
// salt
|
|
||||||
gettingSalt: false,
|
|
||||||
saltSeed: null,
|
|
||||||
saltError: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const lbrysyncReducer = handleActions(
|
|
||||||
{
|
|
||||||
// Register
|
|
||||||
[ACTIONS.LSYNC_REGISTER_STARTED]: (state) => ({
|
|
||||||
...state,
|
|
||||||
registering: true,
|
|
||||||
registerError: null,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_REGISTER_COMPLETED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
registeredEmail: action.data.email,
|
|
||||||
encryptedHmacKey: action.data.hmacKey,
|
|
||||||
encryptedProviderPass: action.data.providerPass,
|
|
||||||
encryptedRoot: action.data.rootPass,
|
|
||||||
saltSeed: action.data.saltSeed,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_REGISTER_FAILED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
registeredEmail: null,
|
|
||||||
registering: false,
|
|
||||||
registerError: action.data.error,
|
|
||||||
}),
|
|
||||||
// Auth
|
|
||||||
[ACTIONS.LSYNC_AUTH_STARTED]: (state) => ({
|
|
||||||
...state,
|
|
||||||
isAuthenticating: true,
|
|
||||||
authError: null,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_AUTH_COMPLETED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
authToken: action.data,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_AUTH_FAILED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
authError: action.data,
|
|
||||||
isAuthenticating: false,
|
|
||||||
}),
|
|
||||||
// derive
|
|
||||||
[ACTIONS.LSYNC_DERIVE_STARTED]: (state) => ({
|
|
||||||
...state,
|
|
||||||
derivingKeys: true,
|
|
||||||
deriveError: null,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_DERIVE_COMPLETED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
derivingKeys: false,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_DERIVE_FAILED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
deriveError: action.data.error,
|
|
||||||
derivingKeys: false,
|
|
||||||
}),
|
|
||||||
// salt
|
|
||||||
[ACTIONS.LSYNC_GET_SALT_STARTED]: (state) => ({
|
|
||||||
...state,
|
|
||||||
gettingSalt: true,
|
|
||||||
saltError: null,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_GET_SALT_COMPLETED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
gettingSalt: false,
|
|
||||||
}),
|
|
||||||
[ACTIONS.LSYNC_GET_SALT_FAILED]: (state, action) => ({
|
|
||||||
...state,
|
|
||||||
saltError: action.data.error,
|
|
||||||
gettingSalt: false,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
defaultState
|
|
||||||
);
|
|
|
@ -16,6 +16,25 @@ const defaultState = {
|
||||||
syncLocked: false,
|
syncLocked: false,
|
||||||
hashChanged: false,
|
hashChanged: false,
|
||||||
fatalError: false,
|
fatalError: false,
|
||||||
|
// lbrysync
|
||||||
|
syncProvider: null,
|
||||||
|
// reg
|
||||||
|
registering: false,
|
||||||
|
registeredEmail: null,
|
||||||
|
registerError: null,
|
||||||
|
// authtoken
|
||||||
|
isAuthenticating: false,
|
||||||
|
authError: null,
|
||||||
|
authToken: null, // store this elsewhere
|
||||||
|
// keys
|
||||||
|
derivingKeys: false,
|
||||||
|
encryptedHmacKey: null,
|
||||||
|
encryptedRoot: null,
|
||||||
|
encryptedProviderPass: null,
|
||||||
|
// salt
|
||||||
|
gettingSalt: false,
|
||||||
|
saltSeed: null,
|
||||||
|
saltError: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
reducers[ACTIONS.SYNC_STATE_POPULATE] = (state) => {
|
||||||
|
@ -107,6 +126,71 @@ reducers[ACTIONS.SYNC_FATAL_ERROR] = (state) => {
|
||||||
fatalError: true,
|
fatalError: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
// lbrysync
|
||||||
|
reducers[ACTIONS.LSYNC_REGISTER_STARTED] = (state) => ({
|
||||||
|
...state,
|
||||||
|
registering: true,
|
||||||
|
registerError: null,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_REGISTER_COMPLETED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
registeredEmail: action.data.email,
|
||||||
|
encryptedHmacKey: action.data.hmacKey,
|
||||||
|
encryptedProviderPass: action.data.providerPass,
|
||||||
|
encryptedRoot: action.data.rootPass,
|
||||||
|
saltSeed: action.data.saltSeed,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_REGISTER_FAILED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
registeredEmail: null,
|
||||||
|
registering: false,
|
||||||
|
registerError: action.data.error,
|
||||||
|
});
|
||||||
|
// Auth
|
||||||
|
reducers[ACTIONS.LSYNC_AUTH_STARTED] = (state) => ({
|
||||||
|
...state,
|
||||||
|
isAuthenticating: true,
|
||||||
|
authError: null,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_AUTH_COMPLETED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
authToken: action.data,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_AUTH_FAILED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
authError: action.data,
|
||||||
|
isAuthenticating: false,
|
||||||
|
});
|
||||||
|
// derive
|
||||||
|
reducers[ACTIONS.LSYNC_DERIVE_STARTED] = (state) => ({
|
||||||
|
...state,
|
||||||
|
derivingKeys: true,
|
||||||
|
deriveError: null,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_DERIVE_COMPLETED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
derivingKeys: false,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_DERIVE_FAILED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
deriveError: action.data.error,
|
||||||
|
derivingKeys: false,
|
||||||
|
});
|
||||||
|
// salt
|
||||||
|
reducers[ACTIONS.LSYNC_GET_SALT_STARTED] = (state) => ({
|
||||||
|
...state,
|
||||||
|
gettingSalt: true,
|
||||||
|
saltError: null,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_GET_SALT_COMPLETED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
gettingSalt: false,
|
||||||
|
});
|
||||||
|
reducers[ACTIONS.LSYNC_GET_SALT_FAILED] = (state, action) => ({
|
||||||
|
...state,
|
||||||
|
saltError: action.data.error,
|
||||||
|
gettingSalt: false,
|
||||||
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
const selectState = (state) => state.lbrysync || {};
|
|
||||||
|
|
||||||
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
|
||||||
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
|
||||||
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
|
||||||
|
|
||||||
export const selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
|
||||||
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
|
||||||
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
|
||||||
|
|
||||||
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
|
||||||
|
|
||||||
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
|
||||||
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
|
||||||
|
|
||||||
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
|
||||||
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
|
||||||
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
|
||||||
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
|
|
@ -29,3 +29,21 @@ export const selectSyncIsLocked = createSelector(selectState, state => state.syn
|
||||||
export const selectPrefsReady = createSelector(selectState, state => state.prefsReady);
|
export const selectPrefsReady = createSelector(selectState, state => state.prefsReady);
|
||||||
|
|
||||||
export const selectSyncFatalError = createSelector(selectState, state => state.fatalError);
|
export const selectSyncFatalError = createSelector(selectState, state => state.fatalError);
|
||||||
|
// lbrysync
|
||||||
|
export const selectLbrySyncRegistering = createSelector(selectState, (state) => state.registering);
|
||||||
|
export const selectLbrySyncEmail = createSelector(selectState, (state) => state.registeredEmail);
|
||||||
|
export const selectLbrySyncRegisterError = createSelector(selectState, (state) => state.registerError);
|
||||||
|
|
||||||
|
export const selectLbrySyncGettingSalt = createSelector(selectState, (state) => state.gettingSalt);
|
||||||
|
export const selectLbrySyncSaltError = createSelector(selectState, (state) => state.saltError);
|
||||||
|
export const selectLbrySyncSaltSeed = createSelector(selectState, (state) => state.saltSeed);
|
||||||
|
|
||||||
|
export const selectLbrySyncIsAuthenticating = createSelector(selectState, (state) => state.isAuthenticating);
|
||||||
|
|
||||||
|
export const selectLbrySyncAuthError = createSelector(selectState, (state) => state.authError);
|
||||||
|
export const selectLbrySyncToken = createSelector(selectState, (state) => state.authToken);
|
||||||
|
|
||||||
|
export const selectLbrySyncDerivingKeys = createSelector(selectState, (state) => state.derivingKeys);
|
||||||
|
export const selectLbrySyncEncryptedHmacKey = createSelector(selectState, (state) => state.encryptedHmacKey);
|
||||||
|
export const selectLbrySyncEncryptedRoot = createSelector(selectState, (state) => state.encryptedRoot);
|
||||||
|
export const selectLbrySyncEncryptedProviderPass = createSelector(selectState, (state) => state.encryptedProviderPass);
|
||||||
|
|
Loading…
Reference in a new issue