Skip to content

Commit

Permalink
trezor: Add udev error and instructions.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeGruffins committed May 28, 2024
1 parent 11e55dd commit f13b15c
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 28 deletions.
5 changes: 4 additions & 1 deletion app/actions/LNActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,10 @@ const connectToLNWallet =
} catch (error) {
// An unimplemented error here probably means dcrlnd was just unlocked
// and is currently starting up the services. Wait a bit and try again.
if (error.code !== 12 && error.toString().indexOf("in the process of starting up") == -1) {
if (
error.code !== 12 &&
error.toString().indexOf("in the process of starting up") == -1
) {
// 12 === UNIMPLEMENTED.
throw error;
}
Expand Down
59 changes: 38 additions & 21 deletions app/actions/TrezorActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
SIGNMESSAGE_SUCCESS
} from "./ControlActions";
import { getAmountFromTxInputs, getTxFromInputs } from "./TransactionActions";
import { push as pushHistory } from "connected-react-router";

const session = require("trezor-connect").default;
const {
Expand All @@ -45,6 +46,7 @@ let setListeners = false;

export const TRZ_WALLET_CLOSED = "TRZ_WALLET_CLOSED";
export const TRZ_TREZOR_ENABLED = "TRZ_TREZOR_ENABLED";
export const TRZ_TREZORD_STARTED = "TRZ_TREZORD_STARTED";

// enableTrezor attepts to start a connection with connect if none exist and
// connect to a trezor device.
Expand All @@ -55,7 +57,11 @@ export const enableTrezor = () => async (dispatch, getState) => {
setDeviceListeners(dispatch, getState);
setListeners = true;
}
await trezord.start();
const trezordStarted = getState().trezor.trezordStarted;
if (!trezordStarted) {
await trezord.start();
dispatch({ type: TRZ_TREZORD_STARTED });
}
connect()(dispatch, getState);
};

Expand All @@ -82,23 +88,42 @@ export const initTransport = async (session, debug) => {
export const TRZ_CONNECT_ATTEMPT = "TRZ_CONNECT_ATTEMPT";
export const TRZ_CONNECT_FAILED = "TRZ_CONNECT_FAILED";
export const TRZ_CONNECT_SUCCESS = "TRZ_CONNECT_SUCCESS";
export const TRZ_UDEV_ERROR = "TREZOR_UDEV_ERROR";

export const connect = () => async (dispatch, getState) => {
const {
trezor: { connected, connectAttempt }
trezor: { connected, connectAttempt, initted }
} = getState();
if (connected || connectAttempt) return;
dispatch({ type: TRZ_CONNECT_ATTEMPT });

wallet.allowExternalRequest(EXTERNALREQUEST_TREZOR_BRIDGE);

const debug = getState().trezor.debug;
await initTransport(session, debug).catch((error) => {
dispatch({ error, type: TRZ_CONNECT_FAILED });
// We can only ever init transport once.
if (!initted) {
const debug = getState().trezor.debug;
await initTransport(session, debug).catch((error) => {
dispatch({ error, type: TRZ_CONNECT_FAILED });
return;
});
}
try {
await dispatch(getFeatures());
} catch (err) {
dispatch({
error: err.message,
type: TRZ_CONNECT_FAILED
});
const needRules = await trezord.needsUdevRules();
if (needRules.needs) {
dispatch({ type: TRZ_UDEV_ERROR });
setTimeout(() => {
dispatch(pushHistory("/error"));
}, 1000);
}
return;
});
}
dispatch({ type: TRZ_CONNECT_SUCCESS });
dispatch(getFeatures());
};

export const TRZ_TREZOR_DISABLED = "TRZ_TREZOR_DISABLED";
Expand All @@ -120,16 +145,13 @@ export const TRZ_NOCONNECTEDDEVICE = "TRZ_NOCONNECTEDDEVICE";
function onChange(dispatch, getState, features) {
if (features == null) throw "no features on change";
const currentDevice = selectors.trezorDevice(getState());
// No current device handle by connect.
if (!currentDevice) return;
let device = features.id;
if (features.mode == BOOTLOADER_MODE) {
device = BOOTLOADER_MODE;
}
if (device == currentDevice) return;
const deviceLabel = features.label;
dispatch({ deviceLabel, device, type: TRZ_SELECTEDDEVICE_CHANGED });
dispatch(getFeatures());
}

function onConnect(dispatch, getState, features) {
Expand All @@ -140,7 +162,6 @@ function onConnect(dispatch, getState, features) {
device = BOOTLOADER_MODE;
}
dispatch({ deviceLabel, device, type: TRZ_LOADDEVICE });
dispatch(getFeatures());
return device;
}

Expand Down Expand Up @@ -330,16 +351,12 @@ async function deviceRun(dispatch, getState, fn) {

export const TRZ_GETFEATURES_SUCCESS = "TRZ_GETFEATURES_SUCCESS";
export const getFeatures = () => async (dispatch, getState) => {
try {
const features = await deviceRun(dispatch, getState, async () => {
const res = await session.getFeatures();
return res.payload;
});
dispatch({ type: TRZ_GETFEATURES_SUCCESS, features });
return features;
} catch (error) {
return null;
}
const features = await deviceRun(dispatch, getState, async () => {
const res = await session.getFeatures();
return res.payload;
});
dispatch({ type: TRZ_GETFEATURES_SUCCESS, features });
return features;
};

export const TRZ_CANCELOPERATION_SUCCESS = "TRZ_CANCELOPERATION_SUCCESS";
Expand Down
25 changes: 25 additions & 0 deletions app/components/views/FatalErrorPage/FatalErrorPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function FatalErrorPage() {
const {
daemonError,
walletError,
trezorUdevError,
isAdvancedDaemon,
shutdownApp,
backToCredentials,
Expand Down Expand Up @@ -125,6 +126,17 @@ function FatalErrorPage() {
<textarea rows="10" value={walletError} disabled />
</>
)}
{trezorUdevError && (
<>
<div className={styles.error}>
<T id="fatal.trezor.udev.title" m="Udev Rules Error" />
</div>
<T
id="fatal.trezor.udev.details"
m="Udev rules were not found at /etc/udev/rules.d/51-trezor.rules"
/>
</>
)}
</div>
</div>
<div className={styles.title}>
Expand All @@ -136,6 +148,19 @@ function FatalErrorPage() {
</div>
<div className={styles.suggestion}>
{daemonError && getErrorAction()}
{trezorUdevError && (
<T
id="fatal.trezor.suggestion.udev"
m="Follow the instuctions at {link}."
values={{
link: (
<ExternalLink href="https://trezor.io/learn/a/udev-rules">
https://trezor.io/learn/a/udev-rules
</ExternalLink>
)
}}
/>
)}
</div>
<div className={styles.toolbar}>
{isAdvancedDaemon && (
Expand Down
4 changes: 3 additions & 1 deletion app/components/views/FatalErrorPage/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function useFatalErrorPage() {
const isAdvancedDaemon = useSelector(sel.isAdvancedDaemon);
const daemonError = useSelector(sel.daemonError);
const walletError = useSelector(sel.walletError);
const trezorUdevError = useSelector(sel.trezorUdevError);

const shutdownApp = () => dispatch(da.shutdownApp());
const backToCredentials = () => dispatch(da.backToCredentials());
Expand All @@ -18,6 +19,7 @@ export function useFatalErrorPage() {
isAdvancedDaemon,
shutdownApp,
backToCredentials,
deleteDaemonData
deleteDaemonData,
trezorUdevError
};
}
3 changes: 3 additions & 0 deletions app/main.development.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import {
removeDcrlnd,
startTrezord,
stopTrezord,
needUdevRules,
lnScbInfo,
startDex,
stopDex,
Expand Down Expand Up @@ -412,6 +413,8 @@ handle("start-dex", startDex);

handle("stop-dex", stopDex);

handle("needs-udev-rules", needUdevRules);

handle("start-trezord", startTrezord);

handle("stop-trezord", stopTrezord);
Expand Down
5 changes: 5 additions & 0 deletions app/main_dev/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
launchTrezord,
GetTrezordPID,
closeTrezord,
needsUdevRules,
launchDex,
initCheckDex,
initDexCall,
Expand Down Expand Up @@ -251,6 +252,10 @@ export const startDcrlnd = async (
}
};

export const needUdevRules = () => {
return { needs: needsUdevRules() };
};

export const startTrezord = async () => {
if (GetTrezordPID() && GetTrezordPID() !== -1) {
logger.log(
Expand Down
11 changes: 11 additions & 0 deletions app/main_dev/launch.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,17 @@ export const closeDcrlnd = () => {
return true;
};

export function needsUdevRules() {
if (os.platform() != "linux") {
return false;
}
const distRules = "/etc/udev/rules.d/51-trezor.rules";
if (fs.existsSync(distRules)) {
return false;
}
return true;
}

export function closeTrezord() {
if (trezordPID === -1) {
// process is not started by decrediton
Expand Down
20 changes: 15 additions & 5 deletions app/reducers/trezor.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ import {
TRZ_INITDEVICE_ATTEMPT,
TRZ_INITDEVICE_FAILED,
TRZ_INITDEVICE_SUCCESS,
TRZ_TREZORD_STARTED,
TRZ_UPDATEFIRMWARE_ATTEMPT,
TRZ_UPDATEFIRMWARE_FAILED,
TRZ_UPDATEFIRMWARE_SUCCESS,
TRZ_GETWALLETCREATIONMASTERPUBKEY_ATTEMPT,
TRZ_GETWALLETCREATIONMASTERPUBKEY_FAILED,
TRZ_GETWALLETCREATIONMASTERPUBKEY_SUCCESS,
TRZ_GETFEATURES_SUCCESS
TRZ_GETFEATURES_SUCCESS,
TRZ_UDEV_ERROR
} from "actions/TrezorActions";
import {
SIGNTX_ATTEMPT,
Expand All @@ -77,7 +79,8 @@ export default function trezor(state = {}, action) {
case TRZ_CONNECT_SUCCESS:
return {
...state,
connectAttempt: false
connectAttempt: false,
udevError: false
};
case TRZ_CONNECT_FAILED:
return {
Expand All @@ -89,6 +92,7 @@ export default function trezor(state = {}, action) {
return {
...state,
transportError: false,
initted: true,
connected: true
};
case TRZ_DEVICETRANSPORT_LOST:
Expand All @@ -97,6 +101,7 @@ export default function trezor(state = {}, action) {
transportError: action.error,
device: null,
deviceLabel: "",
initted: false,
pinProtection: undefined,
passphraseProtection: undefined,
passphraseOnDeviceProtection: undefined,
Expand All @@ -120,8 +125,6 @@ export default function trezor(state = {}, action) {
case TRZ_WALLET_CLOSED:
return {
...state,
device: null,
deviceLabel: "",
pinProtection: undefined,
passphraseProtection: undefined,
passphraseOnDeviceProtection: undefined,
Expand Down Expand Up @@ -222,6 +225,8 @@ export default function trezor(state = {}, action) {
case TRZ_WIPEDEVICE_ATTEMPT:
case TRZ_INITDEVICE_ATTEMPT:
return { ...state, performingOperation: true };
case TRZ_TREZORD_STARTED:
return { ...state, trezordStarted: true };
case TRZ_TOGGLEPINPROTECTION_ATTEMPT:
return {
...state,
Expand Down Expand Up @@ -326,7 +331,12 @@ export default function trezor(state = {}, action) {
performingTogglePassphraseOnDeviceProtection: false
};
case CLOSEWALLET_SUCCESS:
return { ...state, enabled: false };
return { ...state };
case TRZ_UDEV_ERROR:
return {
...state,
udevError: true
};
default:
return state;
}
Expand Down
1 change: 1 addition & 0 deletions app/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,7 @@ export const confirmationDialogModalVisible = bool(

export const isTrezor = get(["trezor", "enabled"]);
export const isPerformingTrezorUpdate = get(["trezor", "performingUpdate"]);
export const trezorUdevError = get(["trezor", "udevError"]);

export const isLedger = get(["ledger", "enabled"]);

Expand Down
1 change: 1 addition & 0 deletions app/wallet/trezord/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { invocable } from "helpers/electronRenderer";

export const needsUdevRules = invocable("needs-udev-rules");
export const start = invocable("start-trezord");
export const stop = invocable("stop-trezord");

0 comments on commit f13b15c

Please sign in to comment.