Skip to content

Commit

Permalink
TrezorActions: Allow updating firmware.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeGruffins committed Oct 30, 2020
1 parent 106226b commit adb9459
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 78 deletions.
148 changes: 74 additions & 74 deletions app/actions/TrezorActions.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as wallet from "wallet";
import * as selectors from "selectors";
import fs from "fs";
import { hexToBytes, str2utf8hex } from "helpers";
import { rawToHex, hexToBytes, str2utf8hex } from "helpers";
import {
walletTxToBtcjsTx,
walletTxToRefTx,
Expand All @@ -22,6 +22,7 @@ import {
SIGNMESSAGE_SUCCESS
} from "./ControlActions";
import { getAmountFromTxInputs, getTxFromInputs } from "./TransactionActions";
import { ipcRenderer } from "electron";

const session = require("connect").default;
const { TRANSPORT_EVENT, UI, UI_EVENT, DEVICE_EVENT } = require("connect");
Expand All @@ -32,6 +33,8 @@ const AQUIRED = "acquired";
const NOBACKUP = "no-backup";
const TRANSPORT_ERROR = "transport-error";
const TRANSPORT_START = "transport-start";
const BOOTLOADER_MODE = "bootloader";
const DISCONNECTED_DURING_ACTION = "device disconnected during action";

let setListeners = false;

Expand All @@ -56,7 +59,7 @@ export const enableTrezor = () => (dispatch, getState) => {
connect()(dispatch, getState);
};

async function initTransport(dispatch, debug) {
export const initTransport = async (session, debug) => {
await session.init({
connectSrc: "https://localhost:8088/",
env: "web",
Expand All @@ -71,7 +74,7 @@ async function initTransport(dispatch, debug) {
.catch(err => {
throw err;
});
}
};

export const TRZ_CONNECT_ATTEMPT = "TRZ_CONNECT_ATTEMPT";
export const TRZ_CONNECT_FAILED = "TRZ_CONNECT_FAILED";
Expand All @@ -87,7 +90,7 @@ export const connect = () => async (dispatch, getState) => {
wallet.allowExternalRequest(EXTERNALREQUEST_TREZOR_BRIDGE);

const debug = getState().trezor.debug;
await initTransport(dispatch, debug)
await initTransport(session, debug)
.catch(error => {
dispatch({ error, type: TRZ_CONNECT_FAILED });
return;
Expand All @@ -111,20 +114,26 @@ export const TRZ_SELECTEDDEVICE_CHANGED = "TRZ_SELECTEDDEVICE_CHANGED";
export const TRZ_NOCONNECTEDDEVICE = "TRZ_NOCONNECTEDDEVICE";

function onChange (dispatch, getState, features) {
if (features == null) throw "no features on change";
if (features == null) return;
const currentDevice = selectors.trezorDevice(getState());
const device = features.id;
// 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 });
};

function onConnect (dispatch, getState, features) {
if (features == null) throw "no features on connect";
const device = features.id;
if (features == null) return;
let device = features.id;
const deviceLabel = features.label;
if (features.mode == BOOTLOADER_MODE) {
device = BOOTLOADER_MODE;
}
dispatch({ deviceLabel, device, type: TRZ_LOADDEVICE });
return device;
};
Expand Down Expand Up @@ -175,11 +184,12 @@ function setDeviceListeners(dispatch, getState) {
break;
};
});

session.on(DEVICE_EVENT, (event) => {
const type = event.type;
switch (type) {
case CHANGE:
if (event.payload.type == AQUIRED) {
if (event.payload && event.payload.type == AQUIRED) {
onChange(dispatch, getState, event.payload);
}
break;
Expand All @@ -191,6 +201,7 @@ function setDeviceListeners(dispatch, getState) {
break;
};
});

// TODO: Trezor needs some time to start listening for the responses to its
// requests. Find a better way than static sleeps to accomplish this.
session.on(UI_EVENT, async (event) => {
Expand All @@ -207,49 +218,49 @@ function setDeviceListeners(dispatch, getState) {
payload: false
});
};
dispatch({ type: TRZ_NOTBACKEDUP });
break;
dispatch({ type: TRZ_NOTBACKEDUP });
break;
case UI.REQUEST_PASSPHRASE: {
console.log("passphrase requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const passPhraseCallBack = (canceled, passphrase) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_PASSPHRASE,
payload: {
value: passphrase,
save: true
}
});
};
dispatch({ passPhraseCallBack, type: TRZ_PASSPHRASE_REQUESTED });
break;
console.log("passphrase requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const passPhraseCallBack = (canceled, passphrase) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_PASSPHRASE,
payload: {
value: passphrase,
save: true
}
});
};
dispatch({ passPhraseCallBack, type: TRZ_PASSPHRASE_REQUESTED });
break;
}
case UI.REQUEST_PIN: {
console.log("pin requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const pinCallBack = (canceled, pin) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_PIN,
payload: pin
});
};
dispatch({ pinCallBack, type: TRZ_PIN_REQUESTED });
break;
console.log("pin requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const pinCallBack = (canceled, pin) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_PIN,
payload: pin
});
};
dispatch({ pinCallBack, type: TRZ_PIN_REQUESTED });
break;
}
case UI.REQUEST_WORD: {
console.log("word requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const wordCallBack = (canceled, word) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_WORD,
payload: word
});
};
dispatch({ wordCallBack, type: TRZ_WORD_REQUESTED });
break;
console.log("word requested, waiting two seconds to respond");
await new Promise(r => setTimeout(r, 2000));
const wordCallBack = (canceled, word) => {
if (canceled) return;
session.uiResponse({
type: UI.RECEIVE_WORD,
payload: word
});
};
dispatch({ wordCallBack, type: TRZ_WORD_REQUESTED });
break;
}
}
});
Expand All @@ -265,9 +276,8 @@ async function deviceRun(dispatch, getState, fn) {
if (noDevice(getState)) throw "no trezor device";
const handleError = (error) => {
const {
trezor: { waitingForPin, waitingForPassphrase, debug }
trezor: { waitingForPin, waitingForPassphrase }
} = getState();
debug && console.log("Handle error no deviceRun", error);
if (waitingForPin) dispatch({ error, type: TRZ_PIN_CANCELED });
if (waitingForPassphrase)
dispatch({ error, type: TRZ_PASSPHRASE_CANCELED });
Expand All @@ -280,21 +290,11 @@ async function deviceRun(dispatch, getState, fn) {
};

try {
const waitFor = async () => {
try {
return await fn();
} catch (err) {
// doesn't seem to be reachable by trezor interruptions, but might be
// caused by fn() failing in some other way (even though it's
// recommended not to do (non-trezor) lengthy operations inside fn())
throw handleError(err);
}
};
const res = await waitFor();
const res = await fn();
if (res && res.error) throw handleError(res.error);
return res;
} catch (outerErr) {
throw handleError(outerErr);
} catch (error) {
throw handleError(error);
}
};

Expand Down Expand Up @@ -768,12 +768,15 @@ export const TRZ_UPDATEFIRMWARE_FAILED = "TRZ_UPDATEFIRMWARE_FAILED";
export const TRZ_UPDATEFIRMWARE_SUCCESS = "TRZ_UPDATEFIRMWARE_SUCCESS";

export const updateFirmware = (path) => async (dispatch, getState) => {
const {
trezor: { performingUpdate }
} = getState();
if (performingUpdate) {
console.log("already updating firmware");
return;
}

dispatch({ type: TRZ_UPDATEFIRMWARE_ATTEMPT });
// TODO: Allow firmware installation.
dispatch({ error: "firware install currently disabled", type: TRZ_UPDATEFIRMWARE_FAILED });
// Strange var to fool linter.
const abort = true;
if (abort) return;

if (noDevice(getState)) {
dispatch({
Expand All @@ -785,13 +788,10 @@ export const updateFirmware = (path) => async (dispatch, getState) => {

try {
const rawFirmware = fs.readFileSync(path);

await deviceRun(dispatch, getState, async () => {
const res = await session.firmwareUpdate({
binary: rawFirmware.buffer
});
return res.payload;
});
const hexFirmware = rawToHex(rawFirmware);
const errorStr = await ipcRenderer.invoke("upload-firmware", hexFirmware);
// A successful upload will end with the user unplugging the device.
if (errorStr != DISCONNECTED_DURING_ACTION) throw errorStr;
dispatch({ type: TRZ_UPDATEFIRMWARE_SUCCESS });
} catch (error) {
dispatch({ error, type: TRZ_UPDATEFIRMWARE_FAILED });
Expand Down
8 changes: 7 additions & 1 deletion app/main.development.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ import {
startDcrlnd,
stopDcrlnd,
removeDcrlnd,
lnScbInfo
lnScbInfo,
updateTrezorFirmware
} from "./main_dev/ipc";
import {
initTemplate,
Expand Down Expand Up @@ -298,6 +299,11 @@ ipcMain.on("remove-wallet", (event, walletPath, testnet) => {
event.returnValue = removeWallet(testnet, walletPath);
});

ipcMain.handle("upload-firmware", async (event, firmware) => {
const res = await updateTrezorFirmware(firmware);
return res;
});

ipcMain.on("stop-daemon", (event) => {
event.returnValue = stopDaemon();
});
Expand Down
21 changes: 21 additions & 0 deletions app/main_dev/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
setDcrdRpcCredentials
} from "./launch";
import { MAINNET } from "constants";
import { initTransport } from "actions/TrezorActions.js";
import * as connect from "connect";

const logger = createLogger();
let watchingOnlyWallet;
Expand Down Expand Up @@ -141,6 +143,25 @@ export const removeWallet = (testnet, walletPath) => {
}
};

export const updateTrezorFirmware = async ( firmware ) => {
let session = connect.default;
try {
await initTransport(session, false);
const res = await session.firmwareUpdate({
binary: firmware
});
if (res.payload && res.payload.error) {
throw res.payload.error;
}
return null;
} catch (e) {
logger.log("error", "error uploading trezor firmware: " + e);
return e.toString();
} finally {
session = null;
}
};

export const startWallet = (
mainWindow,
daemonIsAdvanced,
Expand Down
16 changes: 13 additions & 3 deletions app/reducers/trezor.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,13 @@ export default function trezor(state = {}, action) {
case TRZ_WIPEDEVICE_ATTEMPT:
case TRZ_RECOVERDEVICE_ATTEMPT:
case TRZ_INITDEVICE_ATTEMPT:
case TRZ_UPDATEFIRMWARE_ATTEMPT:
return { ...state, performingOperation: true };
case TRZ_UPDATEFIRMWARE_ATTEMPT:
return {
...state,
performingOperation: true,
performingUpdate: true
};
case TRZ_CHANGELABEL_SUCCESS:
return {
...state,
Expand All @@ -203,11 +208,16 @@ export default function trezor(state = {}, action) {
case TRZ_RECOVERDEVICE_SUCCESS:
case TRZ_INITDEVICE_FAILED:
case TRZ_INITDEVICE_SUCCESS:
case TRZ_UPDATEFIRMWARE_FAILED:
case TRZ_UPDATEFIRMWARE_SUCCESS:
case TRZ_BACKUPDEVICE_FAILED:
case TRZ_BACKUPDEVICE_SUCCESS:
return { ...state, performingOperation: false };
case TRZ_UPDATEFIRMWARE_FAILED:
case TRZ_UPDATEFIRMWARE_SUCCESS:
return {
...state,
performingOperation: false,
performingUpdate: false
};
case CLOSEWALLET_SUCCESS:
return { ...state, enabled: false };
default:
Expand Down

0 comments on commit adb9459

Please sign in to comment.