deth
Version:
Ethereum node focused on Developer Experience
68 lines (67 loc) • 2.44 kB
JavaScript
import { assert } from 'ts-essentials';
import { Mutex } from 'async-mutex';
import * as t from 'io-ts';
import { isRight, isLeft } from 'fp-ts/lib/Either';
import { responseOf } from '@restless/restless';
import { IOTSError, NotFoundHttpError } from '../errorHandler';
import debug from 'debug';
const d = debug('deth:rpc');
const executionMutex = new Mutex();
const jsonRpcEnvelope = t.type({
jsonrpc: t.literal('2.0'),
id: t.union([t.number, t.string]),
method: t.string,
params: t.any,
});
export function sanitizeRPCEnvelope() {
return (_data, req) => {
const result = jsonRpcEnvelope.decode(req.body);
if (isLeft(result)) {
throw new IOTSError(result);
}
};
}
export function sanitizeRPC(schema) {
return (_data, req) => {
const m = req.body.method;
const rpcDescription = schema[m];
d(`--> RPC call ${m}`);
d(`--> RPC call data ${JSON.stringify(req.body.params)}`);
if (!rpcDescription) {
throw new NotFoundHttpError([`RPC method: ${m} called with ${JSON.stringify(req.body.params)} not found`]);
}
const params = req.body.params;
// we need to normalize empty arrays to undefineds
const normalizedParams = Array.isArray(params) && params.length === 0 ? undefined : params;
const res = rpcDescription.parameters.decode(normalizedParams);
if (isRight(res)) {
return res.right;
}
throw new IOTSError(res);
};
}
export function executeRPC(executors) {
return async (data, req) => {
const method = req.body.method;
const executor = executors[method];
assert(executor, `Couldn't find executor for ${method}`);
// @todo: NOTE currently we run RPC calls sequentially. This limits performance but solves concurrency issues.
return executionMutex.runExclusive(async () => {
return executor(data);
});
};
}
export function respondRPC(schema) {
return (data, req) => {
const method = req.body.method;
const rpcDescription = schema[method];
d(`<-- RES: ${JSON.stringify(data)}`);
assert(rpcDescription, `Couldn't find rpc description for ${method}`);
const result = rpcDescription.returns.encode(data);
return responseOf({
jsonrpc: '2.0',
id: req.body.id,
result: result,
});
};
}