Compare commits

...
26 Commits
Author SHA1 Message Date
Chris Troutner 1433c77fcc Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
feat(Blockbook): Removing support for Blockbook indexer
2020-12-24 10:18:55 -08:00
Chris Troutner b1c3311d50 feat(Blockbook): Removing support for Blockbook indexer 2020-12-24 10:16:41 -08:00
Chris Troutner b3e224e481 Cleaned up integration tests 2020-12-24 10:06:39 -08:00
Chris Troutner 06e2dad45c Ran npm audit fix 2020-12-24 09:26:36 -08:00
Chris Troutner d475f2220f feat(SLPDB status): Created bchjs.SLP.Utils.getStatus() to get health status of SLPDB 2020-12-24 09:25:35 -08:00
Chris Troutner 10520a2e08 Merge pull request #75 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): Renabling local caching of tx validation
2020-12-13 09:16:17 -08:00
Chris Troutner 05b654d54a fix(tokenUtxoDetails): Renabling local caching of tx validation 2020-12-13 09:14:06 -08:00
Chris Troutner 3c4051c3a5 Merge pull request #74 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): Updating to handle alternative output from SLPDB
2020-12-13 09:00:48 -08:00
Chris Troutner 0f3cc1e9e8 Merge branch 'master' into ct-unstable 2020-12-13 08:56:28 -08:00
Chris Troutner b9873f7ed8 Added additional corner case unit test for tokenUtxoDetails() 2020-12-13 08:56:09 -08:00
Chris Troutner 82cd9270fa fix(tokenUtxoDetails): Updating to handle alternative output from SLPDB 2020-12-13 08:51:58 -08:00
Chris Troutner ac6b45b162 Merge pull request #73 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Additional debugging
2020-12-13 07:26:07 -08:00
Chris Troutner 66a99c0560 Merge branch 'master' into ct-unstable 2020-12-13 07:20:08 -08:00
Chris Troutner 17e4a32ff4 fix(debugging): Additional debugging 2020-12-13 07:19:24 -08:00
Chris Troutner 6a430b4da6 Merge pull request #72 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Debugging problem with hydrateUtxos()
2020-12-13 06:55:48 -08:00
Chris Troutner 77ab090bc2 fix(debugging): Debugging problem with hydrateUtxos() 2020-12-13 06:51:07 -08:00
Chris Troutner fdff4a1afb Merge pull request #71 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): Fixing bug with fallback validators
2020-12-13 06:25:10 -08:00
Chris Troutner 12701cf3dc fix(tokenUtxoDetails): Fixing bug with fallback validators 2020-12-13 06:20:51 -08:00
Chris Troutner 1032625ac8 Merge pull request #70 from Permissionless-Software-Foundation/ct-unstable
blockbook: Removing blockbook support from testnet
2020-12-11 15:19:36 -08:00
Chris Troutner 586c2972da feat(blockbook): Removing blockbook support from testnet 2020-12-11 15:15:03 -08:00
Chris Troutner 3d41e41192 fix(testnet): Updating integration tests 2020-12-11 14:59:46 -08:00
Chris Troutner af8e0cdf45 Merge pull request #69 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Fixing testnet integration tests
2020-12-08 18:36:54 -08:00
Chris Troutner 10f6f58ec5 fix(testnet): Fixing testnet integration tests 2020-12-08 18:28:30 -08:00
Chris Troutner fd9f399f41 Merge pull request #68 from Permissionless-Software-Foundation/ct-unstable
Added test/integration/implementations/
2020-12-08 17:59:49 -08:00
Chris Troutner 98582edd0b Merge branch 'master' into ct-unstable 2020-12-08 17:56:03 -08:00
Chris Troutner 07b7cb0afd Added test/integration/implementations/ for customized integration tests for different implementations 2020-12-08 17:55:47 -08:00
21 changed files with 704 additions and 1008 deletions
+8 -8
View File
@@ -3962,9 +3962,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inquirer": {
"version": "3.3.0",
@@ -5450,9 +5450,9 @@
"dev": true
},
"npm": {
"version": "6.14.9",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.9.tgz",
"integrity": "sha512-yHi1+i9LyAZF1gAmgyYtVk+HdABlLy94PMIDoK1TRKWvmFQAt5z3bodqVwKvzY0s6dLqQPVsRLiwhJfNtiHeCg==",
"version": "6.14.10",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.10.tgz",
"integrity": "sha512-FT23Qy/JMA+qxEYReMOr1MY7642fKn8Onn+72LASPi872Owvmw0svm+/DXTHOC3yO9CheEO+EslyXEpdBdRtIA==",
"dev": true,
"requires": {
"JSONStream": "^1.3.5",
@@ -5538,7 +5538,7 @@
"npm-user-validate": "^1.0.1",
"npmlog": "~4.1.2",
"once": "~1.4.0",
"opener": "^1.5.1",
"opener": "^1.5.2",
"osenv": "^0.1.5",
"pacote": "^9.5.12",
"path-is-inside": "~1.0.2",
@@ -7718,7 +7718,7 @@
}
},
"opener": {
"version": "1.5.1",
"version": "1.5.2",
"bundled": true,
"dev": true
},
+4 -3
View File
@@ -12,11 +12,12 @@
"test:integration": "npm run test:integration:bchn",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/testnet/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
"test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration",
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ bash test/integration/test-bchn-integration.sh",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet",
"test:temp": "export RESTURL=http://localhost:3000/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/chains/bchn/",
"test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#getStatus' test/integration/",
"test:temp2": "mocha --timeout 30000 -g '#getStatus' test/unit/",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs"
@@ -58,7 +59,7 @@
"ecurve": "^1.0.6",
"figlet": "^1.2.0",
"git-clone": "^0.1.0",
"ini": "^1.3.5",
"ini": "^1.3.8",
"mkdirp": "^0.5.1",
"node-cmd": "^3.0.0",
"node-emoji": "^1.8.1",
-7
View File
@@ -34,7 +34,6 @@ const IPFS = require("./ipfs")
const Encryption = require("./encryption")
// Indexers
const Blockbook = require("./blockbook")
const OpenBazaar = require("./openbazaar")
const Ninsight = require("./ninsight")
const Electrumx = require("./electrumx")
@@ -80,9 +79,6 @@ class BCHJS {
// console.log(`apiToken: ${this.apiToken}`)
// Populate Blockbook endpoints.
this.Blockbook = new Blockbook(libConfig)
// Populate OpenBazaar endpoints
this.OpenBazaar = new OpenBazaar(libConfig)
@@ -194,9 +190,6 @@ class BitboxShim {
this.Mining = new Mining(libConfig)
this.RawTransactions = new RawTransactions(libConfig)
// Populate Blockbook endpoints.
this.Blockbook = new Blockbook(libConfig)
// Populate OpenBazaar endpoints
this.OpenBazaar = new OpenBazaar(libConfig)
-132
View File
@@ -1,132 +0,0 @@
/*
This library interacts with the REST API endpoints in bch-api that communicate
with the Blockbook API.
CT 11/10/20
These endpoints will be soon be deprecated. Blockbook is being fazed out in
favor of Fulcrum.
*/
const axios = require("axios")
let _this
class Blockbook {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// console.log(`Blockbook apiToken: ${this.apiToken}`)
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
_this = this
}
async balance(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.get(
`${this.restURL}blockbook/balance/${address}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}blockbook/balance`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
async utxo(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.get(
`${this.restURL}blockbook/utxos/${address}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}blockbook/utxos`,
{
addresses: address
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
async tx(txid) {
try {
// Handle single txid.
if (typeof txid === "string") {
const response = await axios.get(
`${this.restURL}blockbook/tx/${txid}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(txid)) {
const response = await axios.post(
`${this.restURL}blockbook/tx`,
{
txids: txid
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input txid must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = Blockbook
+102 -8
View File
@@ -431,6 +431,10 @@ class Utils {
* '00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476',
* valid: true } ]
*/
// This function has two responses. If SLPDB is working correctly, the output
// will be like the examples above. If SLPDB has fallen behind real-time
// processing, it will return this output:
// [ null ]
async validateTxid(txid) {
const path = `${this.restURL}slp/validateTxid`
@@ -449,7 +453,9 @@ class Utils {
},
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
// console.log(
// `validateTxid response.data: ${JSON.stringify(response.data, null, 2)}`
// )
const validatedTxids = response.data
@@ -1288,19 +1294,53 @@ class Utils {
// property. i.e. it has been successfully hydrated with SLP
// information.
// CT 12/13/2020: Disabling the cache until I get processing of tokens
// to be more stable.
// If the value has been cached, use the cached version first.
let isValid = cachedTxValidation[utxo.txid]
if (!isValid) isValid = null
// let isValid = null
// There are two possible responses from SLPDB. If SLPDB is functioning
// correctly, then validateTxid() will return this:
// isValid: [
// {
// "txid": "ff0c0354f8d3ddb34fa36f73494eb58ea24f8b8da6904aa8ed43b7a74886c583",
// "valid": true
// }
// ]
//
// If SLPDB has fallen behind real-time processing, it will return this:
// isValid: [
// null
// ]
//
// Note: validateTxid3() has the same output as validateTxid().
// validateTxid2() uses slp-validate, which has a different output format.
// If not in the cache, try the general SLPDB.
if (isValid == null) {
if (isValid === null) {
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
isValid = await this.validateTxid(utxo.txid)
// console.log(
// `validateTxid() isValid: ${JSON.stringify(isValid, null, 2)}`
// )
// Handle corner case where SLPDB returns an array with a null element.
if (isValid[0] === null)
isValid = [{ txid: utxo.txid, valid: null }]
isValid = isValid[0].valid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
// cachedTxValidation[utxo.txid] = isValid
}
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
// console.log(
// `pre-validateTxid3() isValid: ${JSON.stringify(isValid, null, 2)}`
// )
// If still null, check the whitelist SLPDB
if (isValid === null) {
// console.log(
@@ -1334,28 +1374,40 @@ class Utils {
// )}`
// )
isValid = isValid[0].valid
if (isValid[0] !== null) isValid = isValid[0].valid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
// cachedTxValidation[utxo.txid] = isValid
}
}
// console.log(
// `pre-validateTxid2() isValid: ${JSON.stringify(isValid, null, 2)}`
// )
// If still null, as a last resort, check it against slp-validate
if (isValid === null) {
isValid = await this.validateTxid2(utxo.txid)
try {
isValid = await this.validateTxid2(utxo.txid)
} catch (err) {
// Mark as invalid if validateTxid2() throws an error.
isValid = null
}
// console.log(
// `slp-validate isValid: ${JSON.stringify(isValid, null, 2)}`
// )
isValid = isValid.isValid
if (isValid !== null) isValid = isValid.isValid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
// cachedTxValidation[utxo.txid] = isValid
}
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid
// Save the txid to the local cache to reduce API calls.
cachedTxValidation[utxo.txid] = isValid
}
}
@@ -1550,6 +1602,48 @@ class Utils {
else throw error
}
}
/**
* @api SLP.Utils.getStatus() getStatus()
* @apiName getStatus
* @apiGroup SLP Utils
* @apiDescription Get the status and health of the SLPDB connected to bch-api.
*
* @apiExample Example usage:
*
* // Get the current blockheight of the SLPDB indexer.
* (async () => {
* try {
* let validated = await bchjs.SLP.Utils.validateTxid(
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
* );
* console.log(validated);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* [ { txid:
* 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
* valid: true } ]
*
*/
async getStatus(txid) {
const path = `${this.restURL}slp/status`
try {
const response = await _this.axios.get(path, _this.axiosOptions)
// console.log(
// `getStatus response.data: ${JSON.stringify(response.data, null, 2)}`
// )
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
throw error
}
}
}
module.exports = Utils
-148
View File
@@ -1,148 +0,0 @@
/*
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`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
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 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 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")
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,186 +0,0 @@
/*
Integration tests for the Blockbook library.
*/
const chai = require("chai")
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// const bchjs = new BCHJS({ restURL: RESTURL })
//const axios = require("axios")
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe(`#Blockbook`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
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 = [
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
]
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("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
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 = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
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 = [
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2",
"bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
]
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("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
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")
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -15,7 +15,7 @@ const RESTURL = process.env.RESTURL
: "https://testnet3.fullstack.cash/v4/"
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
+1 -1
View File
@@ -9,7 +9,7 @@ const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#control`, () => {
+1 -1
View File
@@ -6,7 +6,7 @@ const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#ElectrumX`, () => {
+3 -2
View File
@@ -2,7 +2,7 @@ const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
const bchjs = new BCHJS({ ninsightURL: "https://trest.bitcoin.com/v2" })
describe(`#Ninsight`, () => {
@@ -105,7 +105,8 @@ describe(`#Ninsight`, () => {
})
describe(`#txDetails`, () => {
it(`should POST transactions details for a single address`, async () => {
const txid = "76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea"
const txid =
"76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea"
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -13,7 +13,7 @@ const RESTURL = process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
@@ -251,7 +251,7 @@ describe("#rawtransaction", () => {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
assert.include(err.error, "Missing inputs")
}
})
@@ -268,7 +268,7 @@ describe("#rawtransaction", () => {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
assert.include(err.error, "Missing inputs")
}
})
+1 -1
View File
@@ -10,7 +10,7 @@ const RESTURL = process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
+1 -1
View File
@@ -11,7 +11,7 @@ const RESTURL = process.env.RESTURL
: `https://testnet3.fullstack.cash/v4/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
const BCHJS = require("../../../../src/bch-js")
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
@@ -0,0 +1,71 @@
/*
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", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
/*
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.equal(true, false, "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)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -0,0 +1,246 @@
/*
Integration tests for the bchjs covering SLP tokens.
These tests are specific to the ABC chain.
*/
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.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
bchjs = new BCHJS()
})
describe("#util", () => {
describe("#tokenUtxoDetails", () => {
it("should handle a range of UTXO types", async () => {
const utxos = [
// Malformed SLP tx
{
note: "Malformed SLP tx",
tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
tx_pos: 1,
value: 546
},
// Normal TX (non-SLP)
{
note: "Normal TX (non-SLP)",
tx_hash:
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
tx_pos: 0,
value: 400000
},
// Valid PSF SLP tx
{
note: "Valid PSF SLP tx",
tx_hash:
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
tx_pos: 1,
value: 546
},
// Valid SLP token not in whitelist
{
note: "Valid SLP token not in whitelist",
tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: "Token send on BCHN network",
tx_hash:
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019",
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: "Token send on ABC network",
tx_hash:
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d",
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: "Known invalid SLP token send of PSF tokens",
tx_hash:
"2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74",
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Malformed SLP tx
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, false)
// Normal TX (non-SLP)
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Valid PSF SLP tx
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
// Valid SLP token not in whitelist
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
})
})
describe("#validateTxid3", () => {
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.equal(result[0].txid, txid)
assert.property(result[0], "valid")
assert.equal(result[0].valid, null)
})
it("should validate a known valid TXID", async () => {
const txid =
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd"
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], "txid")
assert.equal(result[0].txid, txid)
assert.property(result[0], "valid")
assert.equal(result[0].valid, true)
})
it("should handle a mix of valid, invalid, and non-SLP txs", async () => {
const txids = [
// Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
// Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
// Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
// Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
// Unprocessed SLP TX
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019"
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
})
})
describe("#validateTxid", () => {
it("should handle a mix of valid, invalid, and non-SLP txs", async () => {
const txids = [
// Malformed SLP tx
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
// Normal TX (non-SLP)
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
// Valid PSF SLP tx
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
// Valid SLP token not in whitelist
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
// Token send on BCHN network.
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019",
// Token send on ABC network.
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d"
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
})
})
// Promise-based sleep function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+87 -166
View File
@@ -464,97 +464,30 @@ describe(`#SLP`, () => {
assert.equal(data[0].isValid, false)
})
// it("should handle a range of UTXO types", async () => {
// const utxos = [
// // Malformed SLP tx
// {
// note: "Malformed SLP tx",
// tx_hash:
// "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
// tx_pos: 1,
// value: 546
// },
// // Normal TX (non-SLP)
// {
// note: "Normal TX (non-SLP)",
// tx_hash:
// "01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
// tx_pos: 0,
// value: 400000
// },
// // Valid PSF SLP tx
// {
// note: "Valid PSF SLP tx",
// tx_hash:
// "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
// tx_pos: 1,
// value: 546
// },
// // Valid SLP token not in whitelist
// {
// note: "Valid SLP token not in whitelist",
// tx_hash:
// "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
// tx_pos: 1,
// value: 546
// },
// // Token send on BCHN network.
// {
// note: "Token send on BCHN network",
// tx_hash:
// "402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019",
// tx_pos: 1,
// value: 546
// },
// // Token send on ABC network.
// {
// note: "Token send on ABC network",
// tx_hash:
// "336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d",
// tx_pos: 1,
// value: 546
// },
// // Known invalid SLP token send of PSF tokens.
// {
// note: "Known invalid SLP token send of PSF tokens",
// tx_hash:
// "2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74",
// tx_pos: 1,
// value: 546
// }
// ]
//
// const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// // console.log(`data: ${JSON.stringify(data, null, 2)}`)
//
// // Malformed SLP tx
// assert.equal(data[0].tx_hash, utxos[0].tx_hash)
// assert.equal(data[0].isValid, false)
//
// // Normal TX (non-SLP)
// assert.equal(data[1].tx_hash, utxos[1].tx_hash)
// assert.equal(data[1].isValid, false)
//
// // Valid PSF SLP tx
// assert.equal(data[2].tx_hash, utxos[2].tx_hash)
// assert.equal(data[2].isValid, true)
//
// // Valid SLP token not in whitelist
// assert.equal(data[3].tx_hash, utxos[3].tx_hash)
// assert.equal(data[3].isValid, true)
//
// // Token send on BCHN network
// assert.equal(data[4].tx_hash, utxos[4].tx_hash)
// assert.equal(data[4].isValid, null)
//
// // Token send on ABC network
// assert.equal(data[5].tx_hash, utxos[5].tx_hash)
// assert.equal(data[5].isValid, true)
//
// // Known invalid SLP token send of PSF tokens
// assert.equal(data[6].tx_hash, utxos[6].tx_hash)
// assert.equal(data[6].isValid, false)
// })
it("should handle null SLPDB validations", async () => {
const utxos = [
{
height: 665577,
tx_hash:
"4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419",
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
// assert.equal(data[0].isValid, null)
})
})
describe("#balancesForAddress", () => {
@@ -695,6 +628,60 @@ describe(`#SLP`, () => {
console.log("Error: ", err)
}
})
it("should handle null SLPDB validations", async () => {
const utxos = [
{
height: 665577,
tx_hash:
"4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419",
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.hydrateUtxos([{ utxos: utxos }])
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data.slpUtxos)
assert.equal(data.slpUtxos[0].isValid, null)
})
})
describe("#validateTxid", () => {
// This test is not necessary.
it("should handle a null response from SLPDB", async () => {
const txid =
"4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419"
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid)
// assert.equal(result[0].valid, null)
})
it("should handle a null response from SLPDB", async () => {
const txid = [
"4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419",
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
]
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid[0])
// assert.equal(result[0].valid, null)
})
})
describe("#validateTxid2", () => {
@@ -738,82 +725,16 @@ describe(`#SLP`, () => {
})
})
// describe("#validateTxid3", () => {
// it("should invalidate a known invalid TXID", async () => {
// const txid =
// "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
//
// const result = await bchjs.SLP.Utils.validateTxid3(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
//
// assert.property(result[0], "txid")
// assert.equal(result[0].txid, txid)
//
// assert.property(result[0], "valid")
// assert.equal(result[0].valid, null)
// })
//
// it("should validate a known valid TXID", async () => {
// const txid =
// "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd"
//
// const result = await bchjs.SLP.Utils.validateTxid3(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
//
// assert.property(result[0], "txid")
// assert.equal(result[0].txid, txid)
//
// assert.property(result[0], "valid")
// assert.equal(result[0].valid, true)
// })
//
// it("should handle a mix of valid, invalid, and non-SLP txs", async () => {
// const txids = [
// // Malformed SLP tx
// "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
// // Normal TX (non-SLP)
// "01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
// // Valid PSF SLP tx
// "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
// // Valid SLP token not in whitelist
// "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
// // Unprocessed SLP TX
// // "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed" // Mint baton
// "402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019"
// ]
//
// const result = await bchjs.SLP.Utils.validateTxid3(txids)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
// })
// })
describe("#getStatus", () => {
it("should return the current block height of the SLPDB indexer", async () => {
const result = await bchjs.SLP.Utils.getStatus()
// describe("#validateTxid", () => {
// it("should handle a mix of valid, invalid, and non-SLP txs", async () => {
// const txids = [
// // Malformed SLP tx
// "f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
// // Normal TX (non-SLP)
// "01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
// // Valid PSF SLP tx
// "daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
// // Valid SLP token not in whitelist
// "3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
// // Unprocessed SLP TX
// // "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed"
// // Token send on BCHN network.
// "402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019",
// // Token send on ABC network.
// "336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d"
// ]
//
// const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// })
// })
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "bchBlockHeight")
assert.property(result, "slpProcessedBlockHeight")
})
})
})
describe("#tokentype1", () => {
-275
View File
@@ -1,275 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
const axios = require("axios")
const sinon = require("sinon")
const mockData = require("./fixtures/blockbook-mock")
describe(`#Blockbook`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.balance })
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 () => {
// Stub the network call.
sandbox
.stub(axios, "post")
.resolves({ data: [mockData.balance, mockData.balance] })
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 {
// Stub the network call.
sandbox
.stub(axios, "post")
.throws(`Input address must be a string or array of strings`)
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 {
// Stub the network call.
sandbox.stub(axios, "post").throws(`Array too large`)
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 () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
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 () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
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 {
// Stub the network call.
sandbox
.stub(axios, "post")
.throws(`Input address must be a string or array of strings`)
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:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
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")
}
})
*/
})
describe(`#tx`, () => {
it(`should GET tx details for a single txid`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.utxo })
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
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 () => {
// // Stub the network call.
// sandbox.stub(axios, "post").resolves({ data: mockData.utxos })
//
// const addr = [
// "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
// "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
// ]
//
// 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 {
// // Stub the network call.
// sandbox
// .stub(axios, "post")
// .throws(`Input address must be a string or array of strings`)
//
// 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:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
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")
}
})
*/
})
})
-64
View File
@@ -1,64 +0,0 @@
/*
Unit test mocks for Blockbook endpoints.
*/
const balance = {
page: 1,
totalPages: 1,
itemsOnPage: 1000,
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
balance: "230000",
totalReceived: "6194528752",
totalSent: "6194298752",
unconfirmedBalance: "0",
unconfirmedTxs: 0,
txs: 3,
txids: [
"27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
"6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
"5d95f4e6047901fc1502a9758bbdb14ce73f24662b1784d13e0217b3d37bb7a2"
]
}
const utxo = [
{
txid: "27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
vout: 0,
value: "100000",
height: 560430,
confirmations: 29321
},
{
txid: "6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
vout: 0,
value: "130000",
height: 560039,
confirmations: 29712
}
]
const utxos = [
[
{
txid: "27ec8512c1a9ee9e9ae9b98eb60375f1d2bd60e2e76a1eff5a45afdbc517cf9c",
vout: 0,
value: "100000",
height: 560430,
confirmations: 29868
},
{
txid: "6e1ae1bf7db6de799ec1c05ab2816ac65549bd80141567af088e6f291385b07d",
vout: 0,
value: "130000",
height: 560039,
confirmations: 30259
}
],
[]
]
module.exports = {
balance,
utxo,
utxos
}
+102 -1
View File
@@ -1190,6 +1190,106 @@ const whitelist = [
}
]
const slpdbStatus = {
_id: "5fe4cd90cd2bd2000f7fe46c",
version: "1.0.0-beta-rc13",
versionHash: "bb4a805610b9e4d67c1595b716df36190c75e8b1",
deplVersionHash: null,
startCmd: "node index run",
context: "SLPDB",
lastStatusUpdate: {
utc: "Thu, 24 Dec 2020 17:19:12 GMT",
unix: 1608830352
},
lastIncomingTxnZmq: {
utc: "Thu, 24 Dec 2020 17:19:12 GMT",
unix: 1608830352
},
lastIncomingBlockZmq: {
utc: "Thu, 24 Dec 2020 16:43:38 GMT",
unix: 1608828218
},
lastOutgoingTxnZmq: null,
lastOutgoingBlockZmq: null,
state: "RUNNING",
stateHistory: [
{
utc: "Mon, 07 Dec 2020 15:26:23 GMT",
state: "STARTUP_BLOCK_SYNC"
},
{
utc: "Tue, 08 Dec 2020 19:53:04 GMT",
state: "RUNNING"
}
],
network: "mainnet",
bchBlockHeight: 667270,
bchBlockHash:
"000000000000000003d57b35b1b6b8073c8311974e2b9a473e1bd6daa98fde53",
slpProcessedBlockHeight: 667270,
mempoolInfoBch: {
loaded: true,
size: 534,
bytes: 320552,
usage: 956384,
maxmempool: 300000000,
mempoolminfee: 0.00001,
minrelaytxfee: 0.00001
},
mempoolSizeSlp: 78,
tokensCount: 77976,
pastStackTraces: [
"[Tue, 08 Dec 2020 19:35:36 GMT] MongoServerSelectionError: connection <monitor> to 172.17.0.1:12301 timed out\n at Timeout._onTimeout (/home/safeuser/SLPDB/node_modules/mongodb/lib/core/sdam/topology.js:438:30)\n at listOnTimeout (internal/timers.js:554:17)\n at processTimers (internal/timers.js:497:7)",
"[Tue, 08 Dec 2020 19:35:36 GMT] MongoServerSelectionError: connection <monitor> to 172.17.0.1:12301 timed out\n at Timeout._onTimeout (/home/safeuser/SLPDB/node_modules/mongodb/lib/core/sdam/topology.js:438:30)\n at listOnTimeout (internal/timers.js:554:17)\n at processTimers (internal/timers.js:497:7)"
],
doubleSpends: [
{
txo:
"3e377f67e9d065f02e47633d847cdb04b0679d51baccdc0feea5297529e330b5:820",
details: {
originalTxid:
"dc148149d25a546fffe0b207c62b192811c288481df5f43f1f76f67b6004f98c",
current:
"de12db60e253c378eeebb1df85153168a998db8267ec54b38589cc31348b2b8d",
time: {
utc: "Sat, 19 Dec 2020 02:25:37 GMT",
unix: 1608344737
}
}
}
],
reorgs: [],
mongoDbStats: {
db: "slpdb",
collections: 5,
views: 0,
objects: 2497140,
avgObjSize: 3188.778575890819,
dataSize: 7593.943150520325,
storageSize: 3357.74609375,
indexes: 71,
indexSize: 1490.14453125,
totalSize: 4847.890625,
scaleFactor: 1048576,
fsUsedSize: 74424.46484375,
fsTotalSize: 153676.98046875,
ok: 1
},
publicUrl: "fullstack--bchn-02",
telemetryHash: null,
system: {
loadAvg1: 0.16,
loadAvg5: 0.11,
loadAvg15: 0.04,
platform: "linux",
cpuCount: 8,
freeMem: 5780.51953125,
totalMem: 31360.8828125,
uptime: 1654085,
processUptime: 1374204.613560816
}
}
module.exports = {
mockList,
mockToken,
@@ -1218,5 +1318,6 @@ module.exports = {
mockWhitelist,
mockValidateTxid3Valid,
mockValidateTxid3Invalid,
whitelist
whitelist,
slpdbStatus
}
+73
View File
@@ -1771,6 +1771,64 @@ describe("#SLP Utils", () => {
// The UTXO should have been validated by the third validation source.
assert.equal(data[0].isValid, true)
})
it("should handle SLPDB returning null corner case", async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, "decodeOpReturn")
.onCall(0)
.resolves({
tokenType: 1,
txType: "SEND",
tokenId:
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7",
amounts: ["200000000", "99887500000000"]
})
.onCall(1)
.resolves({
tokenType: 1,
txType: "GENESIS",
ticker: "TOK-CH",
name: "TokyoCash",
tokenId:
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7",
documentUri: "",
documentHash: "",
decimals: 8,
mintBatonVout: 0,
qty: "2100000000000000"
})
// Force default SLPDB to return 'null' corner case.
sandbox.stub(uut.Utils, "validateTxid").resolves([null])
// Mock the response from slp-api
sandbox.stub(uut.Utils, "validateTxid2").resolves({
txid:
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
isValid: true,
msg: ""
})
const utxos = [
{
txid:
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await uut.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// The UTXO should have been validated by the third validation source.
assert.equal(data[0].isValid, true)
})
})
describe("#txDetails", () => {
@@ -2103,4 +2161,19 @@ describe("#SLP Utils", () => {
}
})
})
describe("#getStatus", () => {
it("should return the current block height of the SLPDB indexer", async () => {
sandbox
.stub(uut.Utils.axios, "get")
.resolves({ data: mockData.slpdbStatus })
const result = await uut.Utils.getStatus()
// console.log(`result: `, result)
assert.property(result, "bchBlockHeight")
assert.property(result, "slpProcessedBlockHeight")
})
})
})