Skip to content

Commit

Permalink
[tickets] Only ProcessManaged at startup if needed (#3457)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheusd authored and alexlyp committed May 10, 2021
1 parent a4fdbee commit 53ec152
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 78 deletions.
31 changes: 27 additions & 4 deletions app/actions/ControlActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { reverseRawHash, rawToHex } from "helpers/byteActions";
import { listUnspentOutputs } from "./TransactionActions";
import { updateUsedVSPs, getVSPTrackedTickets } from "./VSPActions";
import { isNumber } from "fp";
import { setNeedsVSPdProcessTickets } from "./SettingsActions";

const {
RescanRequest,
Expand Down Expand Up @@ -405,6 +406,10 @@ export const newPurchaseTicketsAttempt = (
const accts = account.value !== 0 ? [account.value, 0] : [account.value];
const purchaseTicketsResponse = await dispatch(
unlockAcctAndExecFn(passphrase, accts, async () => {
// Since we're about to purchase a ticket, ensure on next startup we'll
// process managed tickets.
dispatch(setNeedsVSPdProcessTickets(true));

const res = await wallet.purchaseTicketsV3(
walletService,
account,
Expand Down Expand Up @@ -516,8 +521,9 @@ export const startTicketBuyerV3Attempt = (
unlockAcctAndExecFn(
passphrase,
[accountNum],
() =>
wallet.startTicketAutoBuyerV3(ticketBuyerService, {
() => {
dispatch(setNeedsVSPdProcessTickets(true));
return wallet.startTicketAutoBuyerV3(ticketBuyerService, {
mixedAccount,
mixedAcctBranch,
changeAccount,
Expand All @@ -527,7 +533,8 @@ export const startTicketBuyerV3Attempt = (
accountNum,
pubkey: vsp.pubkey,
host: vsp.host
}),
});
},
true
)
);
Expand Down Expand Up @@ -1253,7 +1260,9 @@ export const lockAccount = (acctNumber) => async (dispatch, getState) => {
if (!account.encrypted) {
throw "Account not encrypted";
}
await wallet.lockAccount(sel.walletService(getState()), acctNumber);
const lockable = filterUnlockableAccounts([acctNumber], getState);
if (lockable.length === 0) return;
await wallet.lockAccount(sel.walletService(getState()), lockable[0]);
dispatch({ type: LOCKACCOUNT_SUCCESS });
} catch (error) {
dispatch({ type: LOCKACCOUNT_FAILED, error });
Expand Down Expand Up @@ -1304,6 +1313,20 @@ export const monitorLockableAccounts = () => (dispatch, getState) => {
// unlocked accounts.
if (getState().control.unlockAndExecFnRunning) return;

// If there are no more tickets being tracked (meaning all were confirmed)
// and we didn't skip the initial ProcessManagedTickets page and the
// autobuyer isn't running, disable running processManagedTickets on the
// next wallet execution.
const canDisableProcessManaged = sel.canDisableProcessManaged(getState());
const getRunningIndicator = sel.getRunningIndicator(getState());
if (
newTicketAccounts.length === 0 &&
canDisableProcessManaged &&
!getRunningIndicator
) {
dispatch(setNeedsVSPdProcessTickets(false));
}

// Attempt to relock all accounts that can now be locked.
const lockable = filterUnlockableAccounts(toLockAccts, getState);
if (lockable.length === 0) return;
Expand Down
6 changes: 5 additions & 1 deletion app/actions/DaemonActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,9 @@ export const startWallet = (selectedWallet, hasPassPhrase) => (
const mixedAccountBranch = walletCfg.get(cfgConstants.MIXED_ACC_BRANCH);
const isLegacy = walletCfg.get(cfgConstants.VSP_IS_LEGACY);
const rememberedVspHost = walletCfg.get(cfgConstants.REMEMBERED_VSP_HOST);
const needsVSPdProcessManaged = walletCfg.get(
cfgConstants.NEEDS_VSPD_PROCESS_TICKETS
);

const autobuyerSettings = walletCfg.get(cfgConstants.AUTOBUYER_SETTINGS);
dispatch({
Expand Down Expand Up @@ -491,7 +494,8 @@ export const startWallet = (selectedWallet, hasPassPhrase) => (
enableDex,
dexAccount,
rpcCreds,
btcWalletName
btcWalletName,
needsVSPdProcessManaged
});
selectedWallet.value.isTrezor && dispatch(enableTrezor());
await dispatch(getVersionServiceAttempt());
Expand Down
8 changes: 8 additions & 0 deletions app/actions/SettingsActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getWalletCfg, getGlobalCfg } from "config";
import { isTestNet } from "selectors";
import { equalElements } from "helpers";
import * as wallet from "wallet";
import * as sel from "selectors";
import { closeWalletRequest } from "actions/WalletLoaderActions";
import { closeDaemonRequest, backToCredentials } from "actions/DaemonActions";
import {
Expand Down Expand Up @@ -205,3 +206,10 @@ export const updateStateVoteSettingsChanged = (settings) => (
dispatch({ tempSettings: currentSettings, type: SETTINGS_UNCHANGED });
}
};

export const setNeedsVSPdProcessTickets = (value) => (dispatch, getState) => {
const walletName = sel.getWalletName(getState());
const isTestNet = sel.isTestNet(getState());
const walletConfig = getWalletCfg(isTestNet, walletName);
walletConfig.set(configConstants.NEEDS_VSPD_PROCESS_TICKETS, value);
};
120 changes: 61 additions & 59 deletions app/actions/VSPActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ export const syncVSPTicketsRequest = ({
} catch (error) {
dispatch({ type: SYNCVSPTICKETS_FAILED, error });
}

// Finally, ask the wallet to reprocess existing tickets. We do this via a
// setTimeout to avoid stalling the return of this function.
setTimeout(() => dispatch(processManagedTickets(passphrase), 500));
};

// getTicketSignature receives the tickethash and request and sign it using the
Expand Down Expand Up @@ -675,68 +679,61 @@ export const PROCESSMANAGEDTICKETS_FAILED = "PROCESSMANAGEDTICKETS_FAILED";

// processManagedTickets gets all vsp and check for tickets which still not
// synced, and sync them.
export const processManagedTickets = (passphrase) => (dispatch, getState) =>
new Promise((resolve, reject) => {
const asyncProcess = async () => {
const walletService = sel.walletService(getState());
const availableVSPsPubkeys = sel.getAvailableVSPsPubkeys(getState());
try {
dispatch({ type: PROCESSMANAGEDTICKETS_ATTEMPT });
let feeAccount, changeAccount;
const mixedAccount = sel.getMixedAccount(getState());
if (mixedAccount) {
feeAccount = mixedAccount;
changeAccount = sel.getChangeAccount(getState());
} else {
feeAccount = sel.defaultSpendingAccount(getState()).value;
changeAccount = sel.defaultSpendingAccount(getState()).value;
}
await dispatch(
unlockAllAcctAndExecFn(passphrase, async () => {
// Process all managed tickets on all VSPs.
await Promise.all(
availableVSPsPubkeys.map((vsp) =>
wallet.processManagedTickets(
walletService,
vsp.host,
vsp.pubkey,
feeAccount,
changeAccount
)
)
);

// Update the list of dcrwallet tracked VSP tickets. This figures out
// which accounts need to be left unlocked.
await dispatch(getVSPTrackedTickets());
})
export const processManagedTickets = (passphrase) => async (
dispatch,
getState
) => {
const walletService = sel.walletService(getState());
const availableVSPsPubkeys = sel.getAvailableVSPsPubkeys(getState());
try {
dispatch({ type: PROCESSMANAGEDTICKETS_ATTEMPT });
let feeAccount, changeAccount;
const mixedAccount = sel.getMixedAccount(getState());
if (mixedAccount) {
feeAccount = mixedAccount;
changeAccount = sel.getChangeAccount(getState());
} else {
feeAccount = sel.defaultSpendingAccount(getState()).value;
changeAccount = sel.defaultSpendingAccount(getState()).value;
}
await dispatch(
unlockAllAcctAndExecFn(passphrase, async () => {
// Process all managed tickets on all VSPs.
await Promise.all(
availableVSPsPubkeys.map((vsp) =>
wallet.processManagedTickets(
walletService,
vsp.host,
vsp.pubkey,
feeAccount,
changeAccount
)
)
);

// get vsp tickets fee status errored so we can resync them
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_ERRORED));
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_STARTED));
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_PAID));
dispatch({ type: PROCESSMANAGEDTICKETS_SUCCESS });
resolve(true);
} catch (error) {
dispatch({ type: PROCESSMANAGEDTICKETS_FAILED, error });
if (
String(error).indexOf(
"wallet.Unlock: invalid passphrase:: secretkey.DeriveKey"
) > 0
) {
reject("Invalid private passphrase, please try again.");
return;
}
reject(error);
return;
}
};
// Update the list of dcrwallet tracked VSP tickets. This figures out
// which accounts need to be left unlocked.
await dispatch(getVSPTrackedTickets());
})
);

asyncProcess()
.then((r) => resolve(r))
.catch((error) => reject(error));
});
// get vsp tickets fee status errored so we can resync them
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_ERRORED));
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_STARTED));
await dispatch(getVSPTicketsByFeeStatus(VSP_FEE_PROCESS_PAID));
dispatch({ type: PROCESSMANAGEDTICKETS_SUCCESS });
} catch (error) {
dispatch({ type: PROCESSMANAGEDTICKETS_FAILED, error });
if (
String(error).indexOf(
"wallet.Unlock: invalid passphrase:: secretkey.DeriveKey"
) > 0
) {
throw "Invalid private passphrase, please try again.";
}
throw error;
}
};

export const PROCESSUNMANAGEDTICKETS_ATTEMPT =
"PROCESSUNMANAGEDTICKETS_ATTEMPT";
Expand Down Expand Up @@ -881,3 +878,8 @@ export const saveAutoBuyerSettings = ({ balanceToMaintain, account, vsp }) => (
autobuyerSettings
});
};

