Skip to content

Commit

Permalink
wallet: add ability to set account depth on first import; remove hsd …
Browse files Browse the repository at this point in the history
…soft fork (#301)

* wallet: add ability to set account depth on first import; remove hsd soft fork

* remove dead code

* fix grammatical error

* fix grammatical error

* add error handling; hide input behind optional cta

* add back account depth setters
  • Loading branch information
chikeichan authored Feb 18, 2021
1 parent 5711bea commit 07afb44
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 33 deletions.
1 change: 1 addition & 0 deletions app/background/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const clientStub = ipcRendererInjector => makeClient(ipcRendererInjector,
'estimateTxFee',
'estimateMaxSend',
'removeWalletById',
'updateAccountDepth',
'rescan',
'reset',
'sendOpen',
Expand Down
36 changes: 35 additions & 1 deletion app/background/wallet/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,40 @@ class WalletService {
return res;
};

updateAccountDepth = async (changeDepth, receiveDepth) => {
if (!this.name) return null;

await this._ensureClient();
const wallet = await this.node.wdb.get(this.name);

if (!wallet) return null;

const account = await wallet.getAccount('default');
const initChange = 0;
const initReceive = 0;
const b = this.node.wdb.db.batch();

account.changeDepth = changeDepth;
account.receiveDepth = receiveDepth;
await account.save(b);

if (changeDepth) {
for (let i = initChange; i < changeDepth; i++) {
const key = account.deriveChange(i);
await account.saveKey(b, key);
}
}

if (receiveDepth) {
for (let j = initReceive; j < receiveDepth; j++) {
const key = account.deriveReceive(j);
await account.saveKey(b, key);
}
}

await b.write();
};

rescan = async (height = 0) => {
await this._ensureClient();
const wdb = this.node.wdb;
Expand Down Expand Up @@ -218,7 +252,6 @@ class WalletService {
payload: uniq([...wids, name]),
});

this.rescan(0);
return res;
};

Expand Down Expand Up @@ -848,6 +881,7 @@ const methods = {
estimateTxFee: service.estimateTxFee,
estimateMaxSend: service.estimateMaxSend,
removeWalletById: service.removeWalletById,
updateAccountDepth: service.updateAccountDepth,
rescan: service.rescan,
reset: service.reset,
sendOpen: service.sendOpen,
Expand Down
16 changes: 16 additions & 0 deletions app/ducks/walletActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export const setWallet = opts => {
type = NONE,
balance = {},
apiKey = '',
changeDepth,
receiveDepth,
} = opts;

return {
Expand All @@ -45,6 +47,8 @@ export const setWallet = opts => {
type,
balance,
apiKey,
changeDepth,
receiveDepth,
},
};
};
Expand Down Expand Up @@ -93,6 +97,12 @@ export const fetchWallet = () => async (dispatch, getState) => {

const apiKey = await walletClient.getAPIKey();


const {
changeDepth = 0,
receiveDepth = 0,
} = accountInfo || {};

dispatch(setWallet({
wid: accountInfo ? accountInfo.wid : '',
initialized: isInitialized,
Expand All @@ -102,9 +112,15 @@ export const fetchWallet = () => async (dispatch, getState) => {
...getInitialState().balance,
},
apiKey,
changeDepth,
receiveDepth,
}));
};

export const setAccountDepth = (changeDepth = 0, receiveDepth = 0) => async () => {
return walletClient.updateAccountDepth(changeDepth, receiveDepth);
}

export const hasAddress = (address) => async () => {
return walletClient.hasAddress(address);
};
Expand Down
4 changes: 4 additions & 0 deletions app/ducks/walletReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export function getInitialState() {
lockedConfirmed: 0,
lockedUnconfirmed: 0,
},
changeDepth: 0,
receiveDepth: 0,
transactions: new Map(),
idle: 0,
walletSync: false,
Expand Down Expand Up @@ -63,6 +65,8 @@ export default function walletReducer(state = getInitialState(), {type, payload}
lockedConfirmed: payload.balance.lockedConfirmed,
spendable: payload.balance.unconfirmed - payload.balance.lockedUnconfirmed,
},
changeDepth: payload.changeDepth,
receiveDepth: payload.receiveDepth,
initialized: typeof payload.initialized === 'undefined' ? state.initialized : payload.initialized,
apiKey: payload.apiKey,
};
Expand Down
42 changes: 33 additions & 9 deletions app/pages/Onboarding/ImportSeedFlow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import OptInAnalytics from '../OptInAnalytics';
import { clientStub as aClientStub } from '../../../background/analytics/client';
import { showError } from '../../../ducks/notifications';
import SetName from '../SetName';
import UpdateAccountDepth from "../UpdateAccountDepth";

