Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
06b76d9
Corrections by code review
dmernies-iohk Dec 4, 2019
a547967
decrypt utils are added to save encrypted spending password and data
dmernies-iohk Dec 11, 2019
d76a264
Create spending password screen is added and displayed when password …
dmernies-iohk Dec 11, 2019
17ab19a
RestoreWalletFromPrivateKey component is created to improve visual co…
dmernies-iohk Dec 12, 2019
e42bc53
A new component to revoer BIP39 phrases is added
dmernies-iohk Dec 12, 2019
decedd5
Saving encrypted wallet function is added and ivoked with hardcoded p…
dmernies-iohk Dec 13, 2019
b94f937
speding password input on restore key is added
dmernies-iohk Dec 16, 2019
689b791
Unlock wallet screen is added to unlock with spending password
dmernies-iohk Dec 18, 2019
892d543
Unlocking wallet function are renamed
dmernies-iohk Dec 18, 2019
5c020db
Cleaning unused code from routes and nodeConection
dmerniestic1987 Dec 19, 2019
0112de9
Corrections by code review
dmerniestic1987 Jan 2, 2020
53d6a6f
Hash is removed from the key that is saved from in localStorate. Now …
dmernies-iohk Jan 7, 2020
8c9a28f
UnlockWalletPassword is used instead of SpendingPassword
dmernies-iohk Jan 8, 2020
f4e53a9
fake salt is removed
dmernies-iohk Jan 8, 2020
797fabb
walletId is removed
dmernies-iohk Jan 10, 2020
9815315
Code Review improvements
dmernies-iohk Jan 10, 2020
4cecbb8
UnlockWalletPassword is mandatory now
dmernies-iohk Jan 14, 2020
853a5e8
A new component to create a new unlock wallet is added
dmernies-iohk Jan 15, 2020
0342b9e
Hide spending password error is deleted
dmernies-iohk Jan 16, 2020
ecc2841
corrections by code review
dmernies-iohk Jan 16, 2020
75a13f1
Form.Control.Feeback is added instead of instead of artisan validations
dmernies-iohk Jan 16, 2020
1761414
<span> tag is used insetead of <em> and Form.Feedback is added
dmernies-iohk Jan 17, 2020
86215c3
Validation of private key is added
dmernies-iohk Jan 17, 2020
86d49cc
Corrections by code review
dmernies-iohk Jan 17, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/explorer/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
},
"rules": {
"func-names": "error",
"new-cap": "off",
"new-cap": ["off", { "capIsNewExceptionPattern": "^Aesjs\.." }],
"arrow-parens": "off",
"consistent-return": "off",
"comma-dangle": "off",
Expand Down
4 changes: 3 additions & 1 deletion examples/wallet/app/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import Settings from './pages/Settings';
import ChooseRestoreOrImport from './containers/ChooseRestoreOrImport';
import Delegate from './pages/Delegate';
import Index from './containers/Index';
import InputKeys from './containers/InputKeys';
import InputKeys from './pages/InputKeys';
import UnlockWallet from './containers/UnlockWallet';

export default () => (
<App>
Expand All @@ -19,6 +20,7 @@ export default () => (
<Route path={routes.STAKING} component={Delegate} />
<Route path={routes.SETTINGS} component={Settings} />
<Route path={routes.INPUT_KEYS} component={InputKeys} />
<Route path={routes.UNLOCK_WALLET} component={UnlockWallet} />
<Route
path={routes.CHOOSE_RESTORE_OR_IMPORT}
component={ChooseRestoreOrImport}
Expand Down
85 changes: 41 additions & 44 deletions examples/wallet/app/actions/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,47 +30,38 @@ import {
getTransactions
} from '../utils/nodeConnection';
import { isValidMnemonic, createSeedFromMnemonic } from '../utils/mnemonic';
import { saveAccountInfoInLocalStorage } from '../utils/storage';
import {
saveEncryptedAccountInfo,
readEncryptedAccountInfo
} from '../utils/storage';

import routes from '../constants/routes.json';

export type SetKeysAction = { type: 'SET_KEYS' } & AccountKeys;
export const SET_KEYS = 'SET_KEYS';
export const PRIVATE_KEY_ERROR = 'privateKeyError';
export const ACCOUNT_STATE_ERROR = 'accountStateError';

export function setAccount(privateKey: string): Thunk {
export function setAccount(
privateKey: string,
unlockWalletPassword: string
): Thunk<SetKeysAction> {
return function setAccountThunk(dispatch) {
return getAccountFromPrivateKey(privateKey).then(
curry(initializeKeysAndRedirect)(dispatch)
return getAccountFromPrivateKey(privateKey).then(keys =>
curry(initializeKeysAndRedirect)(dispatch, keys, unlockWalletPassword)
);
};
}

export function setAccountFromPrivateKey(privateKey: string): Thunk {
return function setAccountFromPrivateKeyThunk(dispatch) {
getAccountFromPrivateKey(privateKey)
.then(loadedPrivateKey => {
dispatch({
type: SET_KEYS,
...loadedPrivateKey
});
return dispatch(updateAccountTransactionsAndState());
})
.catch(error => {
console.error(error);
throw new Error(PRIVATE_KEY_ERROR);
});
};
}

export function updateAccountTransactionsAndState(): Thunk {
return function updateAccountTransactionsAndStateThunk(dispatch) {
return Promise.all([
dispatch(updateAccountTransactions()),
dispatch(updateNodeSettings()),
dispatch(updateAccountState())
]).then(() => dispatch(push(routes.WALLET)));
export function setKeysWithUnlockWalletPassword(
unlockWalletPassword: string = ''
): Thunk<SetKeysAction> {
return function setKeysWithUnlockWalletPasswordThunk(dispatch) {
const accountKeys = readEncryptedAccountInfo(unlockWalletPassword);
if (accountKeys) {
return getAccountFromPrivateKey(accountKeys.privateKey).then(keys =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not necessary to generate the AccountKeys object again, since the AccountKeys object is stored already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wasn't addressed yet

curry(initializeKeysAndRedirect)(dispatch, keys, unlockWalletPassword)
);
}
throw new Error('Invalid password');
};
}

Expand All @@ -83,30 +74,36 @@ export function updateAccountTransactionsAndState(): Thunk {
* @param {boolean} saveAccount If true, the keys are stored in the local storage
*/
const initializeKeysAndRedirect = (
dispatch: Dispatch<SetKeysAction>,
dispatch: Dispatch,
keys: AccountKeys,
saveAccount?: boolean = true
unlockWalletPassword: string = ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this hints the parameter is optional when it's not

) => {
dispatch({
type: SET_KEYS,
...keys
});
if (saveAccount) {
saveAccountInfoInLocalStorage(keys);
}

return dispatch(updateAccountTransactionsAndState());
saveEncryptedAccountInfo(unlockWalletPassword, keys);

return Promise.all([
dispatch(updateAccountTransactions()),
dispatch(updateNodeSettings()),
dispatch(updateAccountState())
])
.catch(console.error)
.then(() => dispatch(push(routes.WALLET)));
};

export function setAccountFromMnemonic(
mnemonicPhrase: string,
mnemonicPassword?: string
) {
mnemonicPassword: string,
unlockWalletPassword: string
): Thunk<SetKeysAction> {
if (isValidMnemonic(mnemonicPhrase)) {
const seed = createSeedFromMnemonic(mnemonicPhrase, mnemonicPassword);
return function setAccountThunk(dispatch) {
return getAccountFromSeed(seed).then(
curry(initializeKeysAndRedirect)(dispatch)
return getAccountFromSeed(seed).then(keys =>
curry(initializeKeysAndRedirect)(dispatch, keys, unlockWalletPassword)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either go with:

.then(keys =>
  initializeKeysAndRedirect(dispatch, keys, unlockWalletPassword)
);

or:

.then(
  curry(initializeKeysAndRedirect)(dispatch, curry.placeholder, unlockWalletPassword)
);

or change the function signature to receive the keys last, and do:

.then(
  curry(initializeKeysAndRedirect)(dispatch, unlockWalletPassword)
);

It's confusing to curry a function and execute it right away. It might lead a reader to infer the function receives even more parameters and'll be executed later on.

);
};
}
Expand All @@ -119,7 +116,7 @@ export type SetAccountStateAction = {
} & AccountState;
export const SET_ACCOUNT_STATE = 'SET_ACCOUNT_STATE';

export function updateAccountState(): Thunk {
export function updateAccountState(): Thunk<SetAccountStateAction> {
return function updateAccountStateThunk(dispatch, getState) {
const { identifier }: { identifier: Identifier } = getState().account;
if (!identifier) {
Expand All @@ -139,7 +136,7 @@ export function updateAccountState(): Thunk {
// TODO: display a notification or something
.catch(() => {
console.error('there was an error fetching account info');
return Promise.reject(new Error(ACCOUNT_STATE_ERROR));
return Promise.reject();
})
);
};
Expand All @@ -151,7 +148,7 @@ export type SetTransactionsAction = {
};
export const SET_TRANSACTIONS = 'SET_TRANSACTIONS';

export function updateAccountTransactions(): Thunk {
export function updateAccountTransactions(): Thunk<SetAccountStateAction> {
return function updateAccountTransactionsThunk(dispatch, getState) {
const { address }: { address: Address } = getState().account;
if (!address) {
Expand All @@ -171,7 +168,7 @@ export function updateAccountTransactions(): Thunk {
// TODO: display a notification or something
.catch(() => {
console.error('there was an error fetching transactions');
return Promise.reject(new Error(ACCOUNT_STATE_ERROR));
return Promise.reject();
})
);
};
Expand Down
25 changes: 4 additions & 21 deletions examples/wallet/app/actions/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,15 @@
import { push } from 'connected-react-router';
import type { Dispatch, GetState } from '../reducers/types';
import type { Address } from '../models';
import routes from '../constants/routes.json';
import {
updateAccountTransactionsAndState,
ACCOUNT_STATE_ERROR
} from './account';
import { readAccountKeysFromLocalStorage } from '../utils/storage';
import routes from '../constants/routes';

export const SET_KEYS = 'SET_KEYS';
import { isUnlockWalletPasswordCreated } from '../utils/storage';

// eslint-disable-next-line import/prefer-default-export
export const redirectToFirstAppPage = () => {
return (dispatch: Dispatch, getState: GetState) => {
const accountKeys = readAccountKeysFromLocalStorage();
if (accountKeys) {
dispatch({
type: SET_KEYS,
...accountKeys
});
return dispatch(updateAccountTransactionsAndState()).catch(error => {
if (error.message === ACCOUNT_STATE_ERROR) {
console.error('There was an error retrieving account status');
}

return dispatch(push(routes.WALLET));
});
}
if (isUnlockWalletPasswordCreated())
return dispatch(push(routes.UNLOCK_WALLET));
const {
account: { address }
}: { account: { address: Address } } = getState();
Expand Down
97 changes: 97 additions & 0 deletions examples/wallet/app/components/CreateUnlockWalletPassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// @flow
import React, { useState } from 'react';
import Form from 'react-bootstrap/Form';
import PropTypes from 'prop-types';

CreateUnlockWalletPassword.propTypes = {
setValidCreateUnlockWalletPassword: PropTypes.func
};
export default function CreateUnlockWalletPassword({
setValidCreateUnlockWalletPassword
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on the called component, the function to be called on a change or other event should be called on{event}, to follow a react naming convention present in, for example, the <input> element with its onChange prop

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here, it should be called onValidPassword or something like that.

}) {
const checkValidUnlockWalletPassword = function checkValidUnlockWalletPassword(
pass
) {
setIsValidPassword(true);
if (!pass) {
setIsValidPassword(false);
return false;
}
if (pass.length < 8) {
setIsValidPassword(false);
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the return value of this function ever used?

}
};

const checkValidUnlockWalletConfirmation = function checkValidUnlockWalletConfirmation(
unlockWalletPassword,
confirmation
) {
setArePasswordAndConfirmationEqual(true);
let isValidUnlockPassword = true;
if (!confirmation || confirmation.length < 8) {
isValidUnlockPassword = false;
}

if (unlockWalletPassword !== confirmation) {
setArePasswordAndConfirmationEqual(false);
isValidUnlockPassword = false;
}

setValidCreateUnlockWalletPassword(
unlockWalletPassword,
isValidUnlockPassword
);
return isValidUnlockPassword;
};

const [
arePasswordAndConfirmationEqual,
setArePasswordAndConfirmationEqual
] = useState(true);

const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all this variables and hooks could be typed via flow

const [isValidPassword, setIsValidPassword] = useState(true);

return (
<Form.Group>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why nest the form groups? there should be one group per input AFAIK: https://react-bootstrap.netlify.com/components/forms/

<Form.Label className="mt-5">Unlock wallet:</Form.Label>
<Form.Group>
<Form.Control
type="password"
id="password"
name="password"
placeholder="New password (min 8 chars)"
value={password}
isInvalid={!isValidPassword}
onChange={event => setPassword(event.target.value)}
onBlur={() => checkValidUnlockWalletPassword(password)}
/>
<Form.Control.Feedback type="invalid">
The password must have at least 8 chars.
</Form.Control.Feedback>
<Form.Control
type="password"
name="confirmPassword"
id="confirmPassword"
placeholder="Confirm password"
value={confirmPassword}
isInvalid={!arePasswordAndConfirmationEqual}
onChange={event => setConfirmPassword(event.target.value)}
onBlur={() =>
checkValidUnlockWalletConfirmation(password, confirmPassword)
}
className="mt-3"
/>
<Form.Text>
This key allows you to unlock your wallet every time you start it and
to keep your account data in a more secure way.
</Form.Text>
<Form.Control.Feedback type="invalid">
password and confirmation must be the same.
</Form.Control.Feedback>
</Form.Group>
</Form.Group>
);
}
Loading