Skip to content

Commit

Permalink
Separate idle modal and Bob "lock" from underlying hsd wallet lock. R…
Browse files Browse the repository at this point in the history
…equest user passphrase when needed. (#128)

* Use default timeout (60s) for wallet unlock

* Removed unused SET_PENDING_TRANSACTIONS

* MiniModal onClose() defaults to history.goBack()

* Do not lock Bob if hsd wallet is locked

Separates Bob's `isLocked` state property from the actual hsd wallet.
`isLocked` will now ONLY refer to Bob's state, triggering the
enter-passphrase prompt to lock out the wallet, and only after
the idle timeout.

Actual hsd wallet lock & unlock will be handled separately

* Do not sign when using createTX to estimate fee

This will prevent hsd from requiring a password just to check the
estimated TX size.

* Accept optional onClose prop in MiniModal

onClose() will be called when user clicks X or outside modal to
close. If no function is provided, it will default to history.goBack()
unless a specific closeRoute prop is passed. If both onClose and
closeRoute are passed, onClose() will be called first instead of
"going" to closeRoute.

* Display error message on TX failure in OpenBid and Reveal panels

* Introduce PassphraseModal

Modal is set in App root and displays when getPassphrase is
activated in global store. Expects a Promise resolve, reject pair
to be passed in via store as well, making it easy to call from
any component to wait for password unlock success before continuing.

* Deploy PassphraseModal any time private key is needed

This includes normal send, OPEN, BID, REVEAL, RENEW, REGISTER

* Update changelog
  • Loading branch information
pinheadmz committed Apr 30, 2020
1 parent 46730f9 commit 8d7698e
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 37 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
with "spendable" balance which is total unconfirmed minus total locked coins
- Covenants in portfolio view now display their value as it affects spendable balance
- Improvements to maxSend based on spendable balance and cleaner fee estimation
- Bob will ask user for passphrase whenever private key is needed (e.g. send TX)
- Bob will no longer "logout" when underlying hsd wallet is locked, however
Bob will still lock the hsd wallet on logout or idle timeout

## [0.2.8] - 2020-03-17
### Fixed
Expand Down
5 changes: 3 additions & 2 deletions app/background/wallet/service.js
Expand Up @@ -151,7 +151,8 @@ class WalletService {
value: Number(toBaseUnits(amount)),
address: to,
}],
subtractFee
subtractFee,
sign: false,
});
return {
feeRate,
Expand Down Expand Up @@ -222,7 +223,7 @@ class WalletService {

unlock = (passphrase) => this._ledgerProxy(
() => null,
() => this.client.unlock(WALLET_ID, passphrase, 60 * 60 * 24 * 365),
() => this.client.unlock(WALLET_ID, passphrase),
);

isLocked = () => this._ledgerProxy(
Expand Down
10 changes: 8 additions & 2 deletions app/components/Modal/MiniModal.js
Expand Up @@ -7,15 +7,21 @@ import './mini-modal.scss';

export class MiniModal extends Component {
static propTypes = {
closeRoute: PropTypes.string.isRequired,
closeRoute: PropTypes.string,
onClose: PropTypes.func,
children: PropTypes.node.isRequired,
title: PropTypes.string.isRequired,
centered: PropTypes.bool,
wide: PropTypes.bool,
};

onClose = () => {
this.props.history.push(this.props.closeRoute)
if (this.props.onClose)
return this.props.onClose();

this.props.closeRoute
? this.props.history.push(this.props.closeRoute)
: this.props.history.goBack();
};

render() {
Expand Down
15 changes: 1 addition & 14 deletions app/ducks/backgroundMonitor.js
Expand Up @@ -2,7 +2,7 @@ import walletClient from '../utils/walletClient';
import nodeClient from '../utils/nodeClient';
import * as logger from '../utils/logClient';
import { store } from '../store/configureStore';
import { LOCK_WALLET, SET_PENDING_TRANSACTIONS } from './walletReducer';
import { SET_PENDING_TRANSACTIONS } from './walletReducer';
import { getInitializationState } from '../db/system';
import isEqual from 'lodash.isequal';
import { SET_NODE_INFO, SET_FEE_INFO, NEW_BLOCK_STATUS } from './nodeReducer';
Expand All @@ -11,7 +11,6 @@ import { getYourBids } from './bids';
import { fetchTransactions, fetchWallet } from './walletActions';

export function createBackgroundMonitor() {
let isLocked;
let timeout;
let info;
let prevNamesWithPendingUpdates = new Set();
Expand All @@ -27,18 +26,6 @@ export function createBackgroundMonitor() {
return;
}

const newIsLocked = await walletClient.isLocked();
if (newIsLocked) {
if (newIsLocked !== isLocked) {
isLocked = newIsLocked;
store.dispatch({
type: LOCK_WALLET,
});
}

return;
}

const infoRes = await nodeClient.getInfo();
const newInfo = {
chain: infoRes.chain,
Expand Down
1 change: 0 additions & 1 deletion app/ducks/myDomains.js
@@ -1,5 +1,4 @@
import walletClient from '../utils/walletClient';
import { SET_PENDING_TRANSACTIONS } from './walletActions';

const FETCH_MY_NAMES_START = 'app/myDomains/fetchMyNamesStart';
const FETCH_MY_NAMES_STOP = 'app/myDomains/fetchMyNamesStop';
Expand Down
27 changes: 23 additions & 4 deletions app/ducks/names.js
Expand Up @@ -6,7 +6,7 @@ import {
stopWalletSync,
waitForWalletSync,
fetchPendingTransactions,
SET_PENDING_TRANSACTIONS
getPassphrase,
} from './walletActions';
import { SET_NAME } from './namesReducer';

Expand Down Expand Up @@ -139,6 +139,10 @@ async function inflateReveals(nClient, walletClient, bids) {
}

export const sendOpen = name => async (dispatch) => {
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});

await walletClient.sendOpen(name);
await namesDb.storeName(name);
await dispatch(fetchPendingTransactions());
Expand All @@ -148,6 +152,9 @@ export const sendBid = (name, amount, lockup, height) => async (dispatch) => {
if (!name) {
return;
}
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});

if (height) {
try {
Expand All @@ -165,34 +172,46 @@ export const sendBid = (name, amount, lockup, height) => async (dispatch) => {
await namesDb.storeName(name);
};

export const sendReveal = (name) => async () => {
export const sendReveal = (name) => async (dispatch) => {
if (!name) {
return;
}
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});

await namesDb.storeName(name);
await walletClient.sendReveal(name);
};

export const sendRedeem = (name) => async () => {
export const sendRedeem = (name) => async (dispatch) => {
if (!name) {
return;
}
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});

await namesDb.storeName(name);
await walletClient.sendRedeem(name);
};

export const sendRenewal = (name) => async () => {
export const sendRenewal = (name) => async (dispatch) => {
if (!name) {
return;
}
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});

await namesDb.storeName(name);
await walletClient.sendRenewal(name);
};

export const sendUpdate = (name, json) => async (dispatch) => {
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});
await namesDb.storeName(name);
await walletClient.sendUpdate(name, json);
await dispatch(fetchPendingTransactions());
Expand Down
23 changes: 17 additions & 6 deletions app/ducks/walletActions.js
Expand Up @@ -16,6 +16,7 @@ import {
START_SYNC_WALLET,
STOP_SYNC_WALLET,
SYNC_WALLET_PROGRESS,
GET_PASSPHRASE,
} from './walletReducer';
import {NEW_BLOCK_STATUS} from './nodeReducer';

Expand All @@ -26,7 +27,6 @@ export const setWallet = opts => {
initialized = false,
address = '',
type = NONE,
isLocked = true,
balance = {},
} = opts;

Expand All @@ -36,7 +36,6 @@ export const setWallet = opts => {
initialized,
address,
type,
isLocked,
balance,
},
};
Expand All @@ -61,20 +60,17 @@ export const fetchWallet = () => async (dispatch, getState) => {
initialized: false,
address: '',
type: NONE,
isLocked: true,
balance: {
...getInitialState().balance,
},
}));
}