export const SET_CANDISABLEPROCESSMANAGED = "SET_CANDISABLEPROCESSMANAGED";
export const setCanDisableProcessManaged = (value) => (dispatch) => {
dispatch({ type: SET_CANDISABLEPROCESSMANAGED, value });
};
29 changes: 20 additions & 9 deletions app/components/views/GetStartedPage/SetupWallet/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
checkAllAccountsEncrypted,
setAccountsPass
} from "actions/ControlActions";
import { getVSPsPubkeys } from "actions/VSPActions";
import {
getVSPsPubkeys,
setCanDisableProcessManaged
} from "actions/VSPActions";
import { ExternalLink } from "shared";
import { DecredLoading } from "indicators";

Expand All @@ -28,7 +31,8 @@ export const useWalletSetup = (settingUpWalletRef) => {
goToHome,
onProcessUnmanagedTickets,
isProcessingUnmanaged,
isProcessingManaged
isProcessingManaged,
needsProcessManagedTickets
} = useDaemonStartup();

const { mixedAccount } = useAccounts();
Expand Down Expand Up @@ -65,13 +69,18 @@ export const useWalletSetup = (settingUpWalletRef) => {
send({ type: "BACK" });
}, [send]);

const onSkipProcessManaged = useCallback(() => {
dispatch(setCanDisableProcessManaged(false));
send({ type: "BACK" });
}, [send, dispatch]);

