Initial scaffold of enclave

This commit is contained in:
HenryNguyen5 2018-04-23 20:34:15 -04:00
parent 0cfc218f2c
commit bbfb084bdc
10 changed files with 230 additions and 0 deletions

View File

@ -0,0 +1,52 @@
import {
createRpcRequestedEv,
createRpcProcessedEv,
sendOnChannel,
idGeneratorFactory,
receiveOnChannelFactory
} from './utils';
import { EnclaveEvents, MatchingIdHandler, RpcEvent, RpcEventSuccess } from './types';
import { Reject, Resolve } from 'mycrypto-shepherd/dist/lib/types';
const genId = idGeneratorFactory();
const matchOnId = (id: number | undefined, cb: MatchingIdHandler) => (
ev: string,
args: RpcEvent
) => {
if (id === args.id) {
cb(ev, args);
}
};
type PromiseHandler<T> = (res: Resolve, rej: Reject) => MatchingIdHandler<T>;
const isRpcSuccess = (args: RpcEvent): args is RpcEventSuccess => args.payload;
const handleServerResponse: PromiseHandler<RpcEvent> = (res, rej) => (_, args) => {
if (isRpcSuccess(args)) {
res(args.payload);
} else {
rej(args.errMsg);
}
};
const clientRecieve = receiveOnChannelFactory((res, rej, id) =>
matchOnId(id, handleServerResponse(res, rej))
);
export const createClientRpcHandler = <T = any, R = any>(
target: any,
channel: EnclaveEvents,
sender: any,
receiver: any
) => (payload: T): Promise<R> => {
const id = genId();
const sendingChannel = createRpcRequestedEv(channel);
const receivingChannel = createRpcProcessedEv(channel);
// send request event on channel to be processed by server
sendOnChannel(sendingChannel, { payload, id }, target, sender);
// return a promise that resolves/rejects when a matching id response comes back from server
return clientRecieve(receivingChannel, id, target, receiver);
};

View File

@ -0,0 +1,23 @@
import { SignRawTransactionParams, ElectronInjectedGlobals, EnclaveEvents } from './types';
import { createClientRpcHandler } from './client-utils';
import EventEmitter from 'events';
const myEE = new EventEmitter();
function isElectronEnv() {
return process.env.BUILD_ELECTRON;
}
function getElectronWindow() {
return window as ElectronInjectedGlobals;
}
export function signRawTransaction(params: SignRawTransactionParams) {
// if we're in electron env then use the globally injected signRawTransaction from the preload script
if (isElectronEnv) {
const w = getElectronWindow();
w.signRawTransaction(params);
} else {
// otherwise create an rpc event handler based on "events" package
createClientRpcHandler(myEE, EnclaveEvents.SIGN_RAW_TRANSACTION, myEE.emit, myEE.on);
}
}

View File

View File

@ -0,0 +1,11 @@
import { ipcRenderer } from 'electron';
import { EnclaveEvents, SignRawTransactionParams, SignRawTransaction } from './types';
import { createClientRpcHandler } from './client-utils';
const createElectronRpcHandler = <T = any, R = any>(channel: EnclaveEvents) =>
createClientRpcHandler<T, R>(ipcRenderer, channel, ipcRenderer.send, ipcRenderer.once);
export const signRawTransaction = createElectronRpcHandler<
SignRawTransactionParams,
SignRawTransaction
>(EnclaveEvents.SIGN_RAW_TRANSACTION);

View File

@ -0,0 +1,18 @@
import { createRpcRequestedEv, receiveOnChannelFactory } from './utils';
import { EnclaveEvents, RpcEventServer } from './types';
// uses factory component, skips the promise handling and id parameter as those are only used on client side
// cb instead handles the receiving event, and on success/failure emits on EnclaveEvent channel with its response
const serverRecieve = <T = any>(cb: any) => receiveOnChannelFactory<RpcEventServer, T>(() => cb);
// create a receiving channel for incoming events of Enclave events type
// calls cb when a the channel has an event emitted to it
export const createServerRpcHandler = <T = any>(
target: any,
channel: EnclaveEvents,
receiver: any,
cb: any
) => {
const receivingChannel = createRpcRequestedEv(channel);
serverRecieve<T>(cb)(receivingChannel, target, receiver);
};

View File

