diff --git a/src/routes/v3/full-node/blockchain2.js b/src/routes/v3/full-node/blockchain2.js index 8831354..9b345ee 100644 --- a/src/routes/v3/full-node/blockchain2.js +++ b/src/routes/v3/full-node/blockchain2.js @@ -29,12 +29,27 @@ class Blockchain { this.router = router this.router.get("/", this.root) + this.router.get("/getBestBlockHash", this.getBestBlockHash) + this.router.get("/getBlockchainInfo", this.getBlockchainInfo) } root(req, res, next) { return res.json({ status: "blockchain" }) } + // DRY error handler. + errorHandler(err, res) { + // Attempt to decode the error message. + const { msg, status } = routeUtils.decodeError(err) + if (msg) { + res.status(status) + return res.json({ error: msg }) + } + + res.status(500) + return res.json({ error: util.inspect(err) }) + } + /** * @api {get} /blockchain/getBestBlockHash Get best block hash * @apiName GetBestBlockHash @@ -60,19 +75,54 @@ class Blockchain { return res.json(response.data.result) } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) wlogger.error(`Error in blockchain.ts/getBestBlockHash().`, err) - res.status(500) - return res.json({ error: util.inspect(err) }) + return this.errorHandler(err, res) + } + } + + /** + * @api {get} /blockchain/getBlockchainInfo Get blockchain info + * @apiName GetBlockchainInfo + * @apiGroup Blockchain + * @apiDescription Returns an object containing various state info regarding blockchain processing. + * + * @apiExample Example usage: + * curl -X GET "https://mainnet.bchjs.cash/v3/blockchain/getBlockchainInfo" -H "accept: application/json" + * + * @apiSuccess {Object} object Object containing data + * @apiSuccess {String} object.chain "main" + * @apiSuccess {Number} object.blocks 561838 + * @apiSuccess {Number} object.headers 561838 + * @apiSuccess {String} object.bestblockhash "000000000000000002307dd38cd01c7308b8febfcdf5772cf087b5bb023d55bc" + * @apiSuccess {Number} object.difficulty 246585566638.1496 + * @apiSuccess {String} object.mediantime 1545402693 + * @apiSuccess {Number} object.verificationprogress 0.999998831622689 + * @apiSuccess {Boolean} object.chainwork "000000000000000000000000000000000000000000d8c09a8ab7262080266b3e" + * @apiSuccess {Number} object.pruned false + * @apiSuccess {Array} object.softforks Array of objects + * @apiSuccess {String} object.softforks.id "bip34" + * @apiSuccess {String} object.softforks.version 2 + * @apiSuccess {Object} object.softforks.reject + * @apiSuccess {String} object.softforks.reject.status true + */ + async getBlockchainInfo(req, res, next) { + try { + // Axios options + const options = routeUtils.getAxiosOptions() + options.data.id = "getblockchaininfo" + options.data.method = "getblockchaininfo" + options.data.params = [] + + const response = await this.axios.request(options) + + return res.json(response.data.result) + } catch (err) { + // Write out error to error log. + wlogger.error(`Error in blockchain.ts/getBlockchainInfo().`, err) + + return this.errorHandler(err, res) } } } diff --git a/test/v3/blockchain2.js b/test/v3/blockchain2.js index 53152d6..3cb0a0b 100644 --- a/test/v3/blockchain2.js +++ b/test/v3/blockchain2.js @@ -121,7 +121,7 @@ describe("#BlockchainRouter", () => { ) }) - it("returns proper error downstream service stalls", async () => { + it("returns proper error when downstream service stalls", async () => { // Mock the timeout error. sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) @@ -136,7 +136,7 @@ describe("#BlockchainRouter", () => { ) }) - it("returns proper error downstream is down", async () => { + it("returns proper error when downstream service is down", async () => { // Mock the timeout error. sandbox.stub(uut.axios, "request").throws({ code: "ECONNREFUSED" }) @@ -167,59 +167,89 @@ describe("#BlockchainRouter", () => { }) }) - // describe("getBlockchainInfo()", () => { - // // block route handler. - // const getBlockchainInfo = - // blockchainRoute.testableComponents.getBlockchainInfo - // - // it("should throw 503 when network issues", async () => { - // // Save the existing RPC URL. - // const savedUrl2 = process.env.RPC_BASEURL - // - // // Manipulate the URL to cause a 500 network error. - // process.env.RPC_BASEURL = "http://fakeurl/api/" - // - // const result = await getBlockchainInfo(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // // Restore the saved URL. - // process.env.RPC_BASEURL = savedUrl2 - // - // assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") - // assert.include( - // result.error, - // "Could not communicate with full node", - // "Error message expected" - // ) - // }) - // - // it("should GET /getBlockchainInfo", async () => { - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .reply(200, { result: mockData.mockBlockchainInfo }) - // } - // - // const result = await getBlockchainInfo(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAnyKeys(result, [ - // "chain", - // "blocks", - // "headers", - // "bestblockhash", - // "difficulty", - // "mediantime", - // "verificationprogress", - // "chainwork", - // "pruned", - // "softforks", - // "bip9_softforks" - // ]) - // }) - // }) - // + describe("getBlockchainInfo()", () => { + // block route handler. + // const getBlockchainInfo = + // blockchainRoute.testableComponents.getBlockchainInfo + + it("should throw 503 when network issues", async () => { + // Save the existing RPC URL. + const savedUrl2 = process.env.RPC_BASEURL + + // Manipulate the URL to cause a 500 network error. + process.env.RPC_BASEURL = "http://fakeurl/api/" + + const result = await uut.getBlockchainInfo(req, res) + //console.log(`result: ${util.inspect(result)}`) + + // Restore the saved URL. + process.env.RPC_BASEURL = savedUrl2 + + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") + assert.include( + result.error, + "Could not communicate with full node", + "Error message expected" + ) + }) + + it("returns proper error when downstream service stalls", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) + + const result = await uut.getBestBlockHash(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") + assert.include( + result.error, + "Could not communicate with full node", + "Error message expected" + ) + }) + + it("returns proper error when downstream service is down", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNREFUSED" }) + + const result = await uut.getBestBlockHash(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") + assert.include( + result.error, + "Could not communicate with full node", + "Error message expected" + ) + }) + + it("should GET /getBlockchainInfo", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + nock(`${process.env.RPC_BASEURL}`) + .post(uri => uri.includes("/")) + .reply(200, { result: mockData.mockBlockchainInfo }) + } + + const result = await uut.getBlockchainInfo(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAnyKeys(result, [ + "chain", + "blocks", + "headers", + "bestblockhash", + "difficulty", + "mediantime", + "verificationprogress", + "chainwork", + "pruned", + "softforks", + "bip9_softforks" + ]) + }) + }) + // describe("getBlockCount()", () => { // // block route handler. // const getBlockCount = blockchainRoute.testableComponents.getBlockCount