From 380acbbfa4e078ddb1006360f19da023ce29023c Mon Sep 17 00:00:00 2001 From: danielhumgon Date: Fri, 14 Feb 2020 13:02:58 -0400 Subject: [PATCH] refactor(blockchain.js): Refactored blockchain.js library [WIP] --- src/routes/v3/full-node/blockchain2.js | 31 +- test/v3/blockchain2.js | 2519 ++++++++++++++---------- 2 files changed, 1478 insertions(+), 1072 deletions(-) diff --git a/src/routes/v3/full-node/blockchain2.js b/src/routes/v3/full-node/blockchain2.js index a84c70e..906707e 100644 --- a/src/routes/v3/full-node/blockchain2.js +++ b/src/routes/v3/full-node/blockchain2.js @@ -36,19 +36,22 @@ class Blockchain { this.router.get("/getBlockchainInfo", this.getBlockchainInfo) this.router.get("/getBlockCount", this.getBlockCount) this.router.get("/getBlockHeader/:hash", this.getBlockHeaderSingle) - this.router.post("/getBlockHeader", getBlockHeaderBulk) - this.router.get("/getChainTips", getChainTips) - this.router.get("/getDifficulty", getDifficulty) - this.router.get("/getMempoolEntry/:txid", getMempoolEntrySingle) - this.router.post("/getMempoolEntry", getMempoolEntryBulk) - this.router.get("/getMempoolAncestors/:txid", getMempoolAncestorsSingle) - this.router.get("/getMempoolInfo", getMempoolInfo) - this.router.get("/getRawMempool", getRawMempool) - this.router.get("/getTxOut/:txid/:n", getTxOut) - this.router.get("/getTxOutProof/:txid", getTxOutProofSingle) - this.router.post("/getTxOutProof", getTxOutProofBulk) - this.router.get("/verifyTxOutProof/:proof", verifyTxOutProofSingle) - this.router.post("/verifyTxOutProof", verifyTxOutProofBulk) + this.router.post("/getBlockHeader", this.getBlockHeaderBulk) + this.router.get("/getChainTips", this.getChainTips) + this.router.get("/getDifficulty", this.getDifficulty) + this.router.get("/getMempoolEntry/:txid", this.getMempoolEntrySingle) + this.router.post("/getMempoolEntry", this.getMempoolEntryBulk) + this.router.get( + "/getMempoolAncestors/:txid", + this.getMempoolAncestorsSingle + ) + this.router.get("/getMempoolInfo", this.getMempoolInfo) + this.router.get("/getRawMempool", this.getRawMempool) + this.router.get("/getTxOut/:txid/:n", this.getTxOut) + this.router.get("/getTxOutProof/:txid", this.getTxOutProofSingle) + this.router.post("/getTxOutProof", this.getTxOutProofBulk) + this.router.get("/verifyTxOutProof/:proof", this.verifyTxOutProofSingle) + this.router.post("/verifyTxOutProof", this.verifyTxOutProofBulk) } root(req, res, next) { @@ -532,7 +535,7 @@ class Blockchain { options.data.method = "getmempoolancestors" options.data.params = [txid, verbose] - const response = await this.axios.request(requestConfig) + const response = await this.axios.request(options) // console.log(`response: ${util.inspect(response)}`) return res.json(response.data.result) diff --git a/test/v3/blockchain2.js b/test/v3/blockchain2.js index debaa2d..d74918d 100644 --- a/test/v3/blockchain2.js +++ b/test/v3/blockchain2.js @@ -10,9 +10,7 @@ const chai = require("chai") const assert = chai.assert -const nock = require("nock") // HTTP mocking const sinon = require("sinon") -// const blockchainRoute = require("../../src/routes/v3/full-node/blockchain") const Blockchain = require("../../src/routes/v3/full-node/blockchain2") const uut = new Blockchain() @@ -62,18 +60,11 @@ describe("#BlockchainRouter2", () => { // Explicitly reset the parmas and body. req.params = {} req.body = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - sandbox = sinon.createSandbox() }) afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - + // Restore Sandbox sandbox.restore() }) @@ -97,9 +88,6 @@ describe("#BlockchainRouter2", () => { }) describe("getBestBlockHash()", () => { - // block route handler. - // const getBestBlockHash = blockchainRoute.testableComponents.getBestBlockHash - it("should throw 503 when network issues", async () => { // Save the existing RPC URL. const savedUrl2 = process.env.RPC_BASEURL @@ -165,12 +153,743 @@ describe("#BlockchainRouter2", () => { assert.isString(result) assert.equal(result.length, 64, "Hash string is fixed length") }) + + describe("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") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { 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()", () => { + 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.getBlockCount(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.getBlockCount(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.getBlockCount(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 /getBlockCount", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: 126769 } }) + } + + const result = await uut.getBlockCount(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.isNumber(result) + }) + }) }) - describe("getBlockchainInfo()", () => { - // block route handler. - // const getBlockchainInfo = - // blockchainRoute.testableComponents.getBlockchainInfo + describe("getBlockHeaderSingle()", async () => { + it("should throw 400 error if hash is missing", async () => { + const result = await uut.getBlockHeaderSingle(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "hash can not be empty") + }) + 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/" + + req.params.hash = + "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" + + const result = await uut.getBlockHeaderSingle(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, + "Network 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" }) + + req.params.hash = + "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" + + const result = await uut.getBlockHeaderSingle(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" }) + + req.params.hash = + "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" + + const result = await uut.getBlockHeaderSingle(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 block header", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox.stub(uut.axios, "request").resolves({ + data: { + result: + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" + } + }) + } + + req.params.hash = + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + + const result = await uut.getBlockHeaderSingle(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.isString(result) + assert.equal( + result, + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" + ) + }) + + it("should GET verbose block header", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockBlockHeader } }) + } + + req.query.verbose = true + req.params.hash = + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + + const result = await uut.getBlockHeaderSingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, [ + "hash", + "confirmations", + "height", + "version", + "versionHex", + "merkleroot", + "time", + "mediantime", + "nonce", + "bits", + "difficulty", + "chainwork", + "previousblockhash", + "nextblockhash" + ]) + }) + }) + + // + + describe("#getBlockHeaderBulk", () => { + it("should throw an error for an empty body", async () => { + req.body = {} + + const result = await uut.getBlockHeaderBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "hashes needs to be an array", + "Proper error message" + ) + }) + + it("should error on non-array single hash", async () => { + req.body.hashes = + "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" + + const result = await uut.getBlockHeaderBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "hashes needs to be an array", + "Proper error message" + ) + }) + + it("should throw 400 error if addresses array is too large", async () => { + const testArray = [] + for (var i = 0; i < 25; i++) testArray.push("") + + req.body.hashes = testArray + + const result = await uut.getBlockHeaderBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "Array too large") + }) + + it("should throw a 400 error for an invalid hash", async () => { + req.body.hashes = ["badHash"] + + await uut.getBlockHeaderBulk(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + }) + + it("should throw 500 when network issues", async () => { + const savedUrl = process.env.BITCOINCOM_BASEURL + + try { + req.body.hashes = [ + "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" + ] + + // Switch the Insight URL to something that will error out. + process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" + + const result = await uut.getBlockHeaderBulk(req, res) + + // Restore the saved URL. + process.env.BITCOINCOM_BASEURL = savedUrl + + assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") + assert.include(result.error, "ENOTFOUND", "Error message expected") + } catch (err) { + // Restore the saved URL. + process.env.BITCOINCOM_BASEURL = savedUrl + } + }) + it("returns proper error when downstream service stalls", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) + + req.body.hashes = [ + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + ] + + const result = await uut.getBlockHeaderBulk(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" }) + + req.body.hashes = [ + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + ] + + const result = await uut.getBlockHeaderBulk(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 concise block header for a single hash", async () => { + req.body.hashes = [ + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + ] + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockBlockHeaderConcise } }) + } + + // Call the details API. + const result = await uut.getBlockHeaderBulk(req, res) + // console.log(`result: ${util.inspect(result)}`) + + // Assert that required fields exist in the returned object. + assert.isArray(result) + assert.equal( + result[0], + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" + ) + }) + + it("should get verbose block header for a single hash", async () => { + req.body = { + hashes: [ + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + ], + verbose: true + } + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockBlockHeader } }) + } + + // Call the details API. + const result = await uut.getBlockHeaderBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + // Assert that required fields exist in the returned object. + assert.isArray(result) + assert.hasAllKeys(result[0], [ + "hash", + "confirmations", + "height", + "version", + "versionHex", + "merkleroot", + "time", + "mediantime", + "nonce", + "bits", + "difficulty", + "chainwork", + "previousblockhash", + "nextblockhash" + ]) + }) + + it("should get details for multiple block heights", async () => { + req.body.hashes = [ + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0", + "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" + ] + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockBlockHeaderConcise } }) + } + + // Call the details API. + const result = await uut.getBlockHeaderBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + assert.equal(result.length, 2, "2 outputs for 2 inputs") + }) + }) + describe("getChainTips()", () => { + 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.getChainTips(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.getChainTips(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.getChainTips(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 /getChainTips", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockChainTips } }) + } + + const result = await uut.getChainTips(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], ["height", "hash", "branchlen", "status"]) + }) + }) + describe("getDifficulty()", () => { + 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.getDifficulty(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.getDifficulty(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.getDifficulty(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 /getDifficulty", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: 4049809.205246544 } }) + } + + const result = await uut.getDifficulty(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.isNumber(result) + }) + }) + describe("getMempoolInfo()", () => { + 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.getMempoolInfo(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.getMempoolInfo(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.getMempoolInfo(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 /getMempoolInfo", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockMempoolInfo } }) + } + + const result = await uut.getMempoolInfo(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAnyKeys(result, [ + "result", + "bytes", + "usage", + "maxmempool", + "mempoolminfree" + ]) + }) + }) + describe("getRawMempool()", () => { + 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.getRawMempool(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.getRawMempool(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.getRawMempool(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 /getMempoolInfo", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockRawMempool } }) + } + + const result = await uut.getRawMempool(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + // Not sure what other assertions should be made here. + }) + }) + describe("getMempoolEntrySingle()", () => { + it("should throw 400 if txid is empty", async () => { + const result = await uut.getMempoolEntrySingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "txid can not be empty") + }) it("should throw 503 when network issues", async () => { // Save the existing RPC URL. @@ -179,7 +898,288 @@ describe("#BlockchainRouter2", () => { // Manipulate the URL to cause a 500 network error. process.env.RPC_BASEURL = "http://fakeurl/api/" - const result = await uut.getBlockchainInfo(req, res) + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolEntrySingle(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" }) + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolEntrySingle(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" }) + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolEntrySingle(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 /getMempoolEntry", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox.stub(uut.axios, "request").resolves({ + data: { result: { error: "Transaction not in mempool" } } + }) + } + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolEntrySingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.isString(result.error) + assert.equal(result.error, "Transaction not in mempool") + }) + }) + describe("#getMempoolEntryBulk", () => { + it("should throw an error for an empty body", async () => { + req.body = {} + + const result = await uut.getMempoolEntryBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "txids needs to be an array", + "Proper error message" + ) + }) + + it("should error on non-array single txid", async () => { + req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolEntryBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "txids needs to be an array", + "Proper error message" + ) + }) + + it("should throw 400 error if addresses array is too large", async () => { + const testArray = [] + for (var i = 0; i < 25; i++) testArray.push("") + + req.body.txids = testArray + + const result = await uut.getMempoolEntryBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "Array too large") + }) + it("returns proper error when downstream service stalls", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) + + req.body.txids = [ + `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + ] + + const result = await uut.getMempoolEntryBulk(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" }) + + req.body.txids = [ + `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + ] + + const result = await uut.getMempoolEntryBulk(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" + ) + }) + // Only execute on integration tests. + if (process.env.TEST !== "unit") { + // Dev-note: This test passes because it expects an error. TXIDs do not + // stay in the mempool for long, so it does not work well for a unit or + // integration test. + it("should retrieve single mempool entry", async () => { + req.body.txids = [ + `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + ] + + const result = await uut.getMempoolEntryBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.isString(result.error) + assert.equal(result.error, "Transaction not in mempool") + }) + + // Dev-note: This test passes because it expects an error. TXIDs do not + // stay in the mempool for long, so it does not work well for a unit or + // integration test. + it("should retrieve multiple mempool entries", async () => { + req.body.txids = [ + `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde`, + `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + ] + + const result = await uut.getMempoolEntryBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.isString(result.error) + assert.equal(result.error, "Transaction not in mempool") + }) + } + }) + describe("getMempoolAncestorsSingle()", () => { + it("should throw 400 if txid is empty", async () => { + const result = await uut.getMempoolAncestorsSingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "txid can not be empty") + }) + + 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/" + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolAncestorsSingle(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" }) + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolAncestorsSingle(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" }) + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getMempoolAncestorsSingle(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 /getMempoolAncestorsSingle", async () => { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockAncestors } }) + + req.params.txid = `bb0d349892d351da2767f8c45f6f7949713ff09bd12838d53e76158ddee3ce93` + + const result = await uut.getMempoolAncestorsSingle(req, res) + // console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + }) + }) + describe("getTxOut()", () => { + it("should throw 400 if txid is empty", async () => { + const result = await uut.getTxOut(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "txid can not be empty") + }) + + it("should throw 400 if n is empty", async () => { + req.params.txid = `sometxid` + const result = await uut.getTxOut(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "n can not be empty") + }) + + 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/" + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + req.params.n = 0 + + const result = await uut.getTxOut(req, res) //console.log(`result: ${util.inspect(result)}`) // Restore the saved URL. @@ -197,7 +1197,11 @@ describe("#BlockchainRouter2", () => { // Mock the timeout error. sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) - const result = await uut.getBestBlockHash(req, res) + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` + req.params.n = 0 + req.query.include_mempool = "true" + + const result = await uut.getTxOut(req, res) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") @@ -207,12 +1211,115 @@ describe("#BlockchainRouter2", () => { "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) + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` + req.params.n = 0 + req.query.include_mempool = "true" + + const result = await uut.getTxOut(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" + ) + }) + // This test can only run for unit tests. See TODO at the top of this file. + it("should GET /getTxOut", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockTxOut } }) + } + + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` + req.params.n = 0 + req.query.include_mempool = "true" + + const result = await uut.getTxOut(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "bestblock", + "confirmations", + "value", + "scriptPubKey", + "coinbase" + ]) + assert.hasAllKeys(result.scriptPubKey, [ + "asm", + "hex", + "reqSigs", + "type", + "addresses" + ]) + assert.isArray(result.scriptPubKey.addresses) + }) + }) + describe("getTxOutProofSingle()", () => { + it("should throw 400 if txid is empty", async () => { + const result = await uut.getTxOutProofSingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "txid can not be empty") + }) + + 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/" + + req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getTxOutProofSingle(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" }) + + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` + req.params.n = 0 + req.query.include_mempool = "true" + + const result = await uut.getTxOutProofSingle(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" }) + + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` + req.params.n = 0 + req.query.include_mempool = "true" + + const result = await uut.getTxOutProofSingle(req, res) // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") @@ -223,1049 +1330,345 @@ describe("#BlockchainRouter2", () => { ) }) - it("should GET /getBlockchainInfo", async () => { + it("should GET /getTxOutProof", 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 }) + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockTxOutProof } }) } - const result = await uut.getBlockchainInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) + req.params.txid = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - assert.hasAnyKeys(result, [ - "chain", - "blocks", - "headers", - "bestblockhash", - "difficulty", - "mediantime", - "verificationprogress", - "chainwork", - "pruned", - "softforks", - "bip9_softforks" - ]) + const result = await uut.getTxOutProofSingle(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isString(result) }) }) + describe("#getTxOutProofBulk", () => { + it("should throw an error for an empty body", async () => { + req.body = {} - // describe("getBlockCount()", () => { - // // block route handler. - // const getBlockCount = blockchainRoute.testableComponents.getBlockCount - // - // 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 getBlockCount(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 /getBlockCount", 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: 126769 }) - // } - // - // const result = await getBlockCount(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.isNumber(result) - // }) - // }) - // - // describe("getBlockHeaderSingle()", async () => { - // const getBlockHeader = - // blockchainRoute.testableComponents.getBlockHeaderSingle - // - // it("should throw 400 error if hash is missing", async () => { - // const result = await getBlockHeader(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "hash can not be empty") - // }) - // - // 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/" - // - // req.params.hash = - // "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - // - // const result = await getBlockHeader(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, - // "Network error: Could not communicate with full node", - // "Error message expected" - // ) - // }) - // - // it("should GET block header", 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: - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" - // }) - // } - // - // req.params.hash = - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" - // - // const result = await getBlockHeader(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.isString(result) - // assert.equal( - // result, - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" - // ) - // }) - // - // it("should GET verbose block header", 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.mockBlockHeader }) - // } - // - // req.query.verbose = true - // req.params.hash = - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" - // - // const result = await getBlockHeader(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, [ - // "hash", - // "confirmations", - // "height", - // "version", - // "versionHex", - // "merkleroot", - // "time", - // "mediantime", - // "nonce", - // "bits", - // "difficulty", - // "chainwork", - // "previousblockhash", - // "nextblockhash" - // ]) - // }) - // }) - // - // describe("#getBlockHeaderBulk", () => { - // // route handler. - // const getBlockHeaderBulk = - // blockchainRoute.testableComponents.getBlockHeaderBulk - // - // it("should throw an error for an empty body", async () => { - // req.body = {} - // - // const result = await getBlockHeaderBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "hashes needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should error on non-array single hash", async () => { - // req.body.hashes = - // "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - // - // const result = await getBlockHeaderBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "hashes needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should throw 400 error if addresses array is too large", async () => { - // const testArray = [] - // for (var i = 0; i < 25; i++) testArray.push("") - // - // req.body.hashes = testArray - // - // const result = await getBlockHeaderBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "Array too large") - // }) - // - // it("should throw a 400 error for an invalid hash", async () => { - // req.body.hashes = ["badHash"] - // - // await getBlockHeaderBulk(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // }) - // - // it("should throw 500 when network issues", async () => { - // const savedUrl = process.env.BITCOINCOM_BASEURL - // - // try { - // req.body.hashes = [ - // "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - // ] - // - // // Switch the Insight URL to something that will error out. - // process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - // - // const result = await getBlockHeaderBulk(req, res) - // - // // Restore the saved URL. - // process.env.BITCOINCOM_BASEURL = savedUrl - // - // assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - // assert.include(result.error, "ENOTFOUND", "Error message expected") - // } catch (err) { - // // Restore the saved URL. - // process.env.BITCOINCOM_BASEURL = savedUrl - // } - // }) - // - // it("should get concise block header for a single hash", async () => { - // req.body.hashes = [ - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" - // ] - // - // // 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.mockBlockHeaderConcise }) - // } - // - // // Call the details API. - // const result = await getBlockHeaderBulk(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // // Assert that required fields exist in the returned object. - // assert.isArray(result) - // assert.equal( - // result[0], - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c" - // ) - // }) - // - // it("should get verbose block header for a single hash", async () => { - // req.body = { - // hashes: [ - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" - // ], - // verbose: true - // } - // - // // 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.mockBlockHeader }) - // } - // - // // Call the details API. - // const result = await getBlockHeaderBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // // Assert that required fields exist in the returned object. - // assert.isArray(result) - // assert.hasAllKeys(result[0], [ - // "hash", - // "confirmations", - // "height", - // "version", - // "versionHex", - // "merkleroot", - // "time", - // "mediantime", - // "nonce", - // "bits", - // "difficulty", - // "chainwork", - // "previousblockhash", - // "nextblockhash" - // ]) - // }) - // - // it("should get details for multiple block heights", async () => { - // req.body.hashes = [ - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0", - // "000000000000000002fa9d7851b284c53a5831651b04211c266badf2ad2d8ef0" - // ] - // - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .times(2) - // .reply(200, { result: mockData.mockBlockHeaderConcise }) - // } - // - // // Call the details API. - // const result = await getBlockHeaderBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.isArray(result) - // assert.equal(result.length, 2, "2 outputs for 2 inputs") - // }) - // }) - // - // describe("getChainTips()", () => { - // // block route handler. - // const getChainTips = blockchainRoute.testableComponents.getChainTips - // - // 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 getChainTips(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 /getChainTips", 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.mockChainTips }) - // } - // - // const result = await getChainTips(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.isArray(result) - // assert.hasAnyKeys(result[0], ["height", "hash", "branchlen", "status"]) - // }) - // }) - // - // describe("getDifficulty()", () => { - // // block route handler. - // const getDifficulty = blockchainRoute.testableComponents.getDifficulty - // - // 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 getDifficulty(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 /getDifficulty", 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: 4049809.205246544 }) - // } - // - // const result = await getDifficulty(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.isNumber(result) - // }) - // }) - // - // describe("getMempoolInfo()", () => { - // // block route handler. - // const getMempoolInfo = blockchainRoute.testableComponents.getMempoolInfo - // - // 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 getMempoolInfo(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 /getMempoolInfo", 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.mockMempoolInfo }) - // } - // - // const result = await getMempoolInfo(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAnyKeys(result, [ - // "result", - // "bytes", - // "usage", - // "maxmempool", - // "mempoolminfree" - // ]) - // }) - // }) - // - // describe("getRawMempool()", () => { - // // block route handler. - // const getRawMempool = blockchainRoute.testableComponents.getRawMempool - // - // 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 getRawMempool(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 /getMempoolInfo", 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.mockRawMempool }) - // } - // - // const result = await getRawMempool(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.isArray(result) - // // Not sure what other assertions should be made here. - // }) - // }) - // - // describe("getMempoolEntry()", () => { - // // block route handler. - // const getMempoolEntry = - // blockchainRoute.testableComponents.getMempoolEntrySingle - // - // it("should throw 400 if txid is empty", async () => { - // const result = await getMempoolEntry(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "txid can not be empty") - // }) - // - // 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/" - // - // req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getMempoolEntry(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 /getMempoolEntry", 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: { error: "Transaction not in mempool" } }) - // } - // - // req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getMempoolEntry(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.isString(result.error) - // assert.equal(result.error, "Transaction not in mempool") - // }) - // }) - // - // describe("#getMempoolEntryBulk", () => { - // // route handler. - // const getMempoolEntryBulk = - // blockchainRoute.testableComponents.getMempoolEntryBulk - // - // it("should throw an error for an empty body", async () => { - // req.body = {} - // - // const result = await getMempoolEntryBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "txids needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should error on non-array single txid", async () => { - // req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getMempoolEntryBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "txids needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should throw 400 error if addresses array is too large", async () => { - // const testArray = [] - // for (var i = 0; i < 25; i++) testArray.push("") - // - // req.body.txids = testArray - // - // const result = await getMempoolEntryBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "Array too large") - // }) - // - // // Only execute on integration tests. - // if (process.env.TEST !== "unit") { - // // Dev-note: This test passes because it expects an error. TXIDs do not - // // stay in the mempool for long, so it does not work well for a unit or - // // integration test. - // it("should retrieve single mempool entry", async () => { - // req.body.txids = [ - // `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // ] - // - // const result = await getMempoolEntryBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.isString(result.error) - // assert.equal(result.error, "Transaction not in mempool") - // }) - // - // // Dev-note: This test passes because it expects an error. TXIDs do not - // // stay in the mempool for long, so it does not work well for a unit or - // // integration test. - // it("should retrieve multiple mempool entries", async () => { - // req.body.txids = [ - // `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde`, - // `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // ] - // - // const result = await getMempoolEntryBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.isString(result.error) - // assert.equal(result.error, "Transaction not in mempool") - // }) - // } - // }) - // - // describe("getMempoolAncestorsSingle()", () => { - // // block route handler. - // const getMempoolAncestorsSingle = - // blockchainRoute.testableComponents.getMempoolAncestorsSingle - // - // it("should throw 400 if txid is empty", async () => { - // const result = await getMempoolAncestorsSingle(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "txid can not be empty") - // }) - // - // 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/" - // - // req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getMempoolAncestorsSingle(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 /getMempoolAncestorsSingle", async () => { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .reply(200, { result: mockData.mockAncestors }) - // - // req.params.txid = `bb0d349892d351da2767f8c45f6f7949713ff09bd12838d53e76158ddee3ce93` - // - // const result = await getMempoolAncestorsSingle(req, res) - // // console.log(`result: ${util.inspect(result)}`) - // - // assert.isArray(result) - // }) - // }) - // - // describe("getTxOut()", () => { - // // block route handler. - // const getTxOut = blockchainRoute.testableComponents.getTxOut - // - // it("should throw 400 if txid is empty", async () => { - // const result = await getTxOut(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "txid can not be empty") - // }) - // - // it("should throw 400 if n is empty", async () => { - // req.params.txid = `sometxid` - // const result = await getTxOut(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "n can not be empty") - // }) - // - // 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/" - // - // req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // req.params.n = 0 - // - // const result = await getTxOut(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" - // ) - // }) - // - // // This test can only run for unit tests. See TODO at the top of this file. - // it("should GET /getTxOut", 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.mockTxOut }) - // } - // - // req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` - // req.params.n = 0 - // req.query.include_mempool = "true" - // - // const result = await getTxOut(req, res) - // // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.hasAllKeys(result, [ - // "bestblock", - // "confirmations", - // "value", - // "scriptPubKey", - // "coinbase" - // ]) - // assert.hasAllKeys(result.scriptPubKey, [ - // "asm", - // "hex", - // "reqSigs", - // "type", - // "addresses" - // ]) - // assert.isArray(result.scriptPubKey.addresses) - // }) - // }) - // - // describe("getTxOutProof()", () => { - // const getTxOutProof = blockchainRoute.testableComponents.getTxOutProofSingle - // - // it("should throw 400 if txid is empty", async () => { - // const result = await getTxOutProof(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "txid can not be empty") - // }) - // - // 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/" - // - // req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getTxOutProof(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 /getTxOutProof", 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.mockTxOutProof }) - // } - // - // req.params.txid = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - // - // const result = await getTxOutProof(req, res) - // // console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isString(result) - // }) - // }) - // - // describe("#getTxOutProofBulk", () => { - // // route handler. - // const getTxOutProofBulk = - // blockchainRoute.testableComponents.getTxOutProofBulk - // - // it("should throw an error for an empty body", async () => { - // req.body = {} - // - // const result = await getTxOutProofBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "txids needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should error on non-array single txid", async () => { - // req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - // - // const result = await getTxOutProofBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "txids needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should throw 400 error if addresses array is too large", async () => { - // const testArray = [] - // for (var i = 0; i < 25; i++) testArray.push("") - // - // req.body.txids = testArray - // - // const result = await getTxOutProofBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "Array too large") - // }) - // - // it("should GET proof for single txid", 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.mockTxOutProof }) - // } - // - // req.body.txids = [ - // `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - // ] - // - // const result = await getTxOutProofBulk(req, res) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.isString(result[0]) - // }) - // - // it("should GET proof for multiple txids", async () => { - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .times(2) - // .reply(200, { result: mockData.mockTxOutProof }) - // } - // - // req.body.txids = [ - // `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266`, - // `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - // ] - // - // const result = await getTxOutProofBulk(req, res) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.equal(result.length, 2, "Correct length of returned array") - // }) - // }) - // - // describe("verifyTxOutProof()", () => { - // const verifyTxOutProof = - // blockchainRoute.testableComponents.verifyTxOutProofSingle - // - // it("should throw 400 if proof is empty", async () => { - // const result = await verifyTxOutProof(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "proof can not be empty") - // }) - // - // 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/" - // - // req.params.proof = mockData.mockTxOutProof - // - // const result = await verifyTxOutProof(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 /verifyTxOutProof", async () => { - // const expected = - // "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" - // - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .reply(200, { result: [expected] }) - // } - // - // req.params.proof = - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" - // - // const result = await verifyTxOutProof(req, res) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.isString(result[0]) - // assert.equal(result[0], expected) - // }) - // }) - // - // describe("#verifyTxOutProofBulk", () => { - // // route handler. - // const verifyTxOutProofBulk = - // blockchainRoute.testableComponents.verifyTxOutProofBulk - // - // it("should throw an error for an empty body", async () => { - // req.body = {} - // - // const result = await verifyTxOutProofBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "proofs needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should error on non-array single txid", async () => { - // req.body.proofs = mockData.mockTxOutProof - // - // const result = await verifyTxOutProofBulk(req, res) - // - // assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - // assert.include( - // result.error, - // "proofs needs to be an array", - // "Proper error message" - // ) - // }) - // - // it("should throw 400 error if addresses array is too large", async () => { - // const testArray = [] - // for (var i = 0; i < 25; i++) testArray.push("") - // - // req.body.proofs = testArray - // - // const result = await verifyTxOutProofBulk(req, res) - // //console.log(`result: ${util.inspect(result)}`) - // - // assert.hasAllKeys(result, ["error"]) - // assert.include(result.error, "Array too large") - // }) - // - // it("should get single proof", async () => { - // const expected = - // "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" - // - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .reply(200, { result: [expected] }) - // } - // - // req.body.proofs = [ - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" - // ] - // - // const result = await verifyTxOutProofBulk(req, res) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.isString(result[0]) - // assert.equal(result[0], expected) - // }) - // - // it("should get multiple proofs", async () => { - // const expected = - // "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" - // - // // Mock the RPC call for unit tests. - // if (process.env.TEST === "unit") { - // nock(`${process.env.RPC_BASEURL}`) - // .post(uri => uri.includes("/")) - // .times(2) - // .reply(200, { result: [expected] }) - // } - // - // req.body.proofs = [ - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700", - // "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" - // ] - // - // const result = await verifyTxOutProofBulk(req, res) - // //console.log(`result: ${JSON.stringify(result, null, 2)}`) - // - // assert.isArray(result) - // assert.isString(result[0]) - // assert.equal(result[0], expected) - // assert.equal(result.length, 2) - // }) - // }) + const result = await uut.getTxOutProofBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "txids needs to be an array", + "Proper error message" + ) + }) + + it("should error on non-array single txid", async () => { + req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` + + const result = await uut.getTxOutProofBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "txids needs to be an array", + "Proper error message" + ) + }) + + it("should throw 400 error if addresses array is too large", async () => { + const testArray = [] + for (var i = 0; i < 25; i++) testArray.push("") + + req.body.txids = testArray + + const result = await uut.getTxOutProofBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "Array too large") + }) + it("returns proper error when downstream service stalls", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) + + req.body.txids = [ + `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` + ] + + const result = await uut.getTxOutProofBulk(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" }) + + req.body.txids = [ + `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` + ] + + const result = await uut.getTxOutProofBulk(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 proof for single txid", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockTxOutProof } }) + } + + req.body.txids = [ + `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` + ] + + const result = await uut.getTxOutProofBulk(req, res) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + }) + + it("should GET proof for multiple txids", async () => { + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: mockData.mockTxOutProof } }) + } + + req.body.txids = [ + `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266`, + `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` + ] + + const result = await uut.getTxOutProofBulk(req, res) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.equal(result.length, 2, "Correct length of returned array") + }) + }) + describe("verifyTxOutProofSingle()", () => { + it("should throw 400 if proof is empty", async () => { + const result = await uut.verifyTxOutProofSingle(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "proof can not be empty") + }) + + 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/" + + req.params.proof = mockData.mockTxOutProof + + const result = await uut.verifyTxOutProofSingle(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" }) + + req.params.proof = + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + + const result = await uut.verifyTxOutProofSingle(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" }) + + req.params.proof = + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + + const result = await uut.verifyTxOutProofSingle(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 /verifyTxOutProof", async () => { + const expected = + "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: [expected] } }) + } + + req.params.proof = + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + + const result = await uut.verifyTxOutProofSingle(req, res) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + assert.equal(result[0], expected) + }) + }) + describe("#verifyTxOutProofBulk", () => { + it("should throw an error for an empty body", async () => { + req.body = {} + + const result = await uut.verifyTxOutProofBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "proofs needs to be an array", + "Proper error message" + ) + }) + + it("should error on non-array single txid", async () => { + req.body.proofs = mockData.mockTxOutProof + + const result = await uut.verifyTxOutProofBulk(req, res) + + assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") + assert.include( + result.error, + "proofs needs to be an array", + "Proper error message" + ) + }) + + it("should throw 400 error if addresses array is too large", async () => { + const testArray = [] + for (var i = 0; i < 25; i++) testArray.push("") + + req.body.proofs = testArray + + const result = await uut.verifyTxOutProofBulk(req, res) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAllKeys(result, ["error"]) + assert.include(result.error, "Array too large") + }) + + it("returns proper error when downstream service stalls", async () => { + // Mock the timeout error. + sandbox.stub(uut.axios, "request").throws({ code: "ECONNABORTED" }) + + req.body.proofs = [ + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + ] + const result = await uut.verifyTxOutProofBulk(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" }) + + req.body.proofs = [ + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + ] + const result = await uut.verifyTxOutProofBulk(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 single proof", async () => { + const expected = + "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: [expected] } }) + } + + req.body.proofs = [ + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + ] + + const result = await uut.verifyTxOutProofBulk(req, res) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + assert.equal(result[0], expected) + }) + + it("should get multiple proofs", async () => { + const expected = + "2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266" + + // Mock the RPC call for unit tests. + if (process.env.TEST === "unit") { + sandbox + .stub(uut.axios, "request") + .resolves({ data: { result: [expected] } }) + } + + req.body.proofs = [ + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700", + "000000202ed2723e7590e2b937f6821a99d6764cb8799bf30f8e300000000000000000001d311c02df9a1e3f57b8dbdcf97ec8dbc3109a26779724c63e560b29ad9ea501e2af955d286403183049e39c1e000000067139f230701a2819a76795564bd2f67ded7eeae68596f368eddb3dd5bc54e59320e896f71d61cfc3ae3d4a90fca08b1aa35ba91256d8939d2cad11e638c0081f66724abdef55cf7b8b9fed064bce0369171434f8b289c1330ccef765f8e97a2c0d794d81aafb535855f7daa6bb51e40f77c6b59d7af7f62d0eb726a4fc4df82353d56fcbda7c7ea6bd935d61af8fb3b295637e6f323b10231135b3f10a034cfb238f635830c0595e52c6c31247cf677b555f7a287076e20cd0e1d3cc9af7260f02b700" + ] + + const result = await uut.verifyTxOutProofBulk(req, res) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + assert.equal(result[0], expected) + assert.equal(result.length, 2) + }) + }) })