@ -0,0 +1,39 @@
import { createRpcProcessedEv, sendOnChannel } from './utils';
import {
EnclaveEvents,
RpcEventServer,
RpcEventHandler,
SignRawTransactionParams,
RpcEventSuccess,
SignRawTransaction
} from './types';
import { ipcMain } from 'electron';
import { createServerRpcHandler } from './server-utils';
// mock
// this hard codes electron specific sending atm (ev.sender.send)
export const signRawTransactionRequest: RpcEventHandler<
RpcEventServer<SignRawTransactionParams>,
void
> = (ev: any, args) => {
// "processing steps"
// this is where you would sign the transaction
const res: RpcEventSuccess<SignRawTransaction> = {
payload: { r: '1', s: '1', v: '1' },
id: args.id,
errMsg: null
};
// emit result back to client as presonse
// pull this out later on into server-utils
const sendingChannel = createRpcProcessedEv(EnclaveEvents.SIGN_RAW_TRANSACTION);
sendOnChannel(sendingChannel, res, ev, ev.sender.send);
};
// create the handler for SIGN_RAW_TRANSACTION
createServerRpcHandler(
ipcMain,
EnclaveEvents.SIGN_RAW_TRANSACTION,
ipcMain.on,
signRawTransactionRequest
);

View File

@ -0,0 +1,43 @@
export enum EnclaveEvents {
SIGN_RAW_TRANSACTION = 'sign-raw-transaction'
}
export interface RpcEventFailure {
payload: null;
errMsg: string;
id: number;
}
export interface RpcEventSuccess<T = any> {
payload: T;
errMsg: null;
id: number;
}
export type RpcEvent<T = any> = RpcEventFailure | RpcEventSuccess<T>;
export interface RpcEventServer<T = any> {
payload: T;
id: number;
}
export type RpcEventHandler<A = any, R = any> = (event: EnclaveEvents, args: A) => R;
export type MatchingIdHandler<A = any> = (event: string, args: A) => void;
export interface SignRawTransactionParams {
path: string;
rawTxHex: string;
}
export interface SignRawTransaction {
s: string;
v: string;
r: string;
}
export interface EnclaveProvider {
signRawTransaction(params: SignRawTransactionParams): Promise<SignRawTransaction>;
}
export type ElectronInjectedGlobals = Window & EnclaveProvider;

View File

@ -0,0 +1,34 @@
import { EnclaveEvents, RpcEventHandler } from './types';
export const idGeneratorFactory = () => {
let callId = 0;
return () => {
const currValue = callId;
callId += 1;
return currValue;
};
};
export const createRpcRequestedEv = (e: EnclaveEvents) => `${e}-requested`;
export const createRpcProcessedEv = (e: EnclaveEvents) => `${e}-processed`;
type Resolve<T = any> = (value?: T | PromiseLike<T>) => void;
type Reject<T = any> = (reason?: T) => void;
export type ReceiveCb<Arguments = any, Ret = any> = (
res: Resolve,
rej: Reject,
id?: number
) => RpcEventHandler<Arguments, Ret>;
export const receiveOnChannelFactory = <Arguments = any, Ret = any, K = any>(
cb: ReceiveCb<Arguments, Ret>
) => (channel: string, target: any, on: any, id?: number): Promise<K> =>
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);

View File

@ -23,6 +23,7 @@
"ethereumjs-tx": "1.3.4",
"ethereumjs-util": "5.1.5",
"ethereumjs-wallet": "0.6.0",
"events": "^2.0.0",
"font-awesome": "4.7.0",
"hdkey": "0.8.0",
"idna-uts46": "1.1.0",
@ -62,6 +63,7 @@
"@types/classnames": "2.2.3",
"@types/enzyme": "3.1.8",
"@types/enzyme-adapter-react-16": "1.0.1",
"@types/events": "^1.2.0",
"@types/history": "4.6.2",
"@types/jest": "22.2.3",
"@types/lodash": "4.14.107",

View File

@ -88,6 +88,10 @@
"@types/cheerio" "*"
"@types/react" "*"
"@types/events@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
"@types/history@*", "@types/history@4.6.2":
version "4.6.2"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0"
@ -3872,6 +3876,10 @@ events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
events@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-2.0.0.tgz#cbbb56bf3ab1ac18d71c43bb32c86255062769f2"
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"