mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-02-03 14:54:17 +00:00
Switch over to using electron protocol, remove code thats no longer necessary
This commit is contained in:
parent
82302b92f6
commit
4c7c6d0a3b
@ -8,7 +8,7 @@ import { render } from 'react-dom';
|
|||||||
import Root from './Root';
|
import Root from './Root';
|
||||||
import { configuredStore } from './store';
|
import { configuredStore } from './store';
|
||||||
import consoleAdvertisement from './utils/consoleAdvertisement';
|
import consoleAdvertisement from './utils/consoleAdvertisement';
|
||||||
import EnclaveAPI, { registerClient } from 'shared/enclave/client';
|
import EnclaveAPI from 'shared/enclave/client';
|
||||||
|
|
||||||
const appEl = document.getElementById('app');
|
const appEl = document.getElementById('app');
|
||||||
|
|
||||||
@ -27,6 +27,5 @@ if (process.env.NODE_ENV === 'production') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.BUILD_ELECTRON) {
|
if (process.env.BUILD_ELECTRON) {
|
||||||
registerClient();
|
|
||||||
(window as any).EnclaveAPI = EnclaveAPI;
|
(window as any).EnclaveAPI = EnclaveAPI;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { app } from 'electron';
|
import { app } from 'electron';
|
||||||
|
import { registerServer } from 'shared/enclave/server';
|
||||||
import getWindow from './window';
|
import getWindow from './window';
|
||||||
|
|
||||||
// Quit application when all windows are closed
|
// Quit application when all windows are closed
|
||||||
@ -20,3 +21,6 @@ app.on('activate', () => {
|
|||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
getWindow();
|
getWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register enclave protocol
|
||||||
|
registerServer(app);
|
||||||
|
@ -4,7 +4,6 @@ import path from 'path';
|
|||||||
import MENU from './menu';
|
import MENU from './menu';
|
||||||
import updater from './updater';
|
import updater from './updater';
|
||||||
import { APP_TITLE } from '../constants';
|
import { APP_TITLE } from '../constants';
|
||||||
import ledger from 'shared/enclave/server/wallets/ledger';
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
// Cached reference, preventing recreations
|
// Cached reference, preventing recreations
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
import { registerServer } from 'shared/enclave/server';
|
|
||||||
registerServer();
|
|
@ -1,2 +1,2 @@
|
|||||||
import { registerServer } from 'shared/enclave/server';
|
import { setupClient } from 'shared/enclave/client/preload';
|
||||||
registerServer();
|
setupClient();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { listenForResponses, makeRequestExpectingResponse } from './requests';
|
import { makeRequest } from './requests';
|
||||||
import {
|
import {
|
||||||
EnclaveEvents,
|
EnclaveMethods,
|
||||||
GetAddressesParams,
|
GetAddressesParams,
|
||||||
GetAddressesResponse,
|
GetAddressesResponse,
|
||||||
GetChainCodeParams,
|
GetChainCodeParams,
|
||||||
@ -9,24 +9,17 @@ import {
|
|||||||
SignTransactionResponse
|
SignTransactionResponse
|
||||||
} from 'shared/enclave/types';
|
} from 'shared/enclave/types';
|
||||||
|
|
||||||
export function registerClient() {
|
|
||||||
listenForResponses();
|
|
||||||
}
|
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
getAddresses(params: GetAddressesParams) {
|
getAddresses(params: GetAddressesParams) {
|
||||||
return makeRequestExpectingResponse<GetAddressesResponse>(EnclaveEvents.GET_ADDRESSES, params);
|
return makeRequest<GetAddressesResponse>(EnclaveMethods.GET_ADDRESSES, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
getChainCode(params: GetChainCodeParams) {
|
getChainCode(params: GetChainCodeParams) {
|
||||||
return makeRequestExpectingResponse<GetChainCodeResponse>(EnclaveEvents.GET_CHAIN_CODE, params);
|
return makeRequest<GetChainCodeResponse>(EnclaveMethods.GET_CHAIN_CODE, params);
|
||||||
},
|
},
|
||||||
|
|
||||||
signTransaction(params: SignTransactionParams) {
|
signTransaction(params: SignTransactionParams) {
|
||||||
return makeRequestExpectingResponse<SignTransactionResponse>(
|
return makeRequest<SignTransactionResponse>(EnclaveMethods.SIGN_TRANSACTION, params);
|
||||||
EnclaveEvents.SIGN_TRANSACTION,
|
|
||||||
params
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
6
shared/enclave/client/preload.ts
Normal file
6
shared/enclave/client/preload.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { webFrame } from 'electron';
|
||||||
|
import { PROTOCOL_NAME } from 'shared/enclave/utils';
|
||||||
|
|
||||||
|
export function setupClient() {
|
||||||
|
webFrame.registerURLSchemeAsPrivileged(PROTOCOL_NAME);
|
||||||
|
}
|
@ -1,67 +1,19 @@
|
|||||||
import EventEmitter from 'events';
|
import { EnclaveMethods, EnclaveMethodParams, EnclaveResponse } from 'shared/enclave/types';
|
||||||
import { EnclaveEvents, EventParams, RpcEvent, RpcRequest } from 'shared/enclave/types';
|
import { PROTOCOL_NAME } from 'shared/enclave/utils';
|
||||||
import { idGeneratorFactory } from 'shared/enclave/utils';
|
|
||||||
|
|
||||||
let isListening = false;
|
export function makeRequest<T>(type: EnclaveMethods, params: EnclaveMethodParams): Promise<T> {
|
||||||
const ee = new EventEmitter();
|
return fetch(`${PROTOCOL_NAME}://${type}`, {
|
||||||
const genId = idGeneratorFactory();
|
method: 'POST',
|
||||||
const resEventPrefix = 'enclave-response:';
|
body: JSON.stringify(params)
|
||||||
|
})
|
||||||
export function listenForResponses() {
|
.then(res => res.json())
|
||||||
if (isListening) {
|
.then((res: EnclaveResponse<T>) => {
|
||||||
return;
|
if (res.data) {
|
||||||
}
|
return res.data;
|
||||||
isListening = true;
|
} else if (res.error) {
|
||||||
|
throw new Error(res.error.message);
|
||||||
window.addEventListener('message', ev => {
|
} else {
|
||||||
// Only take in messages from preload
|
throw new Error('Unknown response from server');
|
||||||
if (ev.origin !== window.location.origin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const response = JSON.parse(ev.data);
|
|
||||||
if (response && response.isResponse && response.id) {
|
|
||||||
ee.emit(`${resEventPrefix}${response.id}`, response as RpcEvent);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// no-op, not meant for us
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeRequest(type: EnclaveEvents, params: EventParams): RpcRequest {
|
|
||||||
const id = genId();
|
|
||||||
const req: RpcRequest = {
|
|
||||||
id,
|
|
||||||
type,
|
|
||||||
params,
|
|
||||||
isRequest: true
|
|
||||||
};
|
|
||||||
window.postMessage(JSON.stringify(req), window.location.origin);
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeRequestExpectingResponse<T>(
|
|
||||||
type: EnclaveEvents,
|
|
||||||
params: EventParams,
|
|
||||||
timeout: number = 10000
|
|
||||||
): Promise<T> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const req = makeRequest(type, params);
|
|
||||||
const eventName = `${resEventPrefix}${req.id}`;
|
|
||||||
console.log('Listening for', eventName);
|
|
||||||
|
|
||||||
ee.once(eventName, (res: RpcEvent<T>) => {
|
|
||||||
if (res.payload) {
|
|
||||||
resolve(res.payload);
|
|
||||||
} else if (res.errMsg) {
|
|
||||||
reject(new Error(res.errMsg));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
ee.removeAllListeners(eventName);
|
|
||||||
reject(new Error('Request timed out'));
|
|
||||||
}, timeout);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import getAddresses from './getAddresses';
|
import getAddresses from './getAddresses';
|
||||||
import getChainCode from './getChainCode';
|
import getChainCode from './getChainCode';
|
||||||
import signTransaction from './signTransaction';
|
import signTransaction from './signTransaction';
|
||||||
import { EnclaveEvents, EventParams, EventResponse } from 'shared/enclave/types';
|
import { EnclaveMethods, EnclaveMethodParams, EnclaveMethodResponse } from 'shared/enclave/types';
|
||||||
|
|
||||||
const handlers: {
|
const handlers: {
|
||||||
[key in EnclaveEvents]: (params: EventParams) => EventResponse | Promise<EventResponse>
|
[key in EnclaveMethods]: (
|
||||||
|
params: EnclaveMethodParams
|
||||||
|
) => EnclaveMethodResponse | Promise<EnclaveMethodResponse>
|
||||||
} = {
|
} = {
|
||||||
[EnclaveEvents.GET_ADDRESSES]: getAddresses,
|
[EnclaveMethods.GET_ADDRESSES]: getAddresses,
|
||||||
[EnclaveEvents.GET_CHAIN_CODE]: getChainCode,
|
[EnclaveMethods.GET_CHAIN_CODE]: getChainCode,
|
||||||
[EnclaveEvents.SIGN_TRANSACTION]: signTransaction
|
[EnclaveMethods.SIGN_TRANSACTION]: signTransaction
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handlers;
|
export default handlers;
|
||||||
|
@ -1,53 +1,73 @@
|
|||||||
|
import { protocol, App } from 'electron';
|
||||||
import handlers from './handlers';
|
import handlers from './handlers';
|
||||||
import { isValidEventType } from 'shared/enclave/utils';
|
import { PROTOCOL_NAME, isValidEventType } from 'shared/enclave/utils';
|
||||||
import { RpcRequest, RpcEventSuccess, RpcEventFailure, EventResponse } from 'shared/enclave/types';
|
import { EnclaveMethods, EnclaveMethodParams } from 'shared/enclave/types';
|
||||||
|
|
||||||
|
export function registerServer(app: App) {
|
||||||
|
protocol.registerStandardSchemes([PROTOCOL_NAME]);
|
||||||
|
|
||||||
|
app.on('ready', () => {
|
||||||
|
protocol.registerStringProtocol(PROTOCOL_NAME, async (req, cb) => {
|
||||||
|
try {
|
||||||
|
const method = getMethod(req);
|
||||||
|
const params = getParams(method, req);
|
||||||
|
const response = await processRequest(method, params);
|
||||||
|
return cb(JSON.stringify({ data: response }));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Request to '${req.url}' failed with error:`, err);
|
||||||
|
return cb(
|
||||||
|
JSON.stringify({
|
||||||
|
code: 500,
|
||||||
|
type: err.name,
|
||||||
|
message: err.message
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMethod(req: Electron.RegisterStringProtocolRequest): EnclaveMethods {
|
||||||
|
const urlSplit = req.url.split(`${PROTOCOL_NAME}://`);
|
||||||
|
|
||||||
|
if (!urlSplit[1]) {
|
||||||
|
throw new Error('No method provided');
|
||||||
|
}
|
||||||
|
|
||||||
|
const method = urlSplit[1].replace('/', '');
|
||||||
|
if (!isValidEventType(method)) {
|
||||||
|
throw new Error(`Invalid or unknown method '${method}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return method as EnclaveMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getParams(
|
||||||
|
method: EnclaveMethods,
|
||||||
|
req: Electron.RegisterStringProtocolRequest
|
||||||
|
): EnclaveMethodParams {
|
||||||
|
const data = req.uploadData.find(d => !!d.bytes);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
throw new Error(`No data provided for '${method}'`);
|
||||||
|
}
|
||||||
|
|
||||||
async function processRequest(req: RpcRequest) {
|
|
||||||
try {
|
try {
|
||||||
const data = await handlers[req.type](req.params);
|
// TODO: Validate params based on provided method
|
||||||
if (data) {
|
const params = JSON.parse(data.bytes.toString());
|
||||||
respondWithPayload(req, data);
|
return params.params as EnclaveMethodParams;
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Request', req.type, 'failed with error:', err);
|
throw new Error(`Invalid JSON blob provided for '${method}'`);
|
||||||
respondWithError(req, err.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function respondWithPayload(req: RpcRequest, payload: EventResponse) {
|
async function processRequest(method: EnclaveMethods, params: EnclaveMethodParams) {
|
||||||
const response: RpcEventSuccess = {
|
try {
|
||||||
id: req.id,
|
const data = await handlers[method](params);
|
||||||
isResponse: true,
|
if (data) {
|
||||||
errMsg: undefined,
|
return data;
|
||||||
payload
|
|
||||||
};
|
|
||||||
window.postMessage(JSON.stringify(response), window.location.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
function respondWithError(req: RpcRequest, errMsg: string) {
|
|
||||||
const response: RpcEventFailure = {
|
|
||||||
id: req.id,
|
|
||||||
isResponse: true,
|
|
||||||
payload: undefined,
|
|
||||||
errMsg
|
|
||||||
};
|
|
||||||
window.postMessage(JSON.stringify(response), window.location.origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function registerServer() {
|
|
||||||
window.addEventListener('message', (ev: MessageEvent) => {
|
|
||||||
// Only take in messages from the webview
|
|
||||||
if (ev.origin !== window.location.origin) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
try {
|
throw new Error(err);
|
||||||
const request = JSON.parse(ev.data);
|
}
|
||||||
if (request && request.isRequest && isValidEventType(request.type)) {
|
|
||||||
processRequest(request as RpcRequest);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// no-op, not meant for us
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Enclave enums
|
// Enclave enums
|
||||||
export enum EnclaveEvents {
|
export enum EnclaveMethods {
|
||||||
GET_ADDRESSES = 'get-addresses',
|
GET_ADDRESSES = 'get-addresses',
|
||||||
GET_CHAIN_CODE = 'get-chain-code',
|
GET_CHAIN_CODE = 'get-chain-code',
|
||||||
SIGN_TRANSACTION = 'sign-transaction'
|
SIGN_TRANSACTION = 'sign-transaction'
|
||||||
@ -44,32 +44,30 @@ export interface SignTransactionResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All Requests & Responses
|
// All Requests & Responses
|
||||||
export type EventParams = GetAddressesParams | GetChainCodeParams | SignTransactionParams;
|
export type EnclaveMethodParams = GetAddressesParams | GetChainCodeParams | SignTransactionParams;
|
||||||
export type EventResponse = GetAddressesResponse | GetChainCodeResponse | SignTransactionResponse;
|
export type EnclaveMethodResponse =
|
||||||
|
| GetAddressesResponse
|
||||||
|
| GetChainCodeResponse
|
||||||
|
| SignTransactionResponse;
|
||||||
|
|
||||||
// RPC requests, responses & failures
|
// RPC requests, responses & failures
|
||||||
export interface RpcRequest {
|
export interface EnclaveSuccessResponse<T = EnclaveMethodResponse> {
|
||||||
isRequest: true;
|
data: T;
|
||||||
params: EventParams;
|
error?: undefined;
|
||||||
type: EnclaveEvents;
|
|
||||||
id: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RpcEventSuccess<T = any> {
|
export interface EnclaveErrorResponse {
|
||||||
isResponse: true;
|
data?: undefined;
|
||||||
payload: T;
|
error: {
|
||||||
errMsg: undefined;
|
code: number;
|
||||||
id: number;
|
type: string;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RpcEventFailure {
|
export type EnclaveResponse<T = EnclaveMethodResponse> =
|
||||||
isResponse: true;
|
| EnclaveSuccessResponse<T>
|
||||||
errMsg: string;
|
| EnclaveErrorResponse;
|
||||||
payload: undefined;
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RpcEvent<T = any> = RpcEventFailure | RpcEventSuccess<T>;
|
|
||||||
|
|
||||||
// Wallet lib
|
// Wallet lib
|
||||||
export interface WalletLib {
|
export interface WalletLib {
|
||||||
|
@ -1,37 +1,6 @@
|
|||||||
import { EnclaveEvents, RpcEventHandler } from './types';
|
import { EnclaveMethods } from './types';
|
||||||
|
|
||||||
export const idGeneratorFactory = () => {
|
export const PROTOCOL_NAME = 'eth-enclave';
|
||||||
let callId = 0;
|
|
||||||
return () => {
|
|
||||||
const currValue = callId;
|
|
||||||
callId += 1;
|
|
||||||
return currValue;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createRpcRequestedEv = (e: EnclaveEvents) => `${e}-requested`;
|
const eventTypes = Object.values(EnclaveMethods);
|
||||||
export const createRpcProcessedEv = (e: EnclaveEvents) => `${e}-processed`;
|
export const isValidEventType = (e: string): e is EnclaveMethods => eventTypes.includes(e);
|
||||||
|
|
||||||
const eventTypes = Object.values(EnclaveEvents);
|
|
||||||
export const isValidEventType = (e: EnclaveEvents) => eventTypes.includes(e);
|
|
||||||
|
|
||||||
type Resolve<T = any> = (value?: T | PromiseLike<T>) => void;
|
|
||||||
type Reject<T = any> = (reason?: T) => void;
|
|
||||||
|
|
||||||
export type ReceiveCb<Arguments, Ret> = (
|
|
||||||
res: Resolve,
|
|
||||||
rej: Reject,
|
|
||||||
id?: number
|
|
||||||
) => RpcEventHandler<Arguments, Ret>;
|
|
||||||
|
|
||||||
export const receiveOnChannelFactory = <CbArgs = any, CbRet = any, Ret = any>(
|
|
||||||
cb: ReceiveCb<CbArgs, CbRet>
|
|
||||||
) => (channel: string, target: any, on: any, id?: number): Promise<Ret> =>
|
|
||||||
new Promise((res, rej) => on.call(target, channel, cb(res, rej, id)));
|
|
||||||
|
|
||||||
export const sendOnChannel = (
|
|
||||||
channel: string,
|
|
||||||
payload: any,
|
|
||||||
target: any,
|
|
||||||
emit: (args: any) => void
|
|
||||||
) => emit.call(target, channel, payload);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user