const getStateComponent = useCallback(async () => {
const { error, isWatchingOnly, isTrezor } = current.context;

let component, hasSoloTickets;

// check if we have live tickets.
const hasLive = Object.keys(stakeTransactions).some((hash) => {
// Check if we have live, vspd-based tickets.
const hasLiveVSPdTickets = Object.keys(stakeTransactions).some((hash) => {
const tx = stakeTransactions[hash];
// check if the wallet has at least one vsp live ticket.
if (
Expand Down Expand Up @@ -157,7 +166,7 @@ export const useWalletSetup = (settingUpWalletRef) => {
break;
case "gettingVSPInfo":
// if no live tickets, we can skip it.
if (!hasLive) {
if (!hasLiveVSPdTickets) {
sendContinue();
} else {
component = h(DecredLoading);
Expand All @@ -167,15 +176,15 @@ export const useWalletSetup = (settingUpWalletRef) => {
break;
case "processingManagedTickets":
// if no live tickets, we can skip it.
if (!hasLive) {
if (!hasLiveVSPdTickets || !needsProcessManagedTickets) {
sendContinue();
} else {
component = h(ProcessManagedTickets, {
error,
onSendContinue: sendContinue,
onSendError,
send,
cancel: onSendBack,
cancel: onSkipProcessManaged,
onProcessTickets: onProcessManagedTickets,
title: (
<T
Expand All @@ -190,7 +199,7 @@ export const useWalletSetup = (settingUpWalletRef) => {
id="getstarted.processManagedTickets.description"
m={`Your wallet appears to have live tickets. Processing managed
tickets confirms with the VSPs that all of your submitted tickets
are currently known and paid for by the VSPs. If you've already
are currently known and paid for by the VSPs. If you've already
confirmed your tickets then you may skip this step.`}
/>
)
Expand Down Expand Up @@ -263,14 +272,16 @@ export const useWalletSetup = (settingUpWalletRef) => {
sendContinue,
isProcessingManaged,
isProcessingUnmanaged,
needsProcessManagedTickets,
onProcessUnmanagedTickets,
onSendBack,
onSendError,
previousState,
current,
onCheckAcctsPass,
onProcessAccounts,
onGetVSPsPubkeys
onGetVSPsPubkeys,
onSkipProcessManaged
]);

return {
Expand Down
7 changes: 6 additions & 1 deletion app/constants/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const DEXWALLET_RPCUSERNAME = "dexwallet_rpcuser";
export const DEXWALLET_RPCPASSWORD = "dexwallet_rpcpass";
export const DEXWALLET_HOSTPORT = "dexwallet_host";
export const BTCWALLET_NAME = "btcwallet_name";
export const NEEDS_VSPD_PROCESS_TICKETS = "needs_vspd_process_tickets";

export const WALLET_INITIAL_VALUE = {
[ENABLE_TICKET_BUYER]: false,
Expand Down Expand Up @@ -124,7 +125,11 @@ export const WALLET_INITIAL_VALUE = {
[AUTOBUYER_SETTINGS]: null,
// STAKEPOOLS is a legacy code which can be deleted after stopping giving
// support for old vsp versions.
[STAKEPOOLS]: []
[STAKEPOOLS]: [],

// Force as true to ensure wallets with tickets prior to when this config was
// introduced trigger a view of the "process managed tickets" page.
[NEEDS_VSPD_PROCESS_TICKETS]: true
};

export const INITIAL_VALUES = {
Expand Down
6 changes: 5 additions & 1 deletion app/hooks/useDaemonStartup.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ const useDaemonStartup = () => {
const stakeTransactions = useSelector(sel.stakeTransactions);
const isProcessingManaged = useSelector(sel.isProcessingManaged);
const isProcessingUnmanaged = useSelector(sel.isProcessingUnmanaged);
const needsProcessManagedTickets = useSelector(
sel.needsProcessManagedTickets
);
// end of vsp selectors

// sync dcrwallet spv or rpc selectors
Expand Down Expand Up @@ -303,7 +306,8 @@ const useDaemonStartup = () => {
stakeTransactions,
rememberedVspHost,
isProcessingManaged,
isProcessingUnmanaged
isProcessingUnmanaged,
needsProcessManagedTickets
};
};

Expand Down

0 comments on commit 53ec152

Please sign in to comment.