From de79b535d10e4b72e7ac2b65af5e599da974dd41 Mon Sep 17 00:00:00 2001 From: Yuren Ju Date: Mon, 11 Jun 2018 18:13:53 +0800 Subject: [PATCH] Issue #17: Implemented eth_getBlockByNumber() --- lib/chain/ChainManager.js | 4 ++++ lib/cliParser.js | 7 +++++++ lib/index.js | 6 ++++++ lib/rpc/index.js | 35 +++++++++++++++++++++++++++++++++++ lib/rpc/modules/eth.js | 15 +++++++++++++++ lib/rpc/modules/index.js | 3 +++ lib/rpc/rpc-manager.js | 21 +++++++++++++++++++++ package.json | 3 ++- tests/rpc.js | 27 +++++++++++++++++++++++++++ 9 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 lib/rpc/index.js create mode 100644 lib/rpc/modules/eth.js create mode 100644 lib/rpc/modules/index.js create mode 100644 lib/rpc/rpc-manager.js create mode 100644 tests/rpc.js diff --git a/lib/chain/ChainManager.js b/lib/chain/ChainManager.js index 5cce72d..355d906 100644 --- a/lib/chain/ChainManager.js +++ b/lib/chain/ChainManager.js @@ -53,6 +53,10 @@ class ChainManager { }) } } + + getBlockchain () { + return this._blockchain + } } module.exports = ChainManager diff --git a/lib/cliParser.js b/lib/cliParser.js index 73331c0..aa5c862 100644 --- a/lib/cliParser.js +++ b/lib/cliParser.js @@ -11,6 +11,13 @@ var parser = require('yargs') choices: ['error', 'warn', 'info', 'debug'], default: 'info' }) + .option('rpc', { + describe: 'Enable the JSON-RPC server' + }) + .option('rpcport', { + describe: 'HTTP-RPC server listening port', + default: 8545 + }) .locale('en_EN') parser.getClientConfig = function () { diff --git a/lib/index.js b/lib/index.js index 323728d..3efbf2f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,6 +5,7 @@ const Logger = require('./logging.js') const EthNetworkManager = require('./net/EthNetworkManager.js') const ChainManager = require('./chain/ChainManager.js') const DBManager = require('./chain/DBManager.js') +const RPCServer = require('./rpc') function runClient () { const cliParser = require('./cliParser.js') @@ -31,6 +32,11 @@ function runClient () { }) var cm = new ChainManager(config, nm, bc) // eslint-disable-line + + if (config.rpc) { + const rpc = new RPCServer(cm.getBlockchain()) + rpc.listen(config.rpcport) + } } runClient() diff --git a/lib/rpc/index.js b/lib/rpc/index.js new file mode 100644 index 0000000..ddb8f7c --- /dev/null +++ b/lib/rpc/index.js @@ -0,0 +1,35 @@ +const http = require('http') +const RPCManager = require('./rpc-manager') + +class RPCServer { + constructor (chain) { + this._manager = new RPCManager(chain) + this._server = http.createServer(this._requestHandler.bind(this)) + } + + listen (port) { + this._server.listen(port) + } + + _requestHandler (req, res) { + let body = [] + req.on('data', chunk => body.push(chunk)) + req.on('end', () => { + body = JSON.parse(Buffer.concat(body).toString()) + this._manager.execute(body, (err, result) => { + let response = result + let code = 200 + + if (err) { + code = 406 + response = { code, message: err.message } + } + + res.writeHead(code, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify(response)) + }) + }) + } +} + +module.exports = RPCServer diff --git a/lib/rpc/modules/eth.js b/lib/rpc/modules/eth.js new file mode 100644 index 0000000..07200f0 --- /dev/null +++ b/lib/rpc/modules/eth.js @@ -0,0 +1,15 @@ +class Eth { + constructor (chain) { + this._chain = chain + } + + getBlockByNumber (params, cb) { + let [blockNumber] = params + blockNumber = Number.parseInt(blockNumber, 16) + this._chain.getBlock(blockNumber, (err, block) => { + cb(err, block.toJSON(true)) + }) + } +} + +module.exports = Eth diff --git a/lib/rpc/modules/index.js b/lib/rpc/modules/index.js new file mode 100644 index 0000000..cf89830 --- /dev/null +++ b/lib/rpc/modules/index.js @@ -0,0 +1,3 @@ +const Eth = require('./eth') + +module.exports = { Eth } diff --git a/lib/rpc/rpc-manager.js b/lib/rpc/rpc-manager.js new file mode 100644 index 0000000..6e7f893 --- /dev/null +++ b/lib/rpc/rpc-manager.js @@ -0,0 +1,21 @@ +const modules = require('./modules') + +class RPCServer { + constructor (chain) { + this._chain = chain + this._modules = {} + + // should be 'Eth', 'EVM', etc. + const moduleList = ['Eth'] + moduleList.forEach(moduleName => { + this._modules[moduleName.toLowerCase()] = new modules[moduleName](this._chain) + }) + } + + execute (req, cb) { + const [moduleName, methodName] = req.method.split('_') + this._modules[moduleName][methodName](req.params, cb) + } +} + +module.exports = RPCServer diff --git a/package.json b/package.json index e2f0168..4065747 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "coverage": "nyc npm run test && nyc report --reporter=text-lcov > .nyc_output/lcov.info", "coveralls": "npm run coverage && coveralls <.nyc_output/lcov.info", "lint": "standard", - "test": "npm run lint && node tests/" + "test": "npm run lint && tape tests/*.js" }, "repository": { "type": "git", @@ -42,6 +42,7 @@ "devDependencies": { "coveralls": "^3.0.0", "nyc": "~11.6.0", + "sinon": "^6.0.0", "standard": "~11.0.1", "tape": "~4.9.0", "tmp": "~0.0.33" diff --git a/tests/rpc.js b/tests/rpc.js new file mode 100644 index 0000000..0d12daf --- /dev/null +++ b/tests/rpc.js @@ -0,0 +1,27 @@ +const test = require('tape') +const sinon = require('sinon') +const Manager = require('../lib/rpc/rpc-manager') + +test('eth_getBlockByNumber', t => { + const block = { + toJSON: sinon.stub().returns({}) + } + const blockchain = { + getBlock: sinon.stub().yields(null, block) + } + const manager = new Manager(blockchain) + const req = { + jsonrpc: '2.0', + method: 'eth_getBlockByNumber', + params: ['0x1', true], + id: 1 + } + + t.false(blockchain.getBlock.called) + manager.execute(req, (err, block) => { + t.error(err) + t.true(blockchain.getBlock.called) + t.equal(blockchain.getBlock.firstCall.args[0], 1) + t.end() + }) +})