Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Remove seed words from Redux #608

Merged
merged 3 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
718 changes: 361 additions & 357 deletions locale/texts.pot

Large diffs are not rendered by default.

5 changes: 0 additions & 5 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,6 @@ export const networkUpdate = data => ({ type: 'network_update', payload: data })
*/
export const lastFailedRequest = data => ({ type: 'last_failed_request', payload: data });

/**
* Save words in Redux (it's always cleaned after the use)
*/
export const updateWords = data => ({ type: 'update_words', payload: data });

/**
* Update token that is selected in the wallet
*/
Expand Down
40 changes: 13 additions & 27 deletions src/components/ModalBackupWords.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,11 @@ import { t } from 'ttag';

import $ from 'jquery';
import _ from 'lodash';
import { updateWords } from '../actions/index';
import { connect } from 'react-redux';
import hathorLib from '@hathor/wallet-lib';
import { WORDS_VALIDATION } from '../constants';
import { getGlobalWallet } from '../modules/wallet';


const mapDispatchToProps = dispatch => {
return {
updateWords: data => dispatch(updateWords(data)),
};
};


const mapStateToProps = (state) => {
return {
words: state.words,
};
};


/**
* Component that shows a modal to do backup of words
* If user is already inside the wallet asks for the user password
Expand All @@ -53,13 +37,15 @@ class ModalBackupWords extends React.Component {
* validationStep.options array of strings composed by the correctWord and four wrong options.
* validationStep.done if true the step was correctly completed.
* validationStep.last if true it is the last step of the array
* words? {string} The 24 words that will be used, if there is still no password attached to them
*/
this.state = {
errorMessage: '',
passwordSuccess: false,
showValidation: false,
passwordFormValidated: false,
validationSteps: [],
words: props.words || '',
};
}

Expand All @@ -71,10 +57,8 @@ class ModalBackupWords extends React.Component {
passwordSuccess: false,
showValidation: false,
validationSteps: [],
words: '',
});
if (this.props.needPassword) {
this.props.updateWords(null);
}
if (this.refs.password) {
this.refs.password.value = '';
}
Expand All @@ -90,6 +74,7 @@ class ModalBackupWords extends React.Component {
};

