diff --git a/test/integration/bchn/blockbook.js b/test/integration/bchn/blockbook.js new file mode 100644 index 0000000..904ec83 --- /dev/null +++ b/test/integration/bchn/blockbook.js @@ -0,0 +1,170 @@ +/* + Integration tests for the Blockbook library. + +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() +//const axios = require("axios") + +// Inspect utility used for debugging. +const util = require("util") +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 3 +} + +describe(`#Blockbook`, () => { + describe(`#Balance`, () => { + it(`should GET balance for a single address`, async () => { + const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" + + const result = await bchjs.Blockbook.balance(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.hasAnyKeys(result, [ + "page", + "totalPages", + "itemsOnPage", + "address", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxs", + "txs", + "txids" + ]) + assert.isArray(result.txids) + }) + + it(`should POST request balances for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Blockbook.balance(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "page", + "totalPages", + "itemsOnPage", + "address", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxs", + "txs", + "txids" + ]) + assert.isArray(result[0].txids) + }) + + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Blockbook.balance(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input address must be a string or array of strings` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Blockbook.balance(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#utxo`, () => { + it(`should GET utxos for a single address`, async () => { + const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7" + + const result = await bchjs.Blockbook.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "txid", + "vout", + "value", + "height", + "confirmations" + ]) + }) + + it(`should POST utxo details for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", + "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" + ] + + const result = await bchjs.Blockbook.utxo(addr) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isArray(result[0]) + assert.hasAnyKeys(result[0][0], [ + "txid", + "vout", + "value", + "height", + "confirmations" + ]) + }) + + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.Blockbook.utxo(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input address must be a string or array of strings` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr") + + const result = await bchjs.Blockbook.utxo(addr) + //console.log(`result: ${util.inspect(result)}`) + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) +}) diff --git a/test/integration/bchn/blockchain.js b/test/integration/bchn/blockchain.js new file mode 100644 index 0000000..bfe2d68 --- /dev/null +++ b/test/integration/bchn/blockchain.js @@ -0,0 +1,331 @@ +/* + Integration tests for the bchjs. Only covers calls made to + rest.bitcoin.com. + + TODO + - getMempoolEntry() only works on TXs in the mempool, so it needs to be part + of an e2e test to be properly tested. +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +// Inspect utility used for debugging. +const util = require("util") +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 3 +} + +describe(`#blockchain`, () => { + describe(`#getBestBlockHash`, () => { + it(`should GET best block hash`, async () => { + const result = await bchjs.Blockchain.getBestBlockHash() + //console.log(`result: ${util.inspect(result)}`) + + assert.isString(result) + assert.equal(result.length, 64, "Specific hash length") + }) + }) + + describe("#getBlockHeader", () => { + it(`should GET block header for a single hash`, async () => { + const hash = + "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" + + const result = await bchjs.Blockchain.getBlockHeader(hash) + + assert.hasAllKeys(result, [ + "hash", + "confirmations", + "height", + "version", + "versionHex", + "merkleroot", + "time", + "mediantime", + "nonce", + "bits", + "difficulty", + "chainwork", + "previousblockhash", + "nextblockhash", + "nTx" + ]) + }) + + it(`should GET block headers for an array of hashes`, async () => { + const hash = [ + "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201", + "00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3" + ] + + const result = await bchjs.Blockchain.getBlockHeader(hash) + + assert.isArray(result) + assert.hasAllKeys(result[0], [ + "hash", + "confirmations", + "height", + "version", + "versionHex", + "merkleroot", + "time", + "mediantime", + "nonce", + "bits", + "difficulty", + "chainwork", + "previousblockhash", + "nextblockhash", + "nTx" + ]) + }) + + it(`should throw an error for improper input`, async () => { + try { + const hash = 12345 + + await bchjs.Blockchain.getBlockHeader(hash) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.include( + err.message, + `Input hash must be a string or array of strings` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const data = [] + for (let i = 0; i < 25; i++) { + data.push( + "000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" + ) + } + + const result = await bchjs.Blockchain.getBlockHeader(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe("#getMempoolEntry", () => { + /* + // To run this test, the txid must be unconfirmed. + const txid = + "defea04c38ee00cf73ad402984714ed22dc0dd99b2ae5cb50d791d94343ba79b" + + it(`should GET single mempool entry`, async () => { + const result = await bchjs.Blockchain.getMempoolEntry(txid) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAnyKeys(result, [ + "size", + "fee", + "modifiedfee", + "time", + "height", + "startingpriority", + "currentpriority", + "descendantcount", + "descendantsize", + "descendantfees", + "ancestorcount", + "ancestorsize", + "ancestorfees", + "depends" + ]) + }) + + it(`should get an array of mempool entries`, async () => { + const result = await bchjs.Blockchain.getMempoolEntry([txid, txid]) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "size", + "fee", + "modifiedfee", + "time", + "height", + "startingpriority", + "currentpriority", + "descendantcount", + "descendantsize", + "descendantfees", + "ancestorcount", + "ancestorsize", + "ancestorfees", + "depends" + ]) + }) + */ + + it(`should throw an error if txid is not in mempool`, async () => { + try { + const txid = + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" + + await bchjs.Blockchain.getMempoolEntry(txid) + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: ${util.inspect(err)}`) + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, `Transaction not in mempool`) + } + }) + + it(`should throw an error for improper single input`, async () => { + try { + const txid = 12345 + + await bchjs.Blockchain.getMempoolEntry(txid) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.include( + err.message, + `Input must be a string or array of strings` + ) + } + }) + }) + + describe(`#getTxOutProof`, () => { + it(`should get single tx out proof`, async () => { + const txid = + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" + + const result = await bchjs.Blockchain.getTxOutProof(txid) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isString(result) + }) + + it(`should get an array of tx out proofs`, async () => { + const txid = [ + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7", + "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" + ] + + const result = await bchjs.Blockchain.getTxOutProof(txid) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + }) + + it(`should throw an error for improper single input`, async () => { + try { + const txid = 12345 + + await bchjs.Blockchain.getTxOutProof(txid) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.include( + err.message, + `Input must be a string or array of strings` + ) + } + }) + }) + + describe(`#verifyTxOutProof`, () => { + const mockTxOutProof = + "0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01" + + it(`should verify a single proof`, async () => { + const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + assert.equal( + result[0], + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" + ) + }) + + it(`should verify an array of proofs`, async () => { + const proofs = [mockTxOutProof, mockTxOutProof] + const result = await bchjs.Blockchain.verifyTxOutProof(proofs) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + assert.equal( + result[0], + "03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7" + ) + }) + + it(`should throw an error for improper single input`, async () => { + try { + const txid = 12345 + + await bchjs.Blockchain.verifyTxOutProof(txid) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.include( + err.message, + `Input must be a string or array of strings` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const data = [] + for (let i = 0; i < 25; i++) data.push(mockTxOutProof) + + const result = await bchjs.Blockchain.verifyTxOutProof(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe("#getTxOut", () => { + it("should get information on an unspent tx", async () => { + const result = await bchjs.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 () => { + const result = await bchjs.Blockchain.getTxOut( + "87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871", + 0, + true + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, null) + }) + }) +}) diff --git a/test/integration/bchn/control.js b/test/integration/bchn/control.js new file mode 100644 index 0000000..e3939e7 --- /dev/null +++ b/test/integration/bchn/control.js @@ -0,0 +1,19 @@ +/* + Integration tests for bchjs control library. +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +describe(`#control`, () => { + describe(`#getNetworkInfo`, () => { + it("should get info on the full node", async () => { + const result = await bchjs.Control.getNetworkInfo() + console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "version") + }) + }) +}) diff --git a/test/integration/bchn/electrumx.js b/test/integration/bchn/electrumx.js new file mode 100644 index 0000000..c635f9e --- /dev/null +++ b/test/integration/bchn/electrumx.js @@ -0,0 +1,356 @@ +const chai = require("chai") +const assert = chai.assert +const sinon = require("sinon") + +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +describe(`#ElectrumX`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#utxo`, () => { + it(`should GET utxos for a single address`, async () => { + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "height") + assert.property(result.utxos[0], "tx_hash") + assert.property(result.utxos[0], "tx_pos") + assert.property(result.utxos[0], "value") + }) + + it(`should POST request for UTXOs for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "utxos") + assert.isArray(result.utxos[0].utxos) + assert.property(result.utxos[0], "address") + + assert.property(result.utxos[0].utxos[0], "height") + assert.property(result.utxos[0].utxos[0], "tx_hash") + assert.property(result.utxos[0].utxos[0], "tx_pos") + assert.property(result.utxos[0].utxos[0], "value") + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Electrumx.utxo(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#balance`, () => { + it(`should GET balance for a single address`, async () => { + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Electrumx.balance(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "balance") + assert.property(result.balance, "confirmed") + assert.property(result.balance, "unconfirmed") + }) + + it(`should POST request for balances for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.balance(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "balances") + assert.isArray(result.balances) + + assert.property(result.balances[0], "address") + assert.property(result.balances[0], "balance") + assert.property(result.balances[0].balance, "confirmed") + assert.property(result.balances[0].balance, "unconfirmed") + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + const result = await bchjs.Electrumx.balance(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#transactions`, () => { + it(`should GET transaction history for a single address`, async () => { + const addr = "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + + const result = await bchjs.Electrumx.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + assert.property(result.transactions[0], "height") + assert.property(result.transactions[0], "tx_hash") + }) + + it(`should POST request for transaction history for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj", + "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v" + ] + + const result = await bchjs.Electrumx.transactions(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + + assert.property(result.transactions[0], "address") + assert.property(result.transactions[0], "transactions") + assert.isArray(result.transactions[0].transactions) + assert.property(result.transactions[0].transactions[0], "height") + assert.property(result.transactions[0].transactions[0], "tx_hash") + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0") + + const result = await bchjs.Electrumx.transactions(addr) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#unconfirmed`, () => { + // These tests won't work because unconfirmed transactions are transient in nature. + /* + it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => { + const addr = "bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv" + + const result = await bchjs.Electrumx.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "height") + assert.property(result.utxos[0], "tx_hash") + assert.property(result.utxos[0], "fee") + }) + + it(`should POST request for unconfirmed UTXOs (mempool) for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv", + "bitcoincash:qpyrtsl9msdu2klgfpcmn2v5r22w9rk24g8pal74ts" + ] + + const result = await bchjs.Electrumx.unconfirmed(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "utxos") + assert.isArray(result.utxos) + + assert.property(result.utxos[0], "utxos") + assert.isArray(result.utxos[0].utxos) + assert.property(result.utxos[0], "address") + + assert.property(result.utxos[0].utxos[0], "height") + assert.property(result.utxos[0].utxos[0], "tx_hash") + assert.property(result.utxos[0].utxos[0], "fee") + }) + */ + + it(`should throw error on array size rate limit`, async () => { + try { + const addr = [] + for (let i = 0; i < 25; i++) + addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf") + + await bchjs.Electrumx.unconfirmed(addr) + + // console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#blockHeader`, () => { + it(`should GET block headers for given height and count`, async () => { + const height = 42 + const count = 2 + + const result = await bchjs.Electrumx.blockHeader(height, count) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "headers") + assert.isArray(result.headers) + assert.equal(result.headers.length, 2) + }) + + it(`should GET block headers for given height with default count = 1`, async () => { + const height = 42 + + const result = await bchjs.Electrumx.blockHeader(height) + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "headers") + assert.isArray(result.headers) + assert.equal(result.headers.length, 1) + }) + }) + + describe(`#txData`, () => { + it(`should GET details for a single transaction`, async () => { + const txid = + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + + const result = await bchjs.Electrumx.txData(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "details") + assert.isObject(result.details) + + assert.property(result.details, "blockhash") + assert.property(result.details, "hash") + assert.property(result.details, "hex") + assert.property(result.details, "vin") + assert.property(result.details, "vout") + assert.equal(result.details.hash, txid) + }) + + it(`should POST details for an array of transactions`, async () => { + const txids = [ + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251", + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + ] + + const result = await bchjs.Electrumx.txData(txids) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + + assert.property(result, "transactions") + assert.isArray(result.transactions) + + assert.property(result.transactions[0], "txid") + assert.property(result.transactions[0], "details") + + assert.property(result.transactions[0].details, "blockhash") + assert.property(result.transactions[0].details, "hash") + assert.property(result.transactions[0].details, "hex") + assert.property(result.transactions[0].details, "vin") + assert.property(result.transactions[0].details, "vout") + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const txids = [] + for (let i = 0; i < 25; i++) { + txids.push( + "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251" + ) + } + + await bchjs.Electrumx.txData(txids) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe(`#broadcast`, () => { + it(`should broadcast a single transaction`, async () => { + const txHex = + "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + + try { + await bchjs.Electrumx.broadcast(txHex) + } catch (err) { + assert.property(err, "success") + assert.equal(err.success, false) + assert.include( + err.error, + "the transaction was rejected by network rules" + ) + } + }) + }) +}) diff --git a/test/integration/bchn/encryption.js b/test/integration/bchn/encryption.js new file mode 100644 index 0000000..540d3d0 --- /dev/null +++ b/test/integration/bchn/encryption.js @@ -0,0 +1,31 @@ +const assert = require("chai").assert + +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +describe("#Encryption", () => { + describe("#getPubKey", () => { + it("should get a public key", async () => { + const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq" + + const result = await bchjs.encryption.getPubKey(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, true) + assert.property(result, "publicKey") + }) + + it("should report when public key can not be found", async () => { + const addr = "bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh" + + const result = await bchjs.encryption.getPubKey(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "success") + assert.equal(result.success, false) + assert.property(result, "publicKey") + assert.equal(result.publicKey, "not found") + }) + }) +}) diff --git a/test/integration/bchn/ninsight.js b/test/integration/bchn/ninsight.js new file mode 100644 index 0000000..46ee0c6 --- /dev/null +++ b/test/integration/bchn/ninsight.js @@ -0,0 +1,57 @@ +const chai = require("chai") +const assert = chai.assert +const sinon = require("sinon") + +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" }) + +describe(`#Ninsight`, () => { + let sandbox + beforeEach(() => (sandbox = sinon.createSandbox())) + afterEach(() => sandbox.restore()) + + describe(`#utxo`, () => { + it(`should GET utxos for a single address`, async () => { + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.Ninsight.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result[0], "utxos") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "cashAddress") + assert.property(result[0], "slpAddress") + assert.property(result[0], "scriptPubKey") + assert.property(result[0], "asm") + + assert.isArray(result[0].utxos) + + assert.property(result[0].utxos[0], "txid") + assert.property(result[0].utxos[0], "vout") + assert.property(result[0].utxos[0], "amount") + assert.property(result[0].utxos[0], "satoshis") + assert.property(result[0].utxos[0], "height") + assert.property(result[0].utxos[0], "confirmations") + }) + + it(`should POST utxo details for an array of addresses`, async () => { + const addr = [ + "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7", + "bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr" + ] + + const result = await bchjs.Ninsight.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isArray(result[0].utxos) + + assert.property(result[0], "utxos") + assert.property(result[0], "legacyAddress") + assert.property(result[0], "cashAddress") + assert.property(result[0], "slpAddress") + assert.property(result[0], "scriptPubKey") + assert.property(result[0], "asm") + }) + }) +}) diff --git a/test/integration/bchn/openbazaar.js b/test/integration/bchn/openbazaar.js new file mode 100644 index 0000000..e27e9b7 --- /dev/null +++ b/test/integration/bchn/openbazaar.js @@ -0,0 +1,117 @@ +/* + tests for OpenBazaar library. +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +describe(`#OpenBazaar`, () => { + describe(`#Balance`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.OpenBazaar.balance(addr) + // assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include(err.message, `Input address must be a string`) + } + }) + + it(`should GET balance for a single address`, async () => { + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.OpenBazaar.balance(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "page", + "totalPages", + "itemsOnPage", + "addrStr", + "balance", + "totalReceived", + "totalSent", + "unconfirmedBalance", + "unconfirmedTxApperances", + "txApperances", + "transactions" + ]) + assert.isArray(result.transactions) + }) + }) + + describe(`#utxo`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.OpenBazaar.utxo(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include(err.message, `Input address must be a string`) + } + }) + + it(`should GET utxos for a single address`, async () => { + const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9" + + const result = await bchjs.OpenBazaar.utxo(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAllKeys(result[0], [ + "txid", + "vout", + "amount", + "height", + "confirmations", + "satoshis" + ]) + }) + }) + + describe(`#tx`, () => { + it(`should throw an error for improper input`, async () => { + try { + const addr = 12345 + + await bchjs.OpenBazaar.tx(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include(err.message, `Input txid must be a string`) + } + }) + + it(`should GET transactions for a single address`, async () => { + const addr = + "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7" + + const result = await bchjs.OpenBazaar.tx(addr) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "txid", + "version", + "vin", + "vout", + "blockhash", + "blockheight", + "confirmations", + "blocktime", + "valueOut", + "valueIn", + "fees", + "hex", + "time" + ]) + assert.isArray(result.vin) + assert.isArray(result.vout) + }) + }) +}) diff --git a/test/integration/bchn/price.js b/test/integration/bchn/price.js new file mode 100644 index 0000000..42ed1e6 --- /dev/null +++ b/test/integration/bchn/price.js @@ -0,0 +1,33 @@ +const assert = require("chai").assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +describe("#price", () => { + describe("#current", () => { + describe("#single currency", () => { + it("should get current price for single currency", async () => { + const result = await bchjs.Price.current("usd") + assert.notEqual(0, result) + }) + }) + }) + + describe("#getUsd", () => { + it("should get the USD price of BCH", async () => { + const result = await bchjs.Price.getUsd() + // console.log(result) + + assert.isNumber(result) + }) + }) + + describe("#rates", () => { + it("should get the price of BCH in several currencies", async () => { + const result = await bchjs.Price.rates() + // console.log(result) + + assert.property(result, "USD") + assert.property(result, "CAD") + }) + }) +}) diff --git a/test/integration/bchn/rawtransaction.js b/test/integration/bchn/rawtransaction.js new file mode 100644 index 0000000..fb26a48 --- /dev/null +++ b/test/integration/bchn/rawtransaction.js @@ -0,0 +1,329 @@ +/* + Integration tests for the bchjs. Only covers calls made to + rest.bitcoin.com. + + TODO +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +// Inspect utility used for debugging. +const util = require("util") +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 3 +} + +describe("#rawtransaction", () => { + describe("#decodeRawTransaction", () => { + it("should decode tx for a single hex", async () => { + const hex = + "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" + + const result = await bchjs.RawTransactions.decodeRawTransaction(hex) + //console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.hasAnyKeys(result, [ + "txid", + "hash", + "size", + "version", + "locktime", + "vin", + "vout" + ]) + assert.isArray(result.vin) + assert.isArray(result.vout) + }) + + it("should decode an array of tx hexes", async () => { + const hexes = [ + "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000", + "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" + ] + + const result = await bchjs.RawTransactions.decodeRawTransaction(hexes) + //console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "txid", + "hash", + "size", + "version", + "locktime", + "vin", + "vout" + ]) + assert.isArray(result[0].vin) + assert.isArray(result[0].vout) + }) + + it(`should throw an error for improper single input`, async () => { + try { + const addr = 12345 + + await bchjs.RawTransactions.decodeRawTransaction(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input must be a string or array of strings.` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const data = [] + for (let i = 0; i < 25; i++) { + data.push( + "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" + ) + } + + const result = await bchjs.RawTransactions.decodeRawTransaction(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe("#getRawTransaction", () => { + it("should decode a single txid, with concise output", async () => { + const txid = + "23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" + const verbose = false + + const result = await bchjs.RawTransactions.getRawTransaction( + txid, + verbose + ) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isString(result) + }) + + it("should decode a single txid, with verbose output", async () => { + const txid = + "23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" + const verbose = true + + const result = await bchjs.RawTransactions.getRawTransaction( + txid, + verbose + ) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAnyKeys(result, [ + "hex", + "txid", + "hash", + "size", + "version", + "locktime", + "vin", + "vout", + "blockhash", + "confirmations", + "time", + "blocktime" + ]) + assert.isArray(result.vin) + assert.isArray(result.vout) + }) + + it("should decode an array of txids, with a concise output", async () => { + const txid = [ + "23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1", + "b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f" + ] + const verbose = false + + const result = await bchjs.RawTransactions.getRawTransaction( + txid, + verbose + ) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.isString(result[0]) + }) + + it("should decode an array of txids, with a verbose output", async () => { + const txid = [ + "23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1", + "b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f" + ] + const verbose = true + + const result = await bchjs.RawTransactions.getRawTransaction( + txid, + verbose + ) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "hex", + "txid", + "hash", + "size", + "version", + "locktime", + "vin", + "vout", + "blockhash", + "confirmations", + "time", + "blocktime" + ]) + assert.isArray(result[0].vin) + assert.isArray(result[0].vout) + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const dataMock = + "23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1" + const data = [] + for (let i = 0; i < 25; i++) data.push(dataMock) + + const result = await bchjs.RawTransactions.getRawTransaction(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) + + describe("#decodeScript", () => { + it("should decode script for a single hex", async () => { + const hex = + "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" + + const result = await bchjs.RawTransactions.decodeScript(hex) + //console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["asm", "type", "p2sh"]) + }) + + // CT 2/20/19 - Waiting for this PR to be merged complete the test: + // https://github.com/Bitcoin-com/rest.bitcoin.com/pull/312 + /* + it("should decode an array of tx hexes", async () => { + const hexes = [ + "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16", + "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" + ] + + const result = await bchjs.RawTransactions.decodeScript(hexes) + console.log(`result ${JSON.stringify(result, null, 2)}`) + }) +*/ + /* + it(`should throw an error for improper single input`, async () => { + try { + const addr = 12345 + + await bchjs.RawTransactions.decodeRawTransaction(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input must be a string or array of strings.` + ) + } + }) +*/ + }) + + /* + Testing sentRawTransaction isn't really possible with an integration test, + as the endpoint really needs an e2e test to be properly tested. The tests + below expect error messages returned from the server, but at least test + that the server is responding on those endpoints, and responds consistently. + */ + describe("sendRawTransaction", () => { + it("should send a single transaction hex", async () => { + try { + const hex = + "01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" + + await bchjs.RawTransactions.sendRawTransaction(hex) + //console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.fail("Unexpected result!") + } catch (err) { + //console.log(`err: ${util.inspect(err)}`) + + assert.hasAllKeys(err, ["error"]) + assert.include(err.error, "Missing inputs") + } + }) + + it("should send an array of tx hexes", async () => { + try { + const hexes = [ + "01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000", + "01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" + ] + + const result = await bchjs.RawTransactions.sendRawTransaction(hexes) + console.log(`result ${JSON.stringify(result, null, 2)}`) + + assert.fail("Unexpected result!") + } catch (err) { + // console.log(`err: ${util.inspect(err)}`) + + assert.hasAllKeys(err, ["error"]) + assert.include(err.error, "Missing inputs") + } + }) + + it(`should throw an error for improper single input`, async () => { + try { + const addr = 12345 + + await bchjs.RawTransactions.sendRawTransaction(addr) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input hex must be a string or array of strings` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const dataMock = + "01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000" + const data = [] + for (let i = 0; i < 25; i++) data.push(dataMock) + + const result = await bchjs.RawTransactions.sendRawTransaction(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) +}) diff --git a/test/integration/bchn/slp.js b/test/integration/bchn/slp.js new file mode 100644 index 0000000..9c64a16 --- /dev/null +++ b/test/integration/bchn/slp.js @@ -0,0 +1,700 @@ +/* + Integration tests for the bchjs covering SLP tokens. +*/ + +const chai = require("chai") +const assert = chai.assert + +const BCHJS = require("../../../src/bch-js") +let bchjs + +// Inspect utility used for debugging. +const util = require("util") +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 1 +} + +describe(`#SLP`, () => { + // before(() => { + // console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`) + // console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`) + // }) + + beforeEach(async () => { + // Introduce a delay so that the BVT doesn't trip the rate limits. + await sleep(1000) + + bchjs = new BCHJS() + }) + + describe("#util", () => { + describe("#list", () => { + it(`should get information on the Spice token`, async () => { + const tokenId = `4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf` + + const result = await bchjs.SLP.Utils.list(tokenId) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAnyKeys(result, [ + "decimals", + "timestamp", + "timestamp_unix", + "versionType", + "documentUri", + "symbol", + "name", + "containsBaton", + "id", + "documentHash", + "initialTokenQty", + "blockCreated", + "blockLastActiveSend", + "blockLastActiveMint", + "txnsSinceGenesis", + "validAddress", + "totalMinted", + "totalBurned", + "circulatingSupply", + "mintingBatonStatus" + ]) + }) + }) + + // describe("#decodeOpReturn", () => { + // it("should decode the OP_RETURN for a SEND txid", async () => { + // const txid = + // "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1" + // + // const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.hasAllKeys(result, [ + // "tokenType", + // "transactionType", + // "tokenId", + // "spendData" + // ]) + // }) + // + // it("should decode the OP_RETURN for a GENESIS txid", async () => { + // const txid = + // "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + // + // const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.hasAllKeys(result, [ + // "tokenType", + // "transactionType", + // "ticker", + // "name", + // "documentUrl", + // "documentHash", + // "decimals", + // "mintBatonVout", + // "initialQty", + // "tokensSentTo", + // "batonHolder" + // ]) + // }) + // + // it("should decode the OP_RETURN for a MINT txid", async () => { + // const txid = + // "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4" + // + // const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // // console.log(`result: ${JSON.stringify(result, null, 2)}`) + // + // assert.hasAllKeys(result, [ + // "tokenType", + // "transactionType", + // "tokenId", + // "mintBatonVout", + // "batonStillExists", + // "quantity", + // "tokensSentTo", + // "batonHolder" + // ]) + // }) + // + // it("should throw an error for a non-SLP transaction", async () => { + // try { + // const txid = + // "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec" + // + // await bchjs.SLP.Utils.decodeOpReturn(txid) + // + // assert.equal(true, false, "Unexpected result") + // } catch (err) { + // // console.log(`err: `, err) + // assert.include(err.message, "Not an OP_RETURN") + // } + // }) + // }) + + describe("#decodeOpReturn", () => { + it("should decode the OP_RETURN for a SEND txid", async () => { + const txid = + "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1" + + const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"]) + assert.equal( + result.tokenId, + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + ) + + // Verify outputs + assert.equal(result.amounts.length, 2) + assert.equal(result.amounts[0], "100000000") + assert.equal(result.amounts[1], "99883300000000") + }) + + it("should decode the OP_RETURN for a GENESIS txid", async () => { + const txid = + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + + const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "tokenType", + "txType", + "tokenId", + "ticker", + "name", + "documentUri", + "documentHash", + "decimals", + "mintBatonVout", + "qty" + ]) + assert.equal( + result.tokenId, + "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7" + ) + assert.equal(result.txType, "GENESIS") + assert.equal(result.ticker, "TOK-CH") + assert.equal(result.name, "TokyoCash") + }) + + it("should decode the OP_RETURN for a MINT txid", async () => { + const txid = + "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4" + + const result = await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, [ + "tokenType", + "txType", + "tokenId", + "mintBatonVout", + "qty" + ]) + }) + + it("should throw an error for a non-SLP transaction", async () => { + try { + const txid = + "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec" + + await bchjs.SLP.Utils.decodeOpReturn(txid) + + assert.equal(true, false, "Unexpected result") + } catch (err) { + // console.log(`err: `, err) + assert.include(err.message, "scriptpubkey not op_return") + } + }) + + // Note: This TX is interpreted as valid by the original decodeOpReturn(). + // Fixing this issue and related issues was the reason for creating the + // decodeOpReturn2() method using the slp-parser library. + it("should throw error for invalid SLP transaction", async () => { + try { + const txid = + "a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415" + + await bchjs.SLP.Utils.decodeOpReturn(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + } catch (err) { + // console.log(`err: `, err) + assert.include(err.message, "amount string size not 8 bytes") + } + }) + }) + + describe("#tokenUtxoDetails", () => { + // // This captures an important corner-case. When an SLP token is created, the + // // change UTXO will contain the same SLP txid, but it is not an SLP UTXO. + it("should return details on minting baton from genesis transaction", async () => { + const utxos = [ + { + txid: + "bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90", + vout: 3, + amount: 0.00002015, + satoshis: 2015, + height: 594892, + confirmations: 5 + }, + { + txid: + "bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90", + vout: 2, + amount: 0.00000546, + satoshis: 546, + height: 594892, + confirmations: 5 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.equal(data[0].isValid, false, "Change UTXO marked as false.") + + assert.property(data[1], "txid") + assert.property(data[1], "vout") + assert.property(data[1], "amount") + assert.property(data[1], "satoshis") + assert.property(data[1], "height") + assert.property(data[1], "confirmations") + assert.property(data[1], "utxoType") + assert.property(data[1], "tokenId") + assert.property(data[1], "tokenTicker") + assert.property(data[1], "tokenName") + assert.property(data[1], "tokenDocumentUrl") + assert.property(data[1], "tokenDocumentHash") + assert.property(data[1], "decimals") + assert.property(data[1], "isValid") + assert.equal(data[1].isValid, true) + }) + + it("should return details for a MINT token utxo", async () => { + // Mock the call to REST API + + const utxos = [ + { + txid: + "cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 600297, + confirmations: 76 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.property(data[0], "txid") + assert.property(data[0], "vout") + assert.property(data[0], "amount") + assert.property(data[0], "satoshis") + assert.property(data[0], "height") + assert.property(data[0], "confirmations") + assert.property(data[0], "utxoType") + assert.property(data[0], "transactionType") + assert.property(data[0], "tokenId") + assert.property(data[0], "tokenTicker") + assert.property(data[0], "tokenName") + assert.property(data[0], "tokenDocumentUrl") + assert.property(data[0], "tokenDocumentHash") + assert.property(data[0], "decimals") + assert.property(data[0], "mintBatonVout") + assert.property(data[0], "tokenQty") + assert.property(data[0], "isValid") + assert.equal(data[0].isValid, true) + }) + + it("should return details for a simple SEND SLP token utxo", async () => { + const utxos = [ + { + txid: + "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 596089, + confirmations: 748 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.property(data[0], "txid") + assert.property(data[0], "vout") + assert.property(data[0], "amount") + assert.property(data[0], "satoshis") + assert.property(data[0], "height") + assert.property(data[0], "confirmations") + assert.property(data[0], "utxoType") + assert.property(data[0], "tokenId") + assert.property(data[0], "tokenTicker") + assert.property(data[0], "tokenName") + assert.property(data[0], "tokenDocumentUrl") + assert.property(data[0], "tokenDocumentHash") + assert.property(data[0], "decimals") + assert.property(data[0], "tokenQty") + assert.property(data[0], "isValid") + assert.equal(data[0].isValid, true) + }) + + it("should handle BCH and SLP utxos in the same TX", async () => { + const utxos = [ + { + txid: + "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56", + vout: 3, + value: "6816", + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56", + vout: 2, + value: "546", + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + + const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.equal(result.length, 2) + assert.equal(result[0].isValid, false) + assert.equal(result[1].isValid, true) + }) + + it("should handle problematic utxos", async () => { + const utxos = [ + { + txid: + "0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959", + vout: 0, + amount: 0.00018362, + satoshis: 18362, + height: 613483, + confirmations: 124 + }, + { + txid: + "67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19", + vout: 1, + amount: 0.00000546, + satoshis: 546, + height: 612075, + confirmations: 1532 + } + ] + + const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.equal(result.length, 2) + assert.equal(result[0].isValid, false) + assert.equal(result[1].isValid, true) + }) + + it("should return false for BCH-only UTXOs", async () => { + const utxos = [ + { + txid: + "a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b", + vout: 0, + amount: 0.00001, + satoshis: 1000, + confirmations: 0, + ts: 1578424704 + }, + { + txid: + "53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44", + vout: 0, + amount: 0.00001, + satoshis: 1000, + confirmations: 0, + ts: 1578424634 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.isArray(data) + assert.equal(data[0].isValid, false) + assert.equal(data[1].isValid, false) + }) + + it("should handle a dust attack", async () => { + // it("#dustattack", async () => { + const utxos = [ + { + height: 655965, + tx_hash: + "a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e", + tx_pos: 151, + value: 547, + satoshis: 547, + txid: + "a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e", + vout: 151, + address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww", + hdIndex: 11 + } + ] + + const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log(`data: ${JSON.stringify(data, null, 2)}`) + + assert.equal(data[0].isValid, false) + }) + }) + + describe("#balancesForAddress", () => { + it(`should throw an error if input is not a string or array of strings`, async () => { + try { + const address = 1234 + + await bchjs.SLP.Utils.balancesForAddress(address) + + assert.equal(true, false, "Uh oh. Code path should not end here.") + } catch (err) { + //console.log(`Error: `, err) + assert.include( + err.message, + `Input address must be a string or array of strings` + ) + } + }) + + it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => { + const balances = await bchjs.SLP.Utils.balancesForAddress( + "simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9" + ) + // console.log(`balances: ${JSON.stringify(balances, null, 2)}`) + + assert.isArray(balances) + assert.hasAllKeys(balances[0], [ + "tokenId", + "balanceString", + "balance", + "decimalCount", + "slpAddress" + ]) + }) + + it(`should fetch balances for multiple addresses`, async () => { + const addresses = [ + "simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9", + "simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57" + ] + + const balances = await bchjs.SLP.Utils.balancesForAddress(addresses) + // console.log(`balances: ${JSON.stringify(balances, null, 2)}`) + + assert.isArray(balances) + assert.isArray(balances[0]) + assert.hasAllKeys(balances[0][0], [ + "tokenId", + "balanceString", + "balance", + "decimalCount", + "slpAddress" + ]) + }) + }) + + describe("#hydrateUtxos", () => { + it("should throw an error if input is not an array", async () => { + try { + const utxos = 1234 + + await bchjs.SLP.Utils.hydrateUtxos(utxos) + + assert.equal(true, false, "Uh oh. Code path should not end here.") + } catch (err) { + // console.log(`Error: `, err) + assert.include(err.message, `Input must be an array.`) + } + }) + + it("should hydrate UTXOs", async () => { + const utxos = [ + { + utxos: [ + { + txid: + "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56", + vout: 3, + value: "6816", + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56", + vout: 2, + value: "546", + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + } + ] + + const result = await bchjs.SLP.Utils.hydrateUtxos(utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 1) + assert.equal(result.slpUtxos[0].utxos.length, 2) + + // Test the non-slp UTXO. + assert.property(result.slpUtxos[0].utxos[0], "txid") + assert.property(result.slpUtxos[0].utxos[0], "vout") + assert.property(result.slpUtxos[0].utxos[0], "value") + assert.property(result.slpUtxos[0].utxos[0], "height") + assert.property(result.slpUtxos[0].utxos[0], "confirmations") + assert.property(result.slpUtxos[0].utxos[0], "satoshis") + assert.property(result.slpUtxos[0].utxos[0], "isValid") + assert.equal(result.slpUtxos[0].utxos[0].isValid, false) + + // Test the slp UTXO. + assert.property(result.slpUtxos[0].utxos[1], "txid") + assert.property(result.slpUtxos[0].utxos[1], "vout") + assert.property(result.slpUtxos[0].utxos[1], "value") + assert.property(result.slpUtxos[0].utxos[1], "height") + assert.property(result.slpUtxos[0].utxos[1], "confirmations") + assert.property(result.slpUtxos[0].utxos[1], "satoshis") + assert.property(result.slpUtxos[0].utxos[1], "isValid") + assert.equal(result.slpUtxos[0].utxos[1].isValid, true) + assert.property(result.slpUtxos[0].utxos[1], "transactionType") + assert.property(result.slpUtxos[0].utxos[1], "tokenId") + assert.property(result.slpUtxos[0].utxos[1], "tokenTicker") + assert.property(result.slpUtxos[0].utxos[1], "tokenName") + assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentUrl") + assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentHash") + assert.property(result.slpUtxos[0].utxos[1], "decimals") + assert.property(result.slpUtxos[0].utxos[1], "tokenType") + assert.property(result.slpUtxos[0].utxos[1], "tokenQty") + }) + + it("should process data directly from Electrumx", async () => { + const addrs = [ + "bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp", + "bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5", + "bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495" + ] + + const utxos = await bchjs.Electrumx.utxo(addrs) + // console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`) + + const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 3) + assert.equal(result.slpUtxos[0].utxos.length, 1) + assert.equal(result.slpUtxos[1].utxos.length, 1) + assert.equal(result.slpUtxos[2].utxos.length, 2) + + try { + // Test the expected values. + assert.equal(result.slpUtxos[0].utxos[0].isValid, false) + assert.equal(result.slpUtxos[1].utxos[0].isValid, true) + assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT") + assert.equal(result.slpUtxos[2].utxos[0].isValid, false) + assert.equal(result.slpUtxos[2].utxos[1].isValid, true) + assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE") + } catch (err) { + console.error( + 'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"' + ) + console.log("Error: ", err) + } + }) + }) + + describe("#validateTxid2", () => { + it("should invalidate a known invalid TXID", async () => { + const txid = + "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" + + const result = await bchjs.SLP.Utils.validateTxid2(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "txid") + assert.equal(result.txid, txid) + + assert.property(result, "isValid") + assert.equal(result.isValid, false) + }) + + it("should validate a known valid TXID", async () => { + const txid = + "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488" + + const result = await bchjs.SLP.Utils.validateTxid2(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "txid") + assert.equal(result.txid, txid) + + assert.property(result, "isValid") + assert.equal(result.isValid, true) + }) + }) + }) + + describe("#tokentype1", () => { + describe("#getHexOpReturn", () => { + it("should return OP_RETURN object ", async () => { + const tokenUtxos = [ + { + tokenId: + "0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1", + decimals: 0, + tokenQty: 2 + } + ] + const sendQty = 1.5 + + const result = await bchjs.SLP.TokenType1.getHexOpReturn( + tokenUtxos, + sendQty + ) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result, "script") + assert.isString(result.script) + + assert.property(result, "outputs") + assert.isNumber(result.outputs) + }) + }) + }) +}) + +// Promise-based sleep function +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)) +} diff --git a/test/integration/bchn/util.js b/test/integration/bchn/util.js new file mode 100644 index 0000000..a90f4a7 --- /dev/null +++ b/test/integration/bchn/util.js @@ -0,0 +1,109 @@ +/* + Integration tests for the bchjs. Only covers calls made to + rest.bitcoin.com. +*/ + +const chai = require("chai") +const assert = chai.assert +const BCHJS = require("../../../src/bch-js") +const bchjs = new BCHJS() + +// Inspect utility used for debugging. +const util = require("util") +util.inspect.defaultOptions = { + showHidden: true, + colors: true, + depth: 3 +} + +describe(`#util`, () => { + describe(`#validateAddress`, () => { + it(`should return false for testnet addr on mainnet`, async () => { + const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` + + const result = await bchjs.Util.validateAddress(address) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["isvalid"]) + assert.equal(result.isvalid, false) + }) + + it(`should return false for bad address`, async () => { + const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu` + + const result = await bchjs.Util.validateAddress(address) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAllKeys(result, ["isvalid"]) + assert.equal(result.isvalid, false) + }) + + it(`should validate valid address`, async () => { + const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z` + + const result = await bchjs.Util.validateAddress(address) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.hasAnyKeys(result, [ + "isvalid", + "address", + "scriptPubKey", + //"ismine", + //"iswatchonly", + "isscript" + ]) + assert.equal(result.isvalid, true) + }) + + it(`should validate an array of addresses`, async () => { + const address = [ + `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`, + `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z` + ] + + const result = await bchjs.Util.validateAddress(address) + //console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.isArray(result) + assert.hasAnyKeys(result[0], [ + "isvalid", + "address", + "scriptPubKey", + //"ismine", + //"iswatchonly", + "isscript" + ]) + }) + + it(`should throw an error for improper single input`, async () => { + try { + const address = 15432 + + await bchjs.Util.validateAddress(address) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err: `, err) + assert.include( + err.message, + `Input must be a string or array of strings.` + ) + } + }) + + it(`should throw error on array size rate limit`, async () => { + try { + const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z` + const data = [] + for (let i = 0; i < 25; i++) data.push(dataMock) + + const result = await bchjs.Util.validateAddress(data) + + console.log(`result: ${util.inspect(result)}`) + assert.equal(true, false, "Unexpected result!") + } catch (err) { + assert.hasAnyKeys(err, ["error"]) + assert.include(err.error, "Array too large") + } + }) + }) +}) diff --git a/test/integration/electrumx.js b/test/integration/electrumx.js index 80e3eee..1c706c5 100644 --- a/test/integration/electrumx.js +++ b/test/integration/electrumx.js @@ -346,7 +346,10 @@ describe(`#ElectrumX`, () => { } catch (err) { assert.property(err, "success") assert.equal(err.success, false) - assert.include(err.error, "Missing inputs") + assert.include( + err.error, + "the transaction was rejected by network rules" + ) } }) }) diff --git a/test/integration/test-free-tier-bchn.sh b/test/integration/test-free-tier-bchn.sh old mode 100644 new mode 100755 index ffa59e5..2a5faea --- a/test/integration/test-free-tier-bchn.sh +++ b/test/integration/test-free-tier-bchn.sh @@ -3,5 +3,5 @@ export RESTURL=https://bchn-free-main.fullstack.cash/v3/ #export RESTURL=http://localhost:3000/v3/ -cd test/integration/ +cd test/integration/bchn/ mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js diff --git a/test/integration/testnet/electrumx.js b/test/integration/testnet/electrumx.js index 1bedb8c..7886803 100644 --- a/test/integration/testnet/electrumx.js +++ b/test/integration/testnet/electrumx.js @@ -275,7 +275,8 @@ describe(`#ElectrumX`, () => { describe(`#txData`, () => { it(`should GET details for a single transaction`, async () => { - const txid = "76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" + const txid = + "76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" const result = await bchjs.Electrumx.txData(txid) // console.log(`result: ${JSON.stringify(result, null, 2)}`) @@ -322,8 +323,11 @@ describe(`#ElectrumX`, () => { it(`should throw error on array size rate limit`, async () => { try { const txids = [] - for (let i = 0; i < 25; i++) - txids.push("76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee") + for (let i = 0; i < 25; i++) { + txids.push( + "76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee" + ) + } await bchjs.Electrumx.txData(txids) @@ -338,14 +342,18 @@ describe(`#ElectrumX`, () => { describe(`#broadcast`, () => { it(`should broadcast a single transaction`, async () => { - const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" + const txHex = + "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000" try { await bchjs.Electrumx.broadcast(txHex) } catch (err) { assert.property(err, "success") assert.equal(err.success, false) - assert.include(err.error, "Missing inputs") + assert.include( + err.error, + "the transaction was rejected by network rules" + ) } }) })