const analytics = aClientStub(() => require('electron').ipcRenderer);

Expand All @@ -23,6 +24,7 @@ const CREATE_PASSWORD = 'CREATE_PASSWORD';
const ENTRY_STEP = 'ENTRY';
const OPT_IN_ANALYTICS = 'ANALYTICS';
const SET_NAME = 'SET_NAME';
const UPDATE_ACCOUNT_DEPTH = 'UPDATE_ACCOUNT_DEPTH';

class ImportSeedFlow extends Component {
static propTypes = {
Expand All @@ -44,6 +46,7 @@ class ImportSeedFlow extends Component {
passphrase: '',
mnemonic: '',
isLoading: false,
accountDepth: 200,
};

render() {
Expand All @@ -52,7 +55,7 @@ class ImportSeedFlow extends Component {
return (
<Terms
currentStep={0}
totalSteps={4}
totalSteps={5}
onAccept={() => this.setState({currentStep: WARNING_STEP})}
onBack={() => this.props.history.push('/existing-options')}
/>
Expand All @@ -61,7 +64,7 @@ class ImportSeedFlow extends Component {
return (
<ImportSeedWarning
currentStep={0}
totalSteps={4}
totalSteps={5}
onBack={() => this.setState({currentStep: TERMS_OF_USE})}
onNext={() => this.goTo(SET_NAME)}
onCancel={() => this.props.history.push('/funding-options')}
Expand All @@ -71,7 +74,7 @@ class ImportSeedFlow extends Component {
return (
<SetName
currentStep={1}
totalSteps={4}
totalSteps={5}
onBack={() => this.setState({
currentStep: WARNING_STEP,
})}
Expand All @@ -85,7 +88,7 @@ class ImportSeedFlow extends Component {
return (
<CreatePassword
currentStep={2}
totalSteps={4}
totalSteps={5}
onBack={() => this.setState({currentStep: WARNING_STEP})}
onNext={passphrase => {
this.setState({
Expand All @@ -100,23 +103,38 @@ class ImportSeedFlow extends Component {
return (
<ImportSeedEnterMnemonic
currentStep={3}
totalSteps={4}
totalSteps={5}
onBack={() => this.goTo(CREATE_PASSWORD)}
onNext={(mnemonic) => {
this.setState({
mnemonic,
});
this.goTo(OPT_IN_ANALYTICS);
this.goTo(UPDATE_ACCOUNT_DEPTH);
}}
onCancel={() => this.props.history.push('/funding-options')}
/>
);
case UPDATE_ACCOUNT_DEPTH:
return (
<UpdateAccountDepth
currentStep={4}
totalSteps={5}
onBack={() => this.goTo(ENTRY_STEP)}
onNext={(accountDepth) => {
this.setState({
accountDepth,
});
this.goTo(OPT_IN_ANALYTICS);
}}
onCancel={() => this.props.history.push('/funding-options')}
/>
)
case OPT_IN_ANALYTICS:
return (
<OptInAnalytics
currentStep={4}
totalSteps={4}
onBack={() => this.setState({currentStep: ENTRY_STEP})}
currentStep={5}
totalSteps={5}
onBack={() => this.goTo(UPDATE_ACCOUNT_DEPTH)}
onNext={async (optInState) => {
await analytics.setOptIn(optInState);
await this.finishFlow(this.state.mnemonic);
Expand All @@ -135,9 +153,15 @@ class ImportSeedFlow extends Component {
}

finishFlow = async mnemonic => {
const {accountDepth} = this.state;
this.setState({isLoading: true});
try {
await walletClient.importSeed(this.state.name, this.state.passphrase, mnemonic);
if (accountDepth > 200) {

await walletClient.updateAccountDepth(accountDepth, accountDepth);
}
walletClient.rescan(0);
await this.props.completeInitialization(this.state.name, this.state.passphrase);
await this.props.fetchWallet();
await this.props.fetchTransactions();
Expand Down
130 changes: 130 additions & 0 deletions app/pages/Onboarding/UpdateAccountDepth/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import c from 'classnames';
import WizardHeader from '../../../components/WizardHeader';
import "./update-account-depth.scss";

export default class UpdateAccountDepth extends Component {
static propTypes = {
onNext: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onBack: PropTypes.func.isRequired,
currentStep: PropTypes.number.isRequired,
totalSteps: PropTypes.number.isRequired,
};

constructor(props) {
super(props);
this.state = {
depth: 200,
errorMessage: '',
isChanging: false,
};
}

updateAccountDepth = async () => {
const { depth } = this.state;

if (depth < 200) {
this.setState({
errorMessage: 'Account depth cannot be less than 200',
});
return;
}

this.props.onNext(depth);
};

renderMaybeChange() {
const { errorMessage } = this.state;
return (
<div className="update-account-depth__content">
<div className="update-account-depth__header_text">
Do you want to change default account depth?
</div>
<div className="update-account-depth__subheader">
Most users can safely keep default depth. A higher initial account depth configures your node to search for more transactions.
</div>
<div className="update-account-depth__error">
{errorMessage}
</div>
<div className="update-account-depth__footer">
<button
className="extension_cta_button update-account-depth__secondary-cta"
onClick={() => this.setState({ isChanging: true })}
>
Change Account Depth
</button>
<button
className="extension_cta_button update-account-depth__cta"
onClick={this.updateAccountDepth}
>
Keep Default Depth
</button>
</div>
</div>
)
}

renderChangeInput() {
const { depth, errorMessage } = this.state;
return (
<div className="update-account-depth__content">
<div className="update-account-depth__header_text">
Set Initial Account Depth
</div>
<div className="update-account-depth__subheader">
A higher initial account depth configures your node to search for more transactions. We recommend that power users set initial account depth to their wallet's total number of transactions, and round up to the nearest hundreds (e.g. set account depth to 1900 if your wallet has 1873 transactions).
</div>
<input
className={c('update-account-depth__input', {
'update-account-depth__input--error': errorMessage,
})}
min={200}
step={1}
pattern="[0-9]"
onChange={e => this.setState({ depth: +e.target.value, errorMessage: '' })}
value={depth}
/>
<div className="update-account-depth__error">
{errorMessage}
</div>
<div className="update-account-depth__footer">
<button
className="extension_cta_button update-account-depth__secondary-cta"
onClick={() => this.setState({
isChanging: false,
depth: 200,
errorMessage: '',
})}
>
Cancel
</button>
<button
className="extension_cta_button update-account-depth__cta"
onClick={this.updateAccountDepth}
>
Next
</button>
</div>
</div>
);
}

render() {
const { onBack, onCancel, currentStep, totalSteps } = this.props;
const { isChanging } = this.state;

return (
<div className="update-account-depth">
<WizardHeader
currentStep={currentStep}
totalSteps={totalSteps}
onBack={onBack}
onCancel={onCancel}
/>
{ isChanging ? this.renderChangeInput() : this.renderMaybeChange() }
</div>
);
}
}
Loading

0 comments on commit 07afb44

Please sign in to comment.