From 056fcc274848e250cbcad334042265b2dfdf8c6a Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 12:17:19 -0800 Subject: [PATCH 1/5] Fixed integration tests --- package.json | 2 +- test/integration/address.js | 12 ++++--- test/integration/other/README.md | 8 +++++ test/integration/transaction.js | 2 +- test/integration/util.js | 6 +--- test/integration/z9-rate-limits.js | 53 ------------------------------ 6 files changed, 19 insertions(+), 64 deletions(-) create mode 100644 test/integration/other/README.md delete mode 100644 test/integration/z9-rate-limits.js diff --git a/package.json b/package.json index ec29264d..d581950f 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "node-mocks-http": "^1.7.0", "nyc": "^14.1.1", "prettier": "^1.14.2", - "semantic-release": "^15.13.3", + "semantic-release": "^15.13.31", "sinon": "^4.5.0", "source-map-support": "^0.5.12", "ts-node": "^8.1.0", diff --git a/test/integration/address.js b/test/integration/address.js index 0c61d1ae..2536f17c 100644 --- a/test/integration/address.js +++ b/test/integration/address.js @@ -130,7 +130,8 @@ describe(`#address`, () => { "legacyAddress", "cashAddress", "scriptPubKey", - "slpAddress" + "slpAddress", + "asm" ]) assert.isArray(result.utxos) assert.hasAnyKeys(result.utxos[0], [ @@ -158,7 +159,8 @@ describe(`#address`, () => { "legacyAddress", "cashAddress", "scriptPubKey", - "slpAddress" + "slpAddress", + "asm" ]) assert.isArray(result[0].utxos) assert.hasAnyKeys(result[0].utxos[0], [ @@ -215,7 +217,8 @@ describe(`#address`, () => { "legacyAddress", "cashAddress", "scriptPubKey", - "slpAddress" + "slpAddress", + "asm" ]) assert.isArray(result.utxos) }) @@ -235,7 +238,8 @@ describe(`#address`, () => { "legacyAddress", "cashAddress", "scriptPubKey", - "slpAddress" + "slpAddress", + "asm" ]) assert.isArray(result[0].utxos) }) diff --git a/test/integration/other/README.md b/test/integration/other/README.md new file mode 100644 index 00000000..5c8068af --- /dev/null +++ b/test/integration/other/README.md @@ -0,0 +1,8 @@ +# Other Integration Tests + +This directory holds stand-alone integration tests that should not be part of +the integration test suite. + +An example of a test that fits this criteria are rate limit tests. Rate limit +tests are complex, require a solid internet connection, and can easily disrupt +other tests. For those reasons, it is better to run them on their own. diff --git a/test/integration/transaction.js b/test/integration/transaction.js index d36476f3..94b086b9 100644 --- a/test/integration/transaction.js +++ b/test/integration/transaction.js @@ -87,7 +87,7 @@ describe(`#Transaction`, () => { // console.log(`result: ${util.inspect(result)}`) assert.equal(false, false, "Unexpected result!") } catch (err) { - console.log(`err: ${util.inspect(err)}`) + // console.log(`err: ${util.inspect(err)}`) assert.hasAnyKeys(err, ["error"]) assert.include(err.error, "Array too large") diff --git a/test/integration/util.js b/test/integration/util.js index 964fe3b7..e2af5e2c 100644 --- a/test/integration/util.js +++ b/test/integration/util.js @@ -44,7 +44,7 @@ describe(`#util`, () => { assert.equal(result.isvalid, false) }) - it(`should return validate valid address`, async () => { + it(`should return a valid address`, async () => { const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z` const result = await bitbox.Util.validateAddress(address) @@ -54,8 +54,6 @@ describe(`#util`, () => { "isvalid", "address", "scriptPubKey", - "ismine", - "iswatchonly", "isscript" ]) assert.equal(result.isvalid, true) @@ -75,8 +73,6 @@ describe(`#util`, () => { "isvalid", "address", "scriptPubKey", - "ismine", - "iswatchonly", "isscript" ]) }) diff --git a/test/integration/z9-rate-limits.js b/test/integration/z9-rate-limits.js deleted file mode 100644 index 6e9c4653..00000000 --- a/test/integration/z9-rate-limits.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - This test file should be run last as it will trigger the rate limits in - rest.bitcoin.com and no more calls to rest will be possible for about 1 minute - after this test is run. -*/ - -const chai = require("chai") -const assert = chai.assert - -const BITBOX = require("../../lib/BITBOX").BITBOX -let bitbox = new BITBOX() - -if (process.env.SERVER === "local") - bitbox = new BITBOX({ restURL: "http://localhost:3000/v2/" }) -if (process.env.SERVER === "stage") - bitbox = new BITBOX({ restURL: "https://rest.btctest.net/v2/" }) - -// Inspect utility used for debugging. -const util = require("util") -util.inspect.defaultOptions = { - showHidden: true, - colors: true, - depth: 3 -} - -describe("#rate-limits", () => { - it("should throw an error when RPM rate limits are triggered", async () => { - try { - const promises = [] - for (let i = 0; i < 300; i++) { - const promise = bitbox.Control.getInfo() - promises.push(promise) - } - - const temp = await Promise.all(promises) - - // console.log(`temp: ${util.inspect(temp)}`) - assert.equal(true, false, "Unexpected result!") - } catch (err) { - // Expected error response - if (err.error) { - assert.hasAnyKeys(err, ["error"]) - assert.include(err.error, "Too many requests") - - // Handle other types of error response. - } else { - console.log(`Unexpected error:`) - // console.log(`err: ${util.inspect(err)}`) - assert.equal(true, false, "Unexpected error") - } - } - }) -}) From c158bcb7c64d20db2d6e3630f1d1e90f3968b6a8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 12:47:57 -0800 Subject: [PATCH 2/5] Fixed gettxout and added unit tests --- lib/Blockchain.ts | 13 ++++++- package.json | 2 +- test/unit/Blockchain.ts | 55 ++++++++++++++++++++++++++++-- test/unit/mocks/blockchain-mock.js | 18 +++++++++- 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/lib/Blockchain.ts b/lib/Blockchain.ts index 5589e7ad..386cddc2 100644 --- a/lib/Blockchain.ts +++ b/lib/Blockchain.ts @@ -239,17 +239,28 @@ export class Blockchain { } } + // Returns details about an unspent transaction output. public async getTxOut( txid: string, n: any, include_mempool: boolean = true ): Promise { + + // Input validation + if (typeof txid !== "string" || txid.length !== 64) + throw new Error(`txid needs to be a proper transaction ID`) + + if (isNaN(n)) throw new Error(`n must be an integer`) + + if (typeof include_mempool !== "boolean") + throw new Error(`include_mempool input must be of type boolean`) + // TODO confirm this works try { const response: AxiosResponse = await axios.get( `${ this.restURL - }blockchain/getTxOut/${txid}/n?include_mempool=${include_mempool}` + }blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}` ) return response.data } catch (error) { diff --git a/package.json b/package.json index d581950f..ee99c4ff 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "npm run test:unit", "test:no-build": "nyc mocha test/unit --timeout 60000 --exit", - "test:unit": "tsc && nyc mocha test/unit --timeout 60000 --exit", + "test:unit": "tsc && nyc mocha test/unit/Blockchain.js --timeout 60000 --exit", "test:integration": "TEST=integration nyc --reporter=text mocha --timeout 30000 test/integration --exit", "test:integration:local": "TEST=integration SERVER=local nyc --reporter=text mocha --timeout 30000 test/integration --exit", "test:integration:stage": "TEST=integration SERVER=stage nyc --reporter=text mocha --timeout 30000 test/integration --exit", diff --git a/test/unit/Blockchain.ts b/test/unit/Blockchain.ts index dcb9f465..96e0cd80 100644 --- a/test/unit/Blockchain.ts +++ b/test/unit/Blockchain.ts @@ -8,6 +8,8 @@ import { REST_URL } from "../../lib/BITBOX" import * as util from "util" import { BlockHeaderResult } from "bitcoin-com-rest" +const mockData = require("./mocks/blockchain-mock") + // consts const bitbox: BITBOX = new BITBOX() const assert: Chai.AssertStatic = chai.assert @@ -347,14 +349,13 @@ describe("#Blockchain", (): void => { }) describe("#getTxOut", (): void => { - // TODO finish this test let sandbox: any beforeEach(() => (sandbox = sinon.sandbox.create())) afterEach(() => sandbox.restore()) const data = { result: {} } - +/* it("should get TODO", done => { const resolved = new Promise(r => r({ data: data })) sandbox.stub(axios, "get").returns(resolved) @@ -369,6 +370,56 @@ describe("#Blockchain", (): void => { }) .then(done, done) }) +*/ + it("should throw an error for improper txid.", async () => { + try { + await bitbox.Blockchain.getTxOut("badtxid", 0) + } catch (err) { + assert.include(err.message, "txid needs to be a proper transaction ID") + } + }) + + it("should throw an error if vout is not an integer.", async () => { + try { + await bitbox.Blockchain.getTxOut( + "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", 'a' + ) + } catch (err) { + assert.include(err.message, "n must be an integer") + } + }) + + it("should get information on an unspent tx", async () => { + sandbox.stub(axios, "get").resolves({ data: mockData.txOutUnspent }) + + const result = await bitbox.Blockchain.getTxOut( + "62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8", + 0, + true + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "bestblock", + "confirmations", + "value", + "scriptPubKey", + "coinbase" + ]) + }) + + // it("should get information on a spent tx", async () => { + // sandbox.stub(axios, "get").resolves({ data: null }) + + // const result = await bitbox.Blockchain.getTxOut( + // "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", + // 0, + // true + // ) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // assert2.equal(result, null) + // }) }) describe("#preciousBlock", (): void => { diff --git a/test/unit/mocks/blockchain-mock.js b/test/unit/mocks/blockchain-mock.js index 1e73c0b4..17b7c43a 100644 --- a/test/unit/mocks/blockchain-mock.js +++ b/test/unit/mocks/blockchain-mock.js @@ -31,5 +31,21 @@ module.exports = { "0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01", verifiedProof: - "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7", + + txOutUnspent: { + bestblock: + "000000000000000000b441e02f5b1b9f5b3def961047afcc6f2f5636c952705e", + confirmations: 2, + value: 0.00006, + scriptPubKey: { + asm: + "OP_DUP OP_HASH160 d19fae66b685f5c3633c0db0600313918347225f OP_EQUALVERIFY OP_CHECKSIG", + hex: "76a914d19fae66b685f5c3633c0db0600313918347225f88ac", + reqSigs: 1, + type: "pubkeyhash", + addresses: ["bitcoincash:qrgeltnxk6zltsmr8sxmqcqrzwgcx3eztusrwgf0x3"] + }, + coinbase: false + } } From 8b5ed526ab9d100270d04889055767e260061327 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 13:46:36 -0800 Subject: [PATCH 3/5] fix(getTxOut): Fixed getTxOut for checking utxos --- lib/Blockchain.ts | 222 ++++++++++++++++----------------- package.json | 2 +- test/integration/blockchain.js | 37 +++++- test/unit/Blockchain.ts | 15 --- 4 files changed, 143 insertions(+), 133 deletions(-) diff --git a/lib/Blockchain.ts b/lib/Blockchain.ts index 386cddc2..a5719465 100644 --- a/lib/Blockchain.ts +++ b/lib/Blockchain.ts @@ -3,7 +3,7 @@ - Add blockhash functionality back into getTxOutProof */ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios" +import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; import { BlockchainInfoResult, BlockDetailsResult, @@ -12,24 +12,24 @@ import { MempoolEntryResult, MempoolInfoResult, TxOutResult -} from "bitcoin-com-rest" -import { REST_URL } from "./BITBOX" +} from "bitcoin-com-rest"; +import { REST_URL } from "./BITBOX"; export class Blockchain { - public restURL: string + public restURL: string; constructor(restURL: string = REST_URL) { - this.restURL = restURL + this.restURL = restURL; } public async getBestBlockHash(): Promise { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBestBlockHash` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -40,11 +40,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -52,11 +52,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockchainInfo` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -64,11 +64,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockCount` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -78,11 +78,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockHash/${height}` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -95,9 +95,9 @@ export class Blockchain { if (typeof hash === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}` - ) + ); - return response.data + return response.data; // Handle array of hashes. } else if (Array.isArray(hash)) { @@ -108,15 +108,15 @@ export class Blockchain { hashes: hash, verbose: verbose } - ) + ); - return response.data + return response.data; } - throw new Error(`Input hash must be a string or array of strings.`) + throw new Error(`Input hash must be a string or array of strings.`); } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -124,11 +124,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getChainTips` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -136,11 +136,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getDifficulty` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -149,18 +149,16 @@ export class Blockchain { txid: string, verbose: boolean = false ): Promise { - if (typeof txid !== "string") txid = JSON.stringify(txid) + if (typeof txid !== "string") txid = JSON.stringify(txid); try { const response: AxiosResponse = await axios.get( - `${ - this.restURL - }blockchain/getMempoolAncestors/${txid}?verbose=${verbose}` - ) - return response.data + `${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}` + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -169,18 +167,16 @@ export class Blockchain { txid: string, verbose: boolean = false ): Promise { - if (typeof txid !== "string") txid = JSON.stringify(txid) + if (typeof txid !== "string") txid = JSON.stringify(txid); try { const response: AxiosResponse = await axios.get( - `${ - this.restURL - }blockchain/getMempoolDescendants/${txid}?verbose=${verbose}` - ) - return response.data + `${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}` + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -191,9 +187,9 @@ export class Blockchain { if (typeof txid === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolEntry/${txid}` - ) + ); - return response.data + return response.data; } else if (Array.isArray(txid)) { const options: AxiosRequestConfig = { method: "POST", @@ -201,16 +197,16 @@ export class Blockchain { data: { txids: txid } - } - const response: AxiosResponse = await axios(options) + }; + const response: AxiosResponse = await axios(options); - return response.data + return response.data; } - throw new Error(`Input must be a string or array of strings.`) + throw new Error(`Input must be a string or array of strings.`); } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -218,11 +214,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolInfo` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -231,11 +227,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getRawMempool?vebose=${verbose}` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -245,27 +241,25 @@ export class Blockchain { n: any, include_mempool: boolean = true ): Promise { - // Input validation if (typeof txid !== "string" || txid.length !== 64) - throw new Error(`txid needs to be a proper transaction ID`) + throw new Error(`txid needs to be a proper transaction ID`); - if (isNaN(n)) throw new Error(`n must be an integer`) + if (isNaN(n)) throw new Error(`n must be an integer`); if (typeof include_mempool !== "boolean") - throw new Error(`include_mempool input must be of type boolean`) + throw new Error(`include_mempool input must be of type boolean`); - // TODO confirm this works try { - const response: AxiosResponse = await axios.get( - `${ - this.restURL - }blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}` - ) - return response.data + const path: string = `${this.restURL}blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}`; + // console.log(`path: ${path}`) + + const response: AxiosResponse = await axios.get(path); + + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -275,11 +269,11 @@ export class Blockchain { try { // Single txid. if (typeof txids === "string") { - const path = `${this.restURL}blockchain/getTxOutProof/${txids}` + const path = `${this.restURL}blockchain/getTxOutProof/${txids}`; //if (blockhash) path = `${path}?blockhash=${blockhash}` - const response: AxiosResponse = await axios.get(path) - return response.data + const response: AxiosResponse = await axios.get(path); + return response.data; // Array of txids. } else if (Array.isArray(txids)) { @@ -289,15 +283,15 @@ export class Blockchain { { txids: txids } - ) + ); - return response.data + return response.data; } - throw new Error(`Input must be a string or array of strings.`) + throw new Error(`Input must be a string or array of strings.`); } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -306,11 +300,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/preciousBlock/${blockhash}` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -319,11 +313,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.post( `${this.restURL}blockchain/pruneBlockchain/${height}` - ) - return response.data + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -333,14 +327,12 @@ export class Blockchain { ): Promise { try { const response: AxiosResponse = await axios.get( - `${ - this.restURL - }blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}` - ) - return response.data + `${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}` + ); + return response.data; } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } @@ -350,8 +342,8 @@ export class Blockchain { if (typeof proof === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/verifyTxOutProof/${proof}` - ) - return response.data + ); + return response.data; // Array of hashes. } else if (Array.isArray(proof)) { @@ -361,15 +353,15 @@ export class Blockchain { { proofs: proof } - ) + ); - return response.data + return response.data; } - throw new Error(`Input must be a string or array of strings.`) + throw new Error(`Input must be a string or array of strings.`); } catch (error) { - if (error.response && error.response.data) throw error.response.data - else throw error + if (error.response && error.response.data) throw error.response.data; + else throw error; } } } diff --git a/package.json b/package.json index ee99c4ff..d581950f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "test": "npm run test:unit", "test:no-build": "nyc mocha test/unit --timeout 60000 --exit", - "test:unit": "tsc && nyc mocha test/unit/Blockchain.js --timeout 60000 --exit", + "test:unit": "tsc && nyc mocha test/unit --timeout 60000 --exit", "test:integration": "TEST=integration nyc --reporter=text mocha --timeout 30000 test/integration --exit", "test:integration:local": "TEST=integration SERVER=local nyc --reporter=text mocha --timeout 30000 test/integration --exit", "test:integration:stage": "TEST=integration SERVER=stage nyc --reporter=text mocha --timeout 30000 test/integration --exit", diff --git a/test/integration/blockchain.js b/test/integration/blockchain.js index 7b401466..2d61c692 100644 --- a/test/integration/blockchain.js +++ b/test/integration/blockchain.js @@ -205,13 +205,46 @@ describe(`#blockchain`, () => { }) }) + describe(`#getTxOut`, () => { + it(`should get information on valid utxo`, async () => { + const txid = `91874bf385a36d54f06c2154b34bce887a03b99540bfddaa17ac78ebc65202d0` + + const result = await bitbox.Blockchain.getTxOut(txid, 0, true) + // 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" + ]) + }) + + it(`should return null for a spent utxo`, async () => { + const txid = `8db6dd4f8a5bb1308541d4a6d4ecdae6c65426679e79f783638ce32b2fb0725b` + + const result = await bitbox.Blockchain.getTxOut(txid, 0, true) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, null) + }) + }) + describe(`#getTxOutProof`, () => { it(`should get single tx out proof`, async () => { const txid = "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" const result = await bitbox.Blockchain.getTxOutProof(txid) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.isString(result) }) @@ -223,7 +256,7 @@ describe(`#blockchain`, () => { ] const result = await bitbox.Blockchain.getTxOutProof(txid) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.isArray(result) assert.isString(result[0]) diff --git a/test/unit/Blockchain.ts b/test/unit/Blockchain.ts index 96e0cd80..0b5b545f 100644 --- a/test/unit/Blockchain.ts +++ b/test/unit/Blockchain.ts @@ -355,22 +355,7 @@ describe("#Blockchain", (): void => { const data = { result: {} } -/* - it("should get TODO", done => { - const resolved = new Promise(r => r({ data: data })) - sandbox.stub(axios, "get").returns(resolved) - bitbox.Blockchain.getTxOut( - "daf58932cb91619304dd4cbd03c7202e89ad7d6cbd6e2209e5f64ce3b6ed7c88", - 0, - true - ) - .then((result: any) => { - assert.deepEqual(data, result) - }) - .then(done, done) - }) -*/ it("should throw an error for improper txid.", async () => { try { await bitbox.Blockchain.getTxOut("badtxid", 0) From 3c9dfe42e9c500b89b72eb643ee3e443e327bbed Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 15:00:30 -0800 Subject: [PATCH 4/5] Adding pretterrc file to enforce linting rules in VS Code --- .prettierrc | 3 + lib/Blockchain.ts | 202 +++++++++++++++++++++++----------------------- package.json | 2 +- 3 files changed, 105 insertions(+), 102 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..dceda2d7 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "semi": false +} \ No newline at end of file diff --git a/lib/Blockchain.ts b/lib/Blockchain.ts index a5719465..b9415c7c 100644 --- a/lib/Blockchain.ts +++ b/lib/Blockchain.ts @@ -3,7 +3,7 @@ - Add blockhash functionality back into getTxOutProof */ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios"; +import axios, { AxiosRequestConfig, AxiosResponse } from "axios" import { BlockchainInfoResult, BlockDetailsResult, @@ -12,24 +12,24 @@ import { MempoolEntryResult, MempoolInfoResult, TxOutResult -} from "bitcoin-com-rest"; -import { REST_URL } from "./BITBOX"; +} from "bitcoin-com-rest" +import { REST_URL } from "./BITBOX" export class Blockchain { - public restURL: string; + public restURL: string constructor(restURL: string = REST_URL) { - this.restURL = restURL; + this.restURL = restURL } public async getBestBlockHash(): Promise { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBestBlockHash` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -40,11 +40,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -52,11 +52,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockchainInfo` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -64,11 +64,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockCount` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -78,11 +78,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockHash/${height}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -95,9 +95,9 @@ export class Blockchain { if (typeof hash === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}` - ); + ) - return response.data; + return response.data // Handle array of hashes. } else if (Array.isArray(hash)) { @@ -108,15 +108,15 @@ export class Blockchain { hashes: hash, verbose: verbose } - ); + ) - return response.data; + return response.data } - throw new Error(`Input hash must be a string or array of strings.`); + throw new Error(`Input hash must be a string or array of strings.`) } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -124,11 +124,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getChainTips` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -136,11 +136,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getDifficulty` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -149,16 +149,16 @@ export class Blockchain { txid: string, verbose: boolean = false ): Promise { - if (typeof txid !== "string") txid = JSON.stringify(txid); + if (typeof txid !== "string") txid = JSON.stringify(txid) try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -167,16 +167,16 @@ export class Blockchain { txid: string, verbose: boolean = false ): Promise { - if (typeof txid !== "string") txid = JSON.stringify(txid); + if (typeof txid !== "string") txid = JSON.stringify(txid) try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -187,9 +187,9 @@ export class Blockchain { if (typeof txid === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolEntry/${txid}` - ); + ) - return response.data; + return response.data } else if (Array.isArray(txid)) { const options: AxiosRequestConfig = { method: "POST", @@ -197,16 +197,16 @@ export class Blockchain { data: { txids: txid } - }; - const response: AxiosResponse = await axios(options); + } + const response: AxiosResponse = await axios(options) - return response.data; + return response.data } - throw new Error(`Input must be a string or array of strings.`); + throw new Error(`Input must be a string or array of strings.`) } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -214,11 +214,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getMempoolInfo` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -227,11 +227,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/getRawMempool?vebose=${verbose}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -243,23 +243,23 @@ export class Blockchain { ): Promise { // Input validation if (typeof txid !== "string" || txid.length !== 64) - throw new Error(`txid needs to be a proper transaction ID`); + throw new Error(`txid needs to be a proper transaction ID`) - if (isNaN(n)) throw new Error(`n must be an integer`); + if (isNaN(n)) throw new Error(`n must be an integer`) if (typeof include_mempool !== "boolean") - throw new Error(`include_mempool input must be of type boolean`); + throw new Error(`include_mempool input must be of type boolean`) try { - const path: string = `${this.restURL}blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}`; + const path: string = `${this.restURL}blockchain/getTxOut/${txid}/${n}?include_mempool=${include_mempool}` // console.log(`path: ${path}`) - const response: AxiosResponse = await axios.get(path); + const response: AxiosResponse = await axios.get(path) - return response.data; + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -269,11 +269,11 @@ export class Blockchain { try { // Single txid. if (typeof txids === "string") { - const path = `${this.restURL}blockchain/getTxOutProof/${txids}`; + const path = `${this.restURL}blockchain/getTxOutProof/${txids}` //if (blockhash) path = `${path}?blockhash=${blockhash}` - const response: AxiosResponse = await axios.get(path); - return response.data; + const response: AxiosResponse = await axios.get(path) + return response.data // Array of txids. } else if (Array.isArray(txids)) { @@ -283,15 +283,15 @@ export class Blockchain { { txids: txids } - ); + ) - return response.data; + return response.data } - throw new Error(`Input must be a string or array of strings.`); + throw new Error(`Input must be a string or array of strings.`) } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -300,11 +300,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/preciousBlock/${blockhash}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -313,11 +313,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.post( `${this.restURL}blockchain/pruneBlockchain/${height}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -328,11 +328,11 @@ export class Blockchain { try { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}` - ); - return response.data; + ) + return response.data } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } @@ -342,8 +342,8 @@ export class Blockchain { if (typeof proof === "string") { const response: AxiosResponse = await axios.get( `${this.restURL}blockchain/verifyTxOutProof/${proof}` - ); - return response.data; + ) + return response.data // Array of hashes. } else if (Array.isArray(proof)) { @@ -353,15 +353,15 @@ export class Blockchain { { proofs: proof } - ); + ) - return response.data; + return response.data } - throw new Error(`Input must be a string or array of strings.`); + throw new Error(`Input must be a string or array of strings.`) } catch (error) { - if (error.response && error.response.data) throw error.response.data; - else throw error; + if (error.response && error.response.data) throw error.response.data + else throw error } } } diff --git a/package.json b/package.json index d581950f..5bdde140 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "@types/wif": "^2.0.1", "chai": "^4.1.2", "coveralls": "^3.0.2", - "eslint": "^5.5.0", + "eslint": "^5.16.0", "eslint-config-prettier": "^3.0.1", "eslint-plugin-node": "7.0.1", "eslint-plugin-prettier": "^2.6.2", From d368eb938545a0e43acc5fb39dabed6cdbc3898a Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 15:04:29 -0800 Subject: [PATCH 5/5] Adding additional unit test for gettxout --- test/unit/Blockchain.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/unit/Blockchain.ts b/test/unit/Blockchain.ts index 0b5b545f..4ce9f2fa 100644 --- a/test/unit/Blockchain.ts +++ b/test/unit/Blockchain.ts @@ -393,18 +393,18 @@ describe("#Blockchain", (): void => { ]) }) - // it("should get information on a spent tx", async () => { - // sandbox.stub(axios, "get").resolves({ data: null }) - - // const result = await bitbox.Blockchain.getTxOut( - // "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", - // 0, - // true - // ) - // // console.log(`result: ${JSON.stringify(result, null, 2)}`) - - // assert2.equal(result, null) - // }) + it("should get information on a spent tx", async () => { + sandbox.stub(axios, "get").resolves({ data: null }) + + const result = await bitbox.Blockchain.getTxOut( + "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", + 0, + true + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, null) + }) }) describe("#preciousBlock", (): void => {