diff --git a/packages/cli/src/server.ts b/packages/cli/src/server.ts index 68e7b169..d7174091 100644 --- a/packages/cli/src/server.ts +++ b/packages/cli/src/server.ts @@ -276,7 +276,8 @@ export class ServerCmd { gqlLogger: winston.Logger ) => Promise, typeDefs: TypeSource, - paymentsManager?: PaymentsManager + paymentsManager?: PaymentsManager, + createEthRPCHandlers?: (indexer: IndexerInterface) => Promise ): Promise<{ app: Application, server: ApolloServer @@ -317,9 +318,11 @@ export class ServerCmd { const gqlLogger = createGQLLogger(config.server.gql.logDir); const resolvers = await createResolvers(indexer, eventWatcher, gqlLogger); + const ethRPCHandlers = createEthRPCHandlers ? await createEthRPCHandlers(indexer) : {}; + // Create an Express app const app: Application = express(); - const server = await createAndStartServer(app, typeDefs, resolvers, config.server, paymentsManager); + const server = await createAndStartServer(app, typeDefs, resolvers, ethRPCHandlers, config.server, paymentsManager); await startGQLMetricsServer(config); diff --git a/packages/util/package.json b/packages/util/package.json index 586573fe..130f5e57 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -36,6 +36,7 @@ "it-length-prefixed": "^8.0.4", "it-pipe": "^2.0.5", "it-pushable": "^3.1.2", + "jayson": "^4.1.2", "js-yaml": "^4.1.0", "json-bigint": "^1.0.0", "lodash": "^4.17.21", diff --git a/packages/util/src/eth-rpc-handlers.ts b/packages/util/src/eth-rpc-handlers.ts new file mode 100644 index 00000000..f589fdd5 --- /dev/null +++ b/packages/util/src/eth-rpc-handlers.ts @@ -0,0 +1,23 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { IndexerInterface } from './types'; + +export const createEthRPCHandlers = async ( + indexer: IndexerInterface +): Promise => { + return { + eth_blockNumber: async (args: any, callback: any) => { + const syncStatus = await indexer.getSyncStatus(); + const result = syncStatus ? `0x${syncStatus.latestProcessedBlockNumber.toString(16)}` : '0x'; + + callback(null, result); + }, + + eth_call: async (args: any, callback: any) => { + // TODO: Implement + }, + + eth_getLogs: async (args: any, callback: any) => { + // TODO: Implement + } + }; +}; diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index b0dc8c1f..76620754 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -28,3 +28,4 @@ export * from './eth'; export * from './consensus'; export * from './validate-config'; export * from './logger'; +export * from './eth-rpc-handlers'; diff --git a/packages/util/src/server.ts b/packages/util/src/server.ts index 0094b60a..12ec71ee 100644 --- a/packages/util/src/server.ts +++ b/packages/util/src/server.ts @@ -11,6 +11,8 @@ import debug from 'debug'; import responseCachePlugin from 'apollo-server-plugin-response-cache'; import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache'; import queue from 'express-queue'; +import jayson from 'jayson'; +import { json as jsonParser } from 'body-parser'; import { TypeSource } from '@graphql-tools/utils'; import { makeExecutableSchema } from '@graphql-tools/schema'; @@ -27,6 +29,7 @@ export const createAndStartServer = async ( app: Application, typeDefs: TypeSource, resolvers: any, + ethRPCHandlers: any, serverConfig: ServerConfig, paymentsManager?: PaymentsManager ): Promise => { @@ -98,6 +101,17 @@ export const createAndStartServer = async ( path: gqlPath }); + // Create a JSON-RPC server to handle ETH RPC calls at /rpc + const rpcServer = jayson.Server(ethRPCHandlers); + + // Mount the JSON-RPC server to /rpc path + app.use( + '/rpc', + jsonParser(), + // TODO: Handle GET requests as well to match Geth's behaviour + rpcServer.middleware() + ); + httpServer.listen(port, host, () => { log(`Server is listening on ${host}:${port}${server.graphqlPath}`); }); diff --git a/yarn.lock b/yarn.lock index 7feecaab..b50330e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3635,6 +3635,13 @@ dependencies: "@types/node" "*" +"@types/connect@^3.4.33": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + "@types/cors@2.8.12": version "2.8.12" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" @@ -3848,6 +3855,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== +"@types/node@^12.12.54": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + "@types/node@^12.12.6": version "12.20.13" resolved "https://registry.npmjs.org/@types/node/-/node-12.20.13.tgz" @@ -3954,6 +3966,13 @@ resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== +"@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + "@types/ws@^8.5.3": version "8.5.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" @@ -4151,7 +4170,7 @@ resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -JSONStream@^1.0.4: +JSONStream@^1.0.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -7398,6 +7417,18 @@ es6-object-assign@^1.1.0: resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + es6-symbol@^3.1.1, es6-symbol@~3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz" @@ -8293,6 +8324,11 @@ extsprintf@^1.2.0: resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + fake-merkle-patricia-tree@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz" @@ -10422,6 +10458,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" @@ -10651,6 +10692,24 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jayson@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.2.tgz#443c26a8658703e0b2e881117b09395d88b6982e" + integrity sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.5.10" + js-sdsl@^4.1.4: version "4.4.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" @@ -17029,6 +17088,11 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + ws@^8.11.0, ws@^8.12.1, ws@^8.4.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"