const accountInfo = await walletClient.getAccountInfo();
const isLocked = await walletClient.isLocked();
dispatch(setWallet({
initialized: isInitialized,
address: accountInfo && accountInfo.receiveAddress,
type: NONE,
isLocked,
balance: (accountInfo && accountInfo.balance) || {
...getInitialState().balance,
},
Expand All @@ -87,7 +83,9 @@ export const revealSeed = (passphrase) => async () => {

export const unlockWallet = passphrase => async (dispatch) => {
await walletClient.unlock(passphrase);
await dispatch(fetchWallet());
dispatch({
type: UNLOCK_WALLET,
});
};

export const lockWallet = () => async (dispatch) => {
Expand All @@ -105,6 +103,9 @@ export const removeWallet = () => async (dispatch, getState) => {
};

export const send = (to, amount, fee) => async (dispatch) => {
await new Promise((resolve, reject) => {
dispatch(getPassphrase(resolve, reject));
});
const res = await walletClient.send(to, amount, fee);
await dispatch(fetchWallet());
return res;
Expand Down Expand Up @@ -218,6 +219,16 @@ export const resetIdle = () => ({
type: RESET_IDLE,
});

export const getPassphrase = (resolve, reject) => ({
type: GET_PASSPHRASE,
payload: {get: true, resolve, reject},
});

export const closeGetPassphrase = () => ({
type: GET_PASSPHRASE,
payload: {get: false},
});

export const watchActivity = () => dispatch => {
if (!idleInterval) {
// Increment idle once a minute
Expand Down
14 changes: 8 additions & 6 deletions app/ducks/walletReducer.js
Expand Up @@ -13,6 +13,7 @@ export const SET_PENDING_TRANSACTIONS = 'app/wallet/setPendingTransactions';
export const START_SYNC_WALLET = 'app/wallet/startSyncWallet';
export const STOP_SYNC_WALLET = 'app/wallet/stopSyncWallet';
export const SYNC_WALLET_PROGRESS = 'app/wallet/syncWalletProgress';
export const GET_PASSPHRASE = 'app/wallet/getPassphrase';

export function getInitialState() {
return {
Expand All @@ -31,6 +32,7 @@ export function getInitialState() {
idle: 0,
walletSync: false,
walletSyncProgress: 0,
getPassphrase: {get: false},
};
}

Expand All @@ -41,7 +43,6 @@ export default function walletReducer(state = getInitialState(), {type, payload}
...state,
address: payload.address,
type: payload.type,
isLocked: payload.isLocked,
balance: {
...state.balance,
confirmed: payload.balance.confirmed,
Expand All @@ -55,11 +56,7 @@ export default function walletReducer(state = getInitialState(), {type, payload}
case LOCK_WALLET:
return {
...state,
balance: {
...state.balance
},
isLocked: true,
transactions: new Map()
isLocked: true
};
case UNLOCK_WALLET:
return {
Expand Down Expand Up @@ -96,6 +93,11 @@ export default function walletReducer(state = getInitialState(), {type, payload}
...state,
walletSyncProgress: payload,
};
case GET_PASSPHRASE:
return {
...state,
getPassphrase: payload,
};
default:
return state;
}
Expand Down

0 comments on commit 8d7698e

Please sign in to comment.