Initial scaffold of enclave
This commit is contained in:
parent
0cfc218f2c
commit
bbfb084bdc
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
};
|
|
@ -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
|
||||
);
|
|
@ -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;
|
|
@ -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);
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue