import { ethErrors } from 'eth-rpc-errors'; import { UNSUPPORTED_RPC_METHODS } from '../../../../shared/constants/network'; import handlers from './handlers'; const handlerMap = handlers.reduce((map, handler) => { for (const methodName of handler.methodNames) { map.set(methodName, handler); } return map; }, new Map()); /** * Returns a middleware that implements the RPC methods defined in the handlers * directory. * * The purpose of this middleware is to create portable RPC method * implementations that are decoupled from the rest of our background * architecture. * * Handlers consume functions that hook into the background, and only depend * on their signatures, not e.g. controller internals. * * Eventually, we'll want to extract this middleware into its own package. * * @param {Object} opts - The middleware options * @returns {(req: Object, res: Object, next: Function, end: Function) => void} */ export default function createMethodMiddleware(opts) { return function methodMiddleware(req, res, next, end) { // Reject unsupported methods. if (UNSUPPORTED_RPC_METHODS.has(req.method)) { return end(ethErrors.rpc.methodNotSupported()); } const handler = handlerMap.get(req.method); if (handler) { const { implementation, hookNames } = handler; return implementation(req, res, next, end, selectHooks(opts, hookNames)); } return next(); }; } /** * Returns the subset of the specified `hooks` that are included in the * `hookNames` object. This is a Principle of Least Authority (POLA) measure * to ensure that each RPC method implementation only has access to the * API "hooks" it needs to do its job. * * @param {Record} hooks - The hooks to select from. * @param {Record} hookNames - The names of the hooks to select. * @returns {Record | undefined} The selected hooks. */ function selectHooks(hooks, hookNames) { if (hookNames) { return Object.keys(hookNames).reduce((hookSubset, hookName) => { hookSubset[hookName] = hooks[hookName]; return hookSubset; }, {}); } return undefined; }