Compare commits

..
34 Commits
Author SHA1 Message Date
Chris Troutner 7fea5c2630 Merge pull request #79 from Permissionless-Software-Foundation/ct-test-joey-slp
Testing Joey's Updates to SLP Handling
2020-12-30 08:52:52 -08:00
Chris Troutner b297743521 feat(SLP): Improved SLP calculation to fix small burns 2020-12-30 08:24:18 -08:00
Chris Troutner 6c33222172 Merge pull request #60 from josephroyking/slp-utils-patch
Resolving correct decimal precision for slp token qty
2020-12-28 05:01:59 -08:00
Chris Troutner 9a36a8261a Merge pull request #54 from josephroyking/generateSendOpReturn-rounding-patch
Generate send op return rounding patch
2020-12-28 04:57:53 -08:00
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
Joey King ad5fabf2e6 Remove stub test 2020-12-03 06:57:44 -08:00
Joey King f8f6db069b Implementing BigNumber for all tokenQty calcs 2020-12-03 06:56:05 -08:00
Joey King df620e0302 stub test method 2020-12-02 16:06:10 -08:00
Joey King 19f2e87528 Resolving correct decimal precision for slp token qty 2020-12-02 16:02:07 -08:00
Joey King b512b222dd Adding large test tx 2020-11-10 14:28:10 -08:00
Joey King 650e3b53fc Adding tests and checks 2020-11-10 13:38:41 -08:00
Joey King 905658ca28 format changes take two 2020-11-10 06:19:36 -08:00
Joey King b0c91c407d matching original formatting 2020-11-10 06:13:53 -08:00
Joey King d2c68efc0c Use BigNumber to eliminate rounding error token burns 2020-11-10 06:10:38 -08:00
15 changed files with 1537 additions and 1021 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
},
+3 -2
View File
@@ -16,7 +16,8 @@
"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
+30 -18
View File
@@ -1,3 +1,7 @@
/*
This library handles the OP_RETURN of SLP TokenType1 transactions.
*/
//const BCHJS = require("../bch-js")
//const bchjs = new BCHJS()
@@ -93,12 +97,16 @@ class TokenType1 {
const tokenId = tokenUtxos[0].tokenId
const decimals = tokenUtxos[0].decimals
// Calculate the total amount of tokens owned by the wallet.
let totalTokens = 0
for (let i = 0; i < tokenUtxos.length; i++)
totalTokens += tokenUtxos[i].tokenQty
const sendQtyBig = new BigNumber(sendQty).times(10 ** decimals)
const change = totalTokens - sendQty
// Calculate the total amount of tokens owned by the wallet.
const totalTokens = tokenUtxos.reduce(
(tot, txo) =>
tot.plus(new BigNumber(txo.tokenQty).times(10 ** decimals)),
new BigNumber(0)
)
const change = totalTokens.minus(sendQtyBig)
// console.log(`change: ${change}`)
let script
@@ -109,19 +117,20 @@ class TokenType1 {
outputs = 2
// Convert the send quantity to the format expected by slp-mdm.
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
const baseQty = sendQtyBig.toString()
// console.log(`baseQty: `, baseQty)
// Convert the change quantity to the format expected by slp-mdm.
let baseChange = new BigNumber(change).times(10 ** decimals)
baseChange = baseChange.absoluteValue()
baseChange = Math.floor(baseChange)
baseChange = baseChange.toString()
const baseChange = change.toString()
// console.log(`baseChange: `, baseChange)
// Check for potential burns
const outputQty = new BigNumber(baseChange).plus(new BigNumber(baseQty))
const inputQty = new BigNumber(totalTokens)
const tokenOutputDelta = outputQty.minus(inputQty).toString() !== "0"
if (tokenOutputDelta)
throw "Token transaction inputs do not match outputs, cannot send transaction"
// Generate the OP_RETURN as a Buffer.
script = slpMdm.TokenType1.send(tokenId, [
new slpMdm.BN(baseQty),
@@ -131,13 +140,16 @@ class TokenType1 {
// Corner case, when there is no token change to send back.
} else {
let baseQty = new BigNumber(sendQty).times(10 ** decimals)
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
const baseQty = sendQtyBig.toString()
// console.log(`baseQty: `, baseQty)
// console.log(`baseQty: ${baseQty.toString()}`)
// Check for potential burns
const noChangeOutputQty = new BigNumber(baseQty)
const noChangeInputQty = new BigNumber(totalTokens)
const tokenSingleOutputError =
noChangeOutputQty.minus(noChangeInputQty).toString() !== "0"
if (tokenSingleOutputError)
throw "Token transaction inputs do not match outputs, cannot send transaction"
// Generate the OP_RETURN as a Buffer.
script = slpMdm.TokenType1.send(tokenId, [new slpMdm.BN(baseQty)])
+115 -11
View File
@@ -1,6 +1,7 @@
// Public npm libraries
const axios = require("axios")
const slpParser = require("slp-parser")
const BigNumber = require("bignumber.js")
// const Script = require("../script")
// const scriptLib = new Script()
@@ -431,6 +432,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 +454,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
@@ -1172,7 +1179,9 @@ class Utils {
// Tokens
else {
utxo.utxoType = "token"
utxo.tokenQty = slpData.qty / Math.pow(10, slpData.decimals)
utxo.tokenQty = new BigNumber(slpData.qty)
.div(Math.pow(10, slpData.decimals))
.toString()
}
utxo.tokenId = utxo.txid
@@ -1215,7 +1224,9 @@ class Utils {
// Tokens
else {
utxo.utxoType = "token"
utxo.tokenQty = slpData.qty / Math.pow(10, genesisData.decimals)
utxo.tokenQty = new BigNumber(slpData.qty)
.div(Math.pow(10, genesisData.decimals))
.toString()
}
// Hydrate the UTXO object with information about the SLP token.
@@ -1273,7 +1284,12 @@ class Utils {
utxo.tokenType = slpData.tokenType
// Calculate the real token quantity.
utxo.tokenQty = tokenQty / Math.pow(10, genesisData.decimals)
const tokenQtyBig = new BigNumber(tokenQty).div(
Math.pow(10, genesisData.decimals)
)
//console.log(`tokenQtyBig`, tokenQtyBig.toString())
utxo.tokenQty = tokenQtyBig.toString()
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
@@ -1288,19 +1304,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 +1384,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 +1612,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))
}
@@ -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")
}
})
+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
}
File diff suppressed because it is too large Load Diff
+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")
})
})
})