componentWillUnmount = () => {
this.setState({ words: '' }); // Being extra cautious with sensitive information
$('#backupWordsModal').modal('hide');
// Removing all event listeners
$('#backupWordsModal').off();
Expand All @@ -110,15 +95,15 @@ class ModalBackupWords extends React.Component {
try {
const { storage } = getGlobalWallet();
const accessData = await storage.getAccessData();
const words = hathorLib.cryptoUtils.decryptData(accessData.words, password);
this.props.updateWords(words);
this.setState({ passwordSuccess: true, errorMessage: '' });
const walletWords = hathorLib.cryptoUtils.decryptData(accessData.words, password);
this.setState({ words: walletWords, passwordSuccess: true, errorMessage: '' });
} catch (err) {
// XXX: The `ErrorMessages.ErrorMessages` property from the lib needs fixing.
if (err.errorCode === hathorLib.ErrorMessages.ErrorMessages.DECRYPTION_ERROR
|| err.errorCode === hathorLib.ErrorMessages.ErrorMessages.INVALID_PASSWD) {
// If the password is invalid it will throw a DecryptionError
this.setState({ errorMessage: t`Invalid password` });
return; // No need to rethrow if the error is an incorrect password
}
throw err;
}
Expand All @@ -131,7 +116,7 @@ class ModalBackupWords extends React.Component {
* As the words were shown previosly, the user should be able to find any word given an index.
*/
handleWordsSaved = () => {
const wordsArray = this.props.words.split(' ');
const wordsArray = this.state.words.split(' ');
const wordsToValidate = _.shuffle(wordsArray).slice(0, WORDS_VALIDATION);
const validationSteps = wordsToValidate.map((word, index) => {
const backupIndex = wordsArray.indexOf(word);
Expand Down Expand Up @@ -188,6 +173,7 @@ class ModalBackupWords extends React.Component {
return;
}
if (validationStep.last) {
this.setState({ words: '' }); // Being extra cautious with sensitive information
this.props.validationSuccess();
return;
}
Expand Down Expand Up @@ -222,13 +208,13 @@ class ModalBackupWords extends React.Component {
const renderButtons = () => {
if (this.props.needPassword && !this.state.passwordSuccess) {
return renderAskPasswordButtons();
} else if (this.props.words && !this.state.showValidation) {
} else if (this.state.words && !this.state.showValidation) {
return renderShowWordsButtons();
}
};

const renderWordsTd = (start, end) => {
return this.props.words.split(' ').slice(start, end).map((word, idx) => {
return this.state.words.split(' ').slice(start, end).map((word, idx) => {
return (
<td key={word}><strong>{start+idx+1}.</strong> {word} </td>
)
Expand Down Expand Up @@ -341,7 +327,7 @@ class ModalBackupWords extends React.Component {
const renderBody = () => {
if (this.props.needPassword && !this.state.passwordSuccess) {
return renderAskPassword();
} else if (this.props.words && !this.state.showValidation) {
} else if (this.state.words && !this.state.showValidation) {
return renderShowWords();
} else if (this.state.showValidation) {
return renderValidation();
Expand Down Expand Up @@ -398,4 +384,4 @@ class ModalBackupWords extends React.Component {
}
}

export default connect(mapStateToProps, mapDispatchToProps)(ModalBackupWords);
export default ModalBackupWords;
4 changes: 0 additions & 4 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ const initialState = {
lastFailedRequest: undefined,
// Status code of last failed response
requestErrorStatusCode: undefined,
// Wallet words
words: undefined,
// Tokens already saved: array of objects
// {'name', 'symbol', 'uid'}
tokens: [hathorLib.constants.HATHOR_TOKEN_CONFIG],
Expand Down Expand Up @@ -159,8 +157,6 @@ const rootReducer = (state = initialState, action) => {
return onCleanData(state, action);
case 'last_failed_request':
return Object.assign({}, state, {lastFailedRequest: action.payload});
case 'update_words':
return Object.assign({}, state, {words: action.payload});
case 'select_token':
return Object.assign({}, state, {selectedToken: action.payload});
case 'new_tokens':
Expand Down
5 changes: 5 additions & 0 deletions src/screens/LoadWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ function LoadWallet() {
LOCAL_STORE.unlock();
// First we clean what can still be there of a last wallet
wallet.generateWallet(words, '', newPin, password);

// Being extra cautious with sensitive information
setWords('');
setPassword('');

LOCAL_STORE.markBackupDone();
LOCAL_STORE.open(); // Mark this wallet as open, so that it does not appear locked after loading
}
Expand Down
19 changes: 9 additions & 10 deletions src/screens/NewWallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import logo from '../assets/images/hathor-logo.png';
import ChoosePassword from '../components/ChoosePassword';
import ChoosePin from '../components/ChoosePin';
import HathorAlert from '../components/HathorAlert';
import { updateWords } from '../actions/index';
import { useDispatch, useSelector } from 'react-redux';
import hathorLib from '@hathor/wallet-lib';
import InitialImages from '../components/InitialImages';
import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal';
Expand All @@ -34,11 +32,8 @@ import LOCAL_STORE from '../storage';
function NewWallet() {
const navigate = useNavigate();
const context = useContext(GlobalModalContext);
const dispatch = useDispatch();

const { words } = useSelector(state => ({
words: state.words,
}));
const [words, setWords] = useState('');
const [password, setPassword] = useState('');
const [step2, setStep2] = useState(false);
const [askPassword, setAskPassword] = useState(false);
Expand All @@ -55,8 +50,8 @@ function NewWallet() {
const isValid = confirmFormRef.current.checkValidity();
if (isValid) {
confirmFormRef.current.classList.remove('was-validated')
const words = hathorLib.walletUtils.generateWalletWords(hathorLib.constants.HD_WALLET_ENTROPY);
dispatch(updateWords(words));
const newWords = hathorLib.walletUtils.generateWalletWords(hathorLib.constants.HD_WALLET_ENTROPY);
setWords(newWords);
setStep2(true);
} else {
confirmFormRef.current.classList.add('was-validated')
Expand Down Expand Up @@ -85,6 +80,7 @@ function NewWallet() {
*/
const backupNow = () => {
context.showModal(MODAL_TYPES.BACKUP_WORDS, {
words,
needPassword: false,
validationSuccess,
});
Expand All @@ -107,10 +103,13 @@ function NewWallet() {
// Generate addresses and load data
LOCAL_STORE.unlock();
wallet.generateWallet(words, '', newPin, password);

// Being extra cautious with sensitive information
setWords('');
setPassword('');

// Mark this wallet as open, so that it does not appear locked after loading
LOCAL_STORE.open();
// Clean words from redux
dispatch(updateWords(null));
}

/**
Expand Down
3 changes: 1 addition & 2 deletions src/screens/Wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import BackButton from '../components/BackButton';
import { colors } from '../constants';
import { TOKEN_DOWNLOAD_STATUS } from '../sagas/tokens';
import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal';
import { tokenFetchBalanceRequested, tokenFetchHistoryRequested, updateWords, } from '../actions/index';
import { tokenFetchBalanceRequested, tokenFetchHistoryRequested } from '../actions/index';
import LOCAL_STORE from '../storage';
import { useNavigate } from 'react-router-dom';
import { getGlobalWallet } from "../modules/wallet";
Expand Down Expand Up @@ -210,7 +210,6 @@ function Wallet() {
context.hideModal();
LOCAL_STORE.markBackupDone();

dispatch(updateWords(null));
setBackupDone(true);
setSuccessMessage(t`Backup completed!`);
alertSuccessRef.current.show(3000);
Expand Down
3 changes: 1 addition & 2 deletions src/screens/WalletVersionError.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { t } from 'ttag';
import logo from '../assets/images/hathor-white-logo.png';
import Version from '../components/Version';
import HathorAlert from '../components/HathorAlert';
import { updateWords, walletReset } from '../actions/index';
import { walletReset } from '../actions/index';
import { useDispatch } from "react-redux";
import { GlobalModalContext, MODAL_TYPES } from '../components/GlobalModal';
import LOCAL_STORE from '../storage';
Expand All @@ -35,7 +35,6 @@ function WalletVersionError() {
const backupSuccess = () => {
context.hideModal();
LOCAL_STORE.markBackupDone();
dispatch(updateWords(null));
alertSuccessRef.current.show(3000);
}

Expand Down
Loading