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

wallet: add deepclean and wdb backup #308

Merged
merged 4 commits into from
Mar 3, 2021
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
2 changes: 2 additions & 0 deletions app/background/wallet/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export const clientStub = ipcRendererInjector => makeClient(ipcRendererInjector,
'estimateMaxSend',
'removeWalletById',
'updateAccountDepth',
'backup',
'rescan',
'deepClean',
'reset',
'sendOpen',
'sendBid',
Expand Down
43 changes: 12 additions & 31 deletions app/background/wallet/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,38 +185,10 @@ class WalletService {
return res;
};

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

backup = async (path) => {
if (!path) throw new Error('path must not be undefined');
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();
return this.client.execute('backupwallet', [path]);
};

rescan = async (height = 0) => {
Expand All @@ -232,6 +204,13 @@ class WalletService {
await wdb.rescan(height);
};

deepClean = async () => {
await this._ensureClient();
const wdb = this.node.wdb;

await wdb.deepClean();
};

importSeed = async (name, passphrase, mnemonic) => {
await this._ensureClient();
this.setWallet(name);
Expand Down Expand Up @@ -882,7 +861,9 @@ const methods = {
estimateMaxSend: service.estimateMaxSend,
removeWalletById: service.removeWalletById,
updateAccountDepth: service.updateAccountDepth,
backup: service.backup,
rescan: service.rescan,
deepClean: service.deepClean,
reset: service.reset,
sendOpen: service.sendOpen,
sendBid: service.sendBid,
Expand Down
5 changes: 5 additions & 0 deletions app/components/Alert/alert.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@
border: 1px solid $orange-red;
color: $orange-red;
}

&--warning {
border: 1px solid $manatee-gray;
color: $manatee-gray;
}
}
12 changes: 8 additions & 4 deletions app/components/Alert/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,25 @@ export default class Alert extends Component {
static propTypes = {
type: PropTypes.oneOf([
'error',
'warning',
'success'
]).isRequired,
message: PropTypes.string.isRequired
message: PropTypes.string,
children: PropTypes.node,
};

render() {
if (!this.props.message) {
const { message, type, children} = this.props;

if (!message && !children) {
return null;
}

const name = `alert alert--${this.props.type}`;
const name = `alert alert--${type}`;

return (
<div className={name}>
{this.props.message}
{children || message}
</div>
);
}
Expand Down
32 changes: 5 additions & 27 deletions app/pages/Onboarding/ImportSeedFlow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ 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 @@ -46,7 +45,6 @@ class ImportSeedFlow extends Component {
passphrase: '',
mnemonic: '',
isLoading: false,
accountDepth: 200,
};

render() {
Expand Down Expand Up @@ -103,38 +101,23 @@ class ImportSeedFlow extends Component {
return (
<ImportSeedEnterMnemonic
currentStep={3}
totalSteps={5}
totalSteps={4}
onBack={() => this.goTo(CREATE_PASSWORD)}
onNext={(mnemonic) => {
this.setState({
mnemonic,
});
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={5}
totalSteps={5}
onBack={() => this.goTo(UPDATE_ACCOUNT_DEPTH)}
currentStep={4}
totalSteps={4}
onBack={() => this.setState({currentStep: ENTRY_STEP})}
onNext={async (optInState) => {
await analytics.setOptIn(optInState);
await this.finishFlow(this.state.mnemonic);
Expand All @@ -153,14 +136,9 @@ 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();
Expand Down
56 changes: 56 additions & 0 deletions app/pages/Settings/DeepCleanAndRescanModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, {Component} from "react";
import MiniModal from "../../components/Modal/MiniModal";
import {withRouter} from "react-router-dom";
import Checkbox from "../../components/Checkbox";
import Anchor from "../../components/Anchor";
import Alert from "../../components/Alert";
import walletClient from '../../utils/walletClient';

@withRouter
export default class DeepCleanAndRescanModal extends Component {
constructor(props) {
super(props);
this.state = {
hasBackup: false,
}
}

render() {
const { hasBackup } = this.state;
return (
<MiniModal
closeRoute="/settings/wallet"
title="Deep Clean + Rescan"
centered
>
<Alert
type="warning"
>
This action wipes out balance and transaction history in the wallet DB but retains key hashes and name maps. It should be used only if the wallet state has been corrupted by issues like the <Anchor href="https://github.com/handshake-org/hsd/issues/454">reserved name registration bug</Anchor> or the <Anchor href="https://github.com/handshake-org/hsd/pull/464">locked coins balance after FINALIZE bug</Anchor>.
</Alert>
<div className="interstitial-warning-modal__checkbox">
<Checkbox
className="interstitial-warning-modal__checkbox-box"
onChange={() => this.setState({ hasBackup: !hasBackup })}
checked={hasBackup}
/>
<div className="interstitial-warning-modal__checkbox-label">
I have backed up my Wallet DB.
</div>
</div>
<button
className="settings__btn"
onClick={async () => {
if (!hasBackup) return;
await walletClient.deepClean();
walletClient.rescan(0);
this.props.history.push('/settings/wallet');
}}
disabled={!hasBackup}
>
Deep Clean and Rescan
</button>
</MiniModal>
);
}
}
Loading