Compare commits

...
32 Commits
Author SHA1 Message Date
Chris Troutner 8a8dcbe593 Merge pull request #41 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Updating testnet integration tests
2020-10-29 08:08:17 -07:00
Chris Troutner ae58895d71 fix(integration tests): Updating testnet integration tests 2020-10-29 07:18:59 -07:00
Chris Troutner a50ace320b Merge pull request #40 from Permissionless-Software-Foundation/ct-unstable
fix(rawtransactions): Updating integration test for latest version of…
2020-10-27 19:16:41 -07:00
Chris Troutner b1d583cfe4 fix(rawtransactions): Updating integration test for latest version of ABC 2020-10-27 18:03:32 -07:00
Chris Troutner 0b86a19726 Merge pull request #39 from Permissionless-Software-Foundation/ct-unstable
fix(price): Fixing broken integration test
2020-10-20 18:34:13 -07:00
Chris Troutner eb3bc7bcff fix(price): Fixing broken integration test 2020-10-20 18:18:18 -07:00
Chris Troutner c5361e0516 Merge pull request #38 from Permissionless-Software-Foundation/ct-unstable
fix(docs): Update to price documentation
2020-10-19 19:55:57 -07:00
Chris Troutner 54d9233f41 fix(docs): Update to price documentation 2020-10-19 19:46:27 -07:00
Chris Troutner 37a5f0e6fa Merge pull request #37 from Permissionless-Software-Foundation/ct-unstable
feat(price): Added additional endpoints for retrieving market price o…
2020-10-19 19:40:51 -07:00
Chris Troutner 2180b14e1f feat(price): Added additional endpoints for retrieving market price of BCH 2020-10-19 19:36:55 -07:00
Chris Troutner 40e1704572 Merge pull request #36 from Permissionless-Software-Foundation/ct-unstable
Added testnet SLP integration tests
2020-10-16 22:25:59 -07:00
Chris Troutner 9539119c57 fix(testnet): Added additional integration tests for testnet SLP 2020-10-16 22:22:18 -07:00
Chris Troutner b397381c7d fix(hydrateUtxos): Improving documentation 2020-10-13 21:49:22 -07:00
Chris Troutner 6cdb9a0867 fix(documentation): Fixing documentation conflict on slp addrs 2020-10-13 15:58:45 -07:00
Chris Troutner 01f45d7103 Merge pull request #34 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): handling dust attack transactions better
2020-10-11 17:59:04 -07:00
Chris Troutner ba38d3e107 fix(tokenUtxoDetails): handling dust attack transactions better 2020-10-11 17:46:27 -07:00
Chris Troutner cbb0d0a8ca Merge pull request #33 from Permissionless-Software-Foundation/ct-unstable
validateTxid2
2020-10-11 16:51:40 -07:00
Chris Troutner 7ec8442540 fix(Electrumx.blockHeader): Mocking negative test-case unit tests 2020-10-11 15:39:17 -07:00
Chris Troutner be3094fc77 feat(validateTxid2): slp-validate based validation of SLP txids 2020-10-11 15:23:50 -07:00
Chris Troutner d35f0e5473 Merge pull request #30 from Permissionless-Software-Foundation/broacast-tx
Add Electrumx Broadcast TX method
2020-10-09 11:35:12 -07:00
Chris Troutner 79088fc68d linting 2020-10-09 11:30:07 -07:00
Chris Troutner fe84729737 Merge branch 'master' into broacast-tx 2020-10-09 11:21:29 -07:00
Chris Troutner 2ec72e73c1 Merge pull request #32 from Permissionless-Software-Foundation/ct-unstable
fix(bchn): Adding bchn free tier to integration tests
2020-10-09 11:21:05 -07:00
Chris Troutner a2c121bf3b fix(bchn): Adding bchn free tier to integration tests 2020-10-09 11:17:40 -07:00
Chris Troutner fd6f58fd45 Merge pull request #31 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Added additional comments
2020-10-07 10:53:01 -07:00
Chris Troutner e8b2f88ff5 fix(decodeOpReturn): Added additional comments 2020-10-07 08:07:06 -07:00
Chris Troutner 4a11db7c45 Merge pull request #29 from kaellis/master
add internal caching for slp.tokenUtxoDetails to reduce duplicate calls
2020-10-07 08:02:08 -07:00
Stoyan Zhekov af118f8a62 Add Electrumx Broadcast TX method 2020-10-07 17:14:30 +09:00
Ken Ellis 1f41b67e3b add internal caching for slp.tokenUtxoDetails to reduce duplicate calls 2020-10-01 09:33:06 -04:00
Chris Troutner 054fb9c14e Merge pull request #28 from Permissionless-Software-Foundation/ct-unstable
Improvements to hydrateUtxos()
2020-09-28 19:33:20 -07:00
Chris Troutner 1fd4095dcb fix(hydateUtxos): Refined integration test 2020-09-28 19:30:06 -07:00
Chris Troutner 0eb1f74ba1 fix(hydrateUtxos): fixing documentation for the function 2020-09-28 19:18:27 -07:00
19 changed files with 999 additions and 78 deletions
+2 -1
View File
@@ -14,8 +14,9 @@
"test:integration:local": "RESTURL=http://localhost:3000/v3/ mocha --timeout 30000 test/integration",
"test:integration:testnet:local": "RESTURL=http://localhost:4000/v3/ mocha --timeout 30000 test/integration/testnet",
"test:integration:free": "bash test/integration/test-free-tier.sh",
"test:integration:free:bchn": "bash test/integration/test-free-tier-bchn.sh",
"test:integration:free:testnet": "bash test/integration/testnet/test-free-tier.sh",
"test:temp": "mocha --timeout 30000 -g '#hydrateUtxos' test/integration/slp.js",
"test:temp": "mocha --timeout 30000 -g '#price' test/integration/",
"test:integration:api": "RESTURL=https://api.fullstack.cash/v3/ mocha --timeout 30000 test/integration",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
+1 -1
View File
@@ -90,7 +90,7 @@ class BCHJS {
this.Generating = new Generating(libConfig)
this.HDNode = new HDNode(this.Address)
this.Mnemonic = new Mnemonic(this.Address)
this.Price = new Price()
this.Price = new Price(libConfig)
this.Script = new Script()
this.TransactionBuilder = TransactionBuilder
this.TransactionBuilder.setAddress(this.Address)
+49 -3
View File
@@ -441,8 +441,14 @@ class ElectrumX {
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
// console.log("error: ", error)
if (error.response && error.response.data) {
if (error.response && error.response.data)
throw new Error(error.response.data.error)
else throw error.response.data
} else {
throw error
}
}
}
@@ -515,7 +521,7 @@ class ElectrumX {
* }
* ]
* }
*/
*/
async txData(txid) {
try {
// Handle single transaction.
@@ -543,6 +549,46 @@ class ElectrumX {
else throw error
}
}
/**
* @api Electrumx.broadcast() broadcast()
* @apiName ElectrumX Broadcast
* @apiGroup ElectrumX
* @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure.
*
* (async () => {
* try {
* const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
* let result = await bchjs.Electrumx.broadcast(txHex)
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* result = {
* "success": true,
* "txid": "..."
* }
*/
async broadcast(txHex) {
try {
if (typeof txHex === "string") {
const response = await axios.post(
`${this.restURL}electrumx/tx/broadcast`,
{ txHex },
_this.axiosOptions
)
return response.data
}
throw new Error(`Input txHex must be a string.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = ElectrumX
+1 -1
View File
@@ -116,7 +116,7 @@ class HDNode {
/**
* @api SLP.HDNode.toSLPAddress() toSLPAddress()
* @apiName toSLPAddress
* @apiGroup SLP
* @apiGroup HDNode
* @apiDescription Get slp address of HDNode.
*
* @apiExample Example usage:
+125 -22
View File
@@ -1,32 +1,135 @@
const axios = require("axios")
/**
* @api price.current() current()
* @apiName Price.
* @apiGroup Price
* @apiDescription Return current price of BCH in multiple currencies.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.current('usd');
* console.log(current);
* } catch(error) {
* console.error(error)
* }
*})()
*
* // 26681
*/
let _this
class Price {
constructor(config) {
_this = this
if (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
}
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
this.axios = axios
}
/**
* @api price.current() current()
* @apiName Price.
* @apiGroup Price
* @apiDescription Return current price of BCH in multiple currencies.
* This endpoint will be deprecated in favor of getUSD. It uses the Bitcoin.com
* price feed.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.current('usd');
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* // 26681
*/
async current(currency = "usd") {
try {
const response = await axios.get(
const response = await this.axios.get(
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.price
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getUsd() getUsd()
* @apiName Price getUsd()
* @apiGroup Price
* @apiDescription Return current price of BCH in USD.
* This endpoint gets the USD price of BCH from the Coinbase API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* // 266.81
*/
async getUsd() {
try {
const response = await this.axios.get(
`${this.restURL}price/usd`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.rates() rates()
* @apiName Price rates()
* @apiGroup Price
* @apiDescription Return current price of BCH in several different currencies.
* This endpoint gets the price of BCH from the Coinbase API in many different
* currencies. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.rates();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* {
* AED: "915.049218",
* AFN: "19144.48874646",
* ALGO: "826.6633482661356600405",
* ...
* ZRX: "644.844402797695193656",
* ZWL: "80215.03"
* }
*/
async rates() {
try {
const response = await this.axios.get(
`${this.restURL}price/rates`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
}
+174 -17
View File
@@ -1,10 +1,10 @@
const axios = require("axios")
const slpParser = require("slp-parser")
const Script = require("../script")
const scriptLib = new Script()
// const Script = require("../script")
// const scriptLib = new Script()
const BigNumber = require("bignumber.js")
// const BigNumber = require("bignumber.js")
let _this
@@ -194,7 +194,7 @@ class Utils {
path = `${this.restURL}slp/list`
}
//console.log(`path: ${path}`)
// console.log(`path: ${path}`)
try {
let response
@@ -316,7 +316,7 @@ class Utils {
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
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
throw error
@@ -455,6 +455,66 @@ class Utils {
}
}
/**
* @api SLP.Utils.validateTxid2() validateTxid2()
* @apiName validateTxid2
* @apiGroup SLP Utils
* @apiDescription Validate that txid is an SLP transaction.
*
* This second validatoin version uses the slp-validate slp library. It is
* much slower and less efficient than SLPDB and is prone to time-outs for
* tokens with large DAGs. However, it operates independently of SLPDB and
* is a great second validation option, particularly when SLPDB returns 'null'
* values.
*
* Due to the inefficiency of this call, only a single TXID can be input at a
* time. This call will throw an error if the input is an array.
*
* @apiExample Example usage:
*
* // validate single SLP txid
* (async () => {
* try {
* let validated = await bchjs.SLP.Utils.validateTxid(
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
* );
* console.log(validated);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* [ { txid:
* 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
* valid: true } ]
*/
async validateTxid2(txid) {
try {
// console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
if (
!txid ||
txid === "" ||
typeof txid !== "string" ||
txid.length !== 64
)
throw new Error("txid must be 64 character string.")
const path = `${this.restURL}slp/validateTxid2/${txid}`
const response = await axios.get(path, _this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
if (error.error && error.error.indexOf("Network error") > -1)
throw new Error("slp-validate timed out")
throw error
}
}
/**
* @api SLP.Utils.tokenStats() tokenStats()
* @apiName tokenStats
@@ -640,7 +700,7 @@ class Utils {
typeof txid !== "string" ||
txid.length !== 64
)
throw new Error(`txid string must be included.`)
throw new Error("txid string must be included.")
// console.log(`this.restURL: ${this.restURL}`)
const path = `${this.restURL}slp/txDetails/${txid}`
@@ -662,6 +722,9 @@ class Utils {
* Similar to decodeOpReturn(), except decodeOpReturn2() uses the slp-parser
* library maintained by JT Freeman. Outputs have slightly different format.
*
* If optional associative array parameter cache is used, will cache and
* reuse responses for the same input.
*
* Throws an error if given a non-SLP txid.
*
* In a future version of bch-js, this method will replace the origonal
@@ -694,11 +757,26 @@ class Utils {
* }
*/
// Reimplementation of decodeOpReturn() using slp-parser.
async decodeOpReturn(txid) {
async decodeOpReturn(txid, cache = null) {
// The cache object is an in-memory cache (JS Object) that can be passed
// into this function. It helps if multiple vouts from the same TXID are
// being evaluated. In that case, it can significantly reduce the number
// of API calls.
// To use: add the output of this function to the cache object:
// cache[txid] = returnValue
// Then pass that cache object back into this function every time its called.
if (cache) {
if (!(cache instanceof Object))
throw new Error("decodeOpReturn cache parameter must be Object")
const cachedVal = cache[txid]
if (cachedVal) return cachedVal
}
try {
// Validate the txid input.
if (!txid || txid === "" || typeof txid !== "string")
throw new Error(`txid string must be included.`)
throw new Error("txid string must be included.")
// Retrieve the transaction object from the full node.
const path = `${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=true`
@@ -746,6 +824,8 @@ class Utils {
}
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
if (cache) cache[txid] = tokenData
return tokenData
} catch (error) {
// console.log('decodeOpReturn error: ', error)
@@ -813,8 +893,12 @@ class Utils {
// CT 5/31/20: Refactored to use slp-parse library.
async tokenUtxoDetails(utxos) {
try {
// utxo list may have duplicate tx_hash, varying tx_pos
// only need to call decodeOpReturn once for those
const decodeOpReturnCache = {}
const cachedTxValidation = {}
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error(`Input must be an array.`)
if (!Array.isArray(utxos)) throw new Error("Input must be an array.")
// Loop through each element in the array and validate the input before
// further processing.
@@ -870,7 +954,7 @@ class Utils {
// If there is no OP_RETURN, mark the UTXO as false.
let slpData = false
try {
slpData = await this.decodeOpReturn(utxo.txid)
slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache)
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
} catch (err) {
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
@@ -880,7 +964,8 @@ class Utils {
// to display the unknown state.
if (
!err.message ||
err.message.indexOf("scriptpubkey not op_return") === -1
(err.message.indexOf("scriptpubkey not op_return") === -1 &&
err.message.indexOf("lokad id") === -1)
) {
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
@@ -955,7 +1040,10 @@ class Utils {
// If UTXO passes validation, then return formatted token data.
else {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Minting Baton
@@ -1003,7 +1091,10 @@ class Utils {
// If UTXO passes validation, then return formatted token data.
else {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
@@ -1030,7 +1121,11 @@ class Utils {
// Finally, validate the SLP txid with SLPDB.
if (outAry[i].tokenType) {
const isValid = await this.validateTxid(utxo.txid)
var isValid = cachedTxValidation[utxo.txid]
if (isValid == null) {
isValid = await this.validateTxid(utxo.txid)
cachedTxValidation[utxo.txid] = isValid
}
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid[0].valid
@@ -1111,7 +1206,17 @@ class Utils {
* "satoshis": 546,
* "txid": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
* "vout": 1,
* "isValid": false
* "utxoType": "token",
* "transactionType": "send",
* "tokenId": "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2",
* "tokenTicker": "TROUT",
* "tokenName": "Trout's test token",
* "tokenDocumentUrl": "troutsblog.com",
* "tokenDocumentHash": "",
* "decimals": 2,
* "tokenType": 1,
* "tokenQty": 10,
* "isValid": true
* }
* ],
* "address": "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89"
@@ -1136,7 +1241,17 @@ class Utils {
* "satoshis": 546,
* "txid": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
* "vout": 1,
* "isValid": false
* "utxoType": "token",
* "transactionType": "send",
* "tokenId": "6201f3efe486c577433622817b99645e1d473cd3882378f9a0efc128ab839a82",
* "tokenTicker": "VALENTINE",
* "tokenName": "Valentine day token",
* "tokenDocumentUrl": "fullstack.cash",
* "tokenDocumentHash": "",
* "decimals": 2,
* "tokenType": 1,
* "tokenQty": 5,
* "isValid": true
* }
* ],
* "address": "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
@@ -1144,13 +1259,55 @@ class Utils {
* ]
* }
*
* (async () => {
* try {
* const utxos = [
* {
* utxos: [
* {
* txid: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
* vout: 3,
* value: "6816",
* height: 606848,
* confirmations: 13,
* satoshis: 6816
* }
* ]
* }
* ]
*
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos)
*
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
* console.error(error)
* }
* })()
*
* // returns
* {
* "slpUtxos": [
* {
* "utxos": [
* {
* "txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
* "vout": 3,
* "value": "6816",
* "height": 606848,
* "confirmations": 13,
* "satoshis": 6816,
* "isValid": false
* }
* ]
* }
* ]
*/
// Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
// do the heavy lifting.
async hydrateUtxos(utxos) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error(`Input must be an array.`)
if (!Array.isArray(utxos)) throw new Error("Input must be an array.")
const response = await axios.post(
`${this.restURL}slp/hydrateUtxos`,
+22 -3
View File
@@ -271,7 +271,8 @@ describe(`#ElectrumX`, () => {
describe(`#txData`, () => {
it(`should GET details for a single transaction`, async () => {
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -318,8 +319,11 @@ describe(`#ElectrumX`, () => {
it(`should throw error on array size rate limit`, async () => {
try {
const txids = []
for (let i = 0; i < 25; i++)
txids.push("4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251")
for (let i = 0; i < 25; i++) {
txids.push(
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
)
}
await bchjs.Electrumx.txData(txids)
@@ -331,4 +335,19 @@ describe(`#ElectrumX`, () => {
}
})
})
describe(`#broadcast`, () => {
it(`should broadcast a single transaction`, async () => {
const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, "success")
assert.equal(err.success, false)
assert.include(err.error, "Missing inputs")
}
})
})
})
+20 -1
View File
@@ -1,4 +1,4 @@
const assert = require("assert")
const assert = require("chai").assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
@@ -11,4 +11,23 @@ describe("#price", () => {
})
})
})
describe("#getUsd", () => {
it("should get the USD price of BCH", async () => {
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe("#rates", () => {
it("should get the price of BCH in several currencies", async () => {
const result = await bchjs.Price.rates()
// console.log(result)
assert.property(result, "USD")
assert.property(result, "CAD")
})
})
})
+2 -2
View File
@@ -271,7 +271,7 @@ describe("#rawtransaction", () => {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -288,7 +288,7 @@ describe("#rawtransaction", () => {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
+73 -2
View File
@@ -6,7 +6,7 @@ const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
let bchjs
// Inspect utility used for debugging.
const util = require("util")
@@ -25,6 +25,8 @@ describe(`#SLP`, () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
await sleep(1000)
bchjs = new BCHJS()
})
describe("#util", () => {
@@ -437,6 +439,30 @@ describe(`#SLP`, () => {
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it("should handle a dust attack", async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#balancesForAddress", () => {
@@ -582,7 +608,7 @@ describe(`#SLP`, () => {
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
@@ -590,6 +616,51 @@ describe(`#SLP`, () => {
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT")
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE")
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log("Error: ", err)
}
})
})
describe("#validateTxid2", () => {
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, false)
})
it("should validate a known valid TXID", async () => {
const txid =
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, true)
})
})
})
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
export RESTURL=https://bchn-free-main.fullstack.cash/v3/
#export RESTURL=http://localhost:3000/v3/
cd test/integration/
mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
+14
View File
@@ -335,4 +335,18 @@ describe(`#ElectrumX`, () => {
}
})
})
describe(`#broadcast`, () => {
it(`should broadcast a single transaction`, async () => {
const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, "success")
assert.equal(err.success, false)
assert.include(err.error, "Missing inputs")
}
})
})
})
+2 -2
View File
@@ -278,7 +278,7 @@ describe("#rawtransaction", () => {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -295,7 +295,7 @@ describe("#rawtransaction", () => {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
+55 -1
View File
@@ -89,7 +89,7 @@ describe(`#SLP`, () => {
}
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
it(`should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9`, async () => {
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
@@ -139,6 +139,60 @@ describe(`#SLP`, () => {
])
})
})
describe("#tokenUtxoDetails", () => {
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(
utxos.utxos[0].utxos
)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo[0].isValid, true)
})
})
describe("#hydrateUtxos", () => {
// This test will error out if the LOCAL_RESTURL settings is not set properly
// in bch-api.
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo.slpUtxos[0].utxos)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo.slpUtxos[0].utxos[0].isValid, true)
})
})
describe("#validateTxid2", () => {
it("should validate a token txid", async () => {
const txid =
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1"
const validated = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(validated)
assert.equal(validated[0].valid, true)
})
})
})
// Promise-based sleep function
+53 -3
View File
@@ -264,17 +264,38 @@ describe(`#ElectrumX`, () => {
describe(`#blockHeader`, () => {
it(`should throw an error for improper height input`, async () => {
try {
// Mock network calls.
sandbox.stub(axios, "get").rejects({
response: {
data: {
success: false,
error: "height must be a positive number"
}
}
})
const height = -10
await bchjs.Electrumx.blockHeader(height)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.error, `height must be a positive number`)
assert.include(err.message, `height must be a positive number`)
}
})
it(`should throw an error for improper count input`, async () => {
try {
// Mock network calls.
sandbox.stub(axios, "get").rejects({
response: {
data: {
success: false,
error: "count must be a positive number"
}
}
})
const height = 42
const count = -10
@@ -282,7 +303,7 @@ describe(`#ElectrumX`, () => {
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.error, `count must be a positive number`)
assert.include(err.message, `count must be a positive number`)
}
})
@@ -319,7 +340,8 @@ describe(`#ElectrumX`, () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.details })
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -368,4 +390,32 @@ describe(`#ElectrumX`, () => {
assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs")
})
})
describe(`#broadcast`, () => {
it(`should throw an error for improper input`, async () => {
try {
const txHex = 12345
await bchjs.Electrumx.broadcast(txHex)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, `Input txHex must be a string.`)
}
})
it(`should broadcast a single transaction`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.broadcast })
const tx = mockData.details
const txid = tx.details.txid
const result = await bchjs.Electrumx.broadcast(tx.details.hex)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "txid")
assert.equal(result.txid, txid)
})
})
})
+7 -1
View File
@@ -250,6 +250,11 @@ const detailsArray = {
]
}
const broadcast = {
success: true,
txid: txDetails.txid
}
module.exports = {
utxo,
utxos,
@@ -261,5 +266,6 @@ module.exports = {
unconfirmedArray,
blockHeaders,
details,
detailsArray
detailsArray,
broadcast
}
+214
View File
@@ -0,0 +1,214 @@
/*
Mocks for price.
*/
const mockRates = {
AED: "915.049218",
AFN: "19144.48874646",
ALGO: "826.6633482661356600405",
ALL: "26221.96098759",
AMD: "119977.82663822",
ANG: "446.841810455",
AOA: "162269.774275",
ARS: "19322.704621",
ATOM: "45.5920571010248851205",
AUD: "353.78913716",
AWG: "448.407",
AZN: "424.1182875",
BAL: "18.9361216125975755781",
BAM: "413.75759465",
BAND: "40.84187228461349099275",
BAT: "1169.75444148879972951",
BBD: "498.23",
BCH: "1.0",
BDT: "21107.92000745",
BGN: "413.93197515",
BHD: "93.93578597",
BIF: "481540.76228735",
BMD: "249.115",
BND: "337.84677362",
BOB: "1718.851399565",
BRL: "1396.737982",
BSD: "249.115",
BSV: "1.555324933590611026515",
BTC: "0.021265",
BTN: "18249.513962505",
BWP: "2848.17713393",
BYN: "637.963336685",
BYR: "6379633.36685000049823",
BZD: "501.752236985",
CAD: "328.80838319",
CDF: "488848.679106575",
CGLD: "121.0412516398620018305",
CHF: "226.8989243",
CLF: "7.110489445",
CLP: "196202.978234955",
CNH: "1664.25510705",
CNY: "1666.429881",
COMP: "2.4706436576415750709",
COP: "958730.858397465",
CRC: "150173.845981",
CUC: "248.997666835",
CVE: "23541.3675",
CZK: "5770.848621",
DAI: "247.0463573269230002455",
DASH: "3.327367317363110236645",
DJF: "44315.31795969",
DKK: "1575.7370741",
DOP: "14529.76988648",
DZD: "32107.28592277",
EEK: "3640.489633465",
EGP: "3911.404438",
EOS: "96.6873665825732601419",
ERN: "3736.727740265",
ETB: "9302.454572035",
ETC: "44.91795888928957612395",
ETH: "0.6574429621419051422355",
EUR: "211.595",
FJD: "533.11855575",
FKP: "192.55642863",
GBP: "192.87",
GEL: "804.64145",
GGP: "192.55642863",
GHS: "1448.776859925",
GIP: "192.55642863",
GMD: "12891.70125",
GNF: "2438630.43574976",
GTQ: "1936.689014855",
GYD: "52047.88891278",
HKD: "1930.67861725",
HNL: "6126.43736492",
HRK: "1605.40119007",
HTG: "15682.48428085",
HUF: "77261.419177275",
IDR: "3666848.2425",
ILS: "843.29662455",
IMP: "192.55642863",
INR: "18275.43761675",
IQD: "297176.65680577",
ISK: "34639.44075",
JEP: "192.55642863",
JMD: "36261.061070375",
JOD: "176.622535",
JPY: "26304.9247525",
KES: "27093.7474",
KGS: "20240.23303148",
KHR: "1021526.66327067",
KMF: "104678.164103975",
KNC: "275.5392102643512742545",
KRW: "284115.6575",
KWD: "76.196555935",
KYD: "207.44852333",
KZT: "106578.169689505",
LAK: "2300372.70137523",
LBP: "376426.60931701",
LINK: "22.8704181013371177476",
LKR: "45903.840861165",
LRC: "1428.4116972477063306",
LRD: "48689.521020355",
LSL: "4093.68786226",
LTC: "5.182877353583689269445",
LTL: "803.357262175",
LVL: "163.484459015",
LYD: "340.470203685",
MAD: "2290.86403115",
MDL: "4231.74490411",
MGA: "978918.42727237",
MKD: "13034.71219274",
MKR: "0.43727565410331105846",
MMK: "321367.16219401",
MNT: "706980.17312004",
MOP: "1987.157222705",
MRO: "88934.055",
MTL: "170.32939187",
MUR: "9942.18014823",
MVR: "3836.371",
MWK: "187533.350248305",
MXN: "5285.574344805",
MYR: "1033.2044625",
MZN: "18168.45667469",
NAD: "4120.3621",
NGN: "95465.8503",
NIO: "8675.07065117",
NMR: "8.6886689628111785348",
NOK: "2328.95371465",
NPR: "29198.52701022",
NZD: "378.934057915",
OMG: "74.27621574882972595255",
OMR: "95.913509955",
OXT: "1023.479868529169993565",
PAB: "249.115",
PEN: "892.41413087",
PGK: "870.976539545",
PHP: "12100.979598855",
PKR: "40413.26181118",
PLN: "968.71705891",
PYG: "1750259.579863715",
QAR: "907.08999375",
REN: "758.802924154736515935",
REP: "18.2168190127970744169",
REPV2: "18.225711080673967324",
RON: "1032.33256",
RSD: "24895.307525",
RUB: "19351.4774035",
RWF: "243426.179717085",
SAR: "934.309544225",
SBD: "2013.0584566",
SCR: "4561.388071665",
SEK: "2201.10291435",
SGD: "338.356712025",
SHP: "192.55642863",
SLL: "2482430.971263275",
SOS: "144000.839307095",
SRD: "3525.97371",
SSP: "32449.7199",
STD: "5232524.31108792",
SVC: "2178.147216215",
SZL: "4091.741526765",
THB: "7785.4665375",
TJS: "2568.96954016",
TMT: "871.9025",
TND: "686.00043125",
TOP: "578.0165522",
TRY: "1963.587954325",
TTD: "1689.460313635",
TWD: "7158.9423125",
TZS: "577510.41827928",
UAH: "7061.44019619",
UGX: "931508.2111739",
UMA: "30.09543944427665349095",
UNI: "79.18593747516647884725",
USD: "249.115",
USDC: "249.115",
UYU: "10688.478117885",
UZS: "2582693.606121005",
VEF: "61901998.996866715",
VES: "112695476.713406465",
VND: "5773814.12282902",
VUV: "28486.73470656",
WST: "653.35042289",
XAF: "138884.3079243",
XAG: "10.22661168355",
XAU: "0.1312935696",
XCD: "673.24574325",
XDR: "176.50246157",
XLM: "2936.20532162536435083",
XOF: "138884.3079243",
XPD: "0.1061030608",
XPF: "25265.84267069",
XPT: "0.2910211253",
XRP: "1014.931757995518373565",
XTZ: "113.8135051169590628124",
YER: "62365.930534515",
YFI: "0.0178703370267443543872",
ZAR: "4122.31765275",
ZEC: "3.87305659203980154283",
ZMK: "1308619.842149325",
ZMW: "5027.95231667",
ZRX: "644.844402797695193656",
ZWL: "80215.03"
}
module.exports = {
mockRates
}
+49 -18
View File
@@ -1,28 +1,59 @@
const chai = require("chai")
const assert = require("assert")
const assert = require("chai").assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
const axios = require("axios")
const sinon = require("sinon")
const mockDataLib = require("./fixtures/price-mocks")
let mockData
describe("#Price", () => {
let sandbox
let bchjs
beforeEach(() => {
sandbox = sinon.createSandbox()
bchjs = new BCHJS()
mockData = Object.assign({}, mockDataLib)
})
afterEach(() => sandbox.restore())
describe("#current", () => {
describe("#single currency", () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
it("should get current price for single currency", async () => {
sandbox
.stub(bchjs.Price.axios, "get")
.resolves({ data: { price: 24905 } })
it("should get current price for single currency", done => {
const data = 46347
const resolved = new Promise(r => r({ data: data }))
sandbox.stub(axios, "get").returns(resolved)
const result = await bchjs.Price.current("usd")
// console.log(result)
bchjs.Price.current("usd")
.then(result => {
assert.deepEqual(data.price, result)
})
.then(done, done)
})
assert.isNumber(result)
})
})
describe("#getUsd", () => {
it("should get the USD price of BCH", async () => {
sandbox.stub(bchjs.Price.axios, "get").resolves({ data: { usd: 249.87 } })
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe("#rates", () => {
it("should get the price of BCH in several currencies", async () => {
sandbox
.stub(bchjs.Price.axios, "get")
.resolves({ data: mockData.mockRates })
const result = await bchjs.Price.rates()
// console.log(result)
assert.property(result, "USD")
assert.property(result, "CAD")
})
})
})
+129
View File
@@ -1735,6 +1735,36 @@ describe("#SLP Utils", () => {
assert.equal(data[0].isValid, null)
assert.equal(data[1].isValid, null)
})
// it("should handle a dust attack", async () => {
it("#dustattack", async () => {
// Mock external dependencies.
// Stub the calls to decodeOpReturn.
sandbox
.stub(slp.Utils, "decodeOpReturn")
.rejects(new Error("lokad id wrong size"))
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await slp.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#txDetails", () => {
@@ -1815,4 +1845,103 @@ describe("#SLP Utils", () => {
}
})
})
describe("#validateTxid2", () => {
it("should throw an error if the input is an array", async () => {
try {
const txid = [
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
]
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "txid must be 64 character string")
}
})
it("should throw an error for a malformed txid", async () => {
try {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783"
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "txid must be 64 character string")
}
})
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
// Mock live network calls.
sandbox.stub(axios, "get").resolves({
data: {
txid: txid,
isValid: false,
msg: ""
}
})
const result = await slp.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.property(result, "txid")
assert2.equal(result.txid, txid)
assert2.property(result, "isValid")
assert2.equal(result.isValid, false)
})
it("should validate a known valid TXID", async () => {
const txid =
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
// Mock live network calls.
sandbox.stub(axios, "get").resolves({
data: {
txid: txid,
isValid: true,
msg: ""
}
})
const result = await slp.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.property(result, "txid")
assert2.equal(result.txid, txid)
assert2.property(result, "isValid")
assert2.equal(result.isValid, true)
})
// slp-validate can take a long time. bch-api cuts it off if it fails to
// return is less than 10 seconds. This test case handle this situation.
it("should handle timeout errors", async () => {
try {
const txid =
"eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579"
// Mock live network calls.
sandbox.stub(axios, "get").rejects({
error:
"Network error: Could not communicate with full node or other external service."
})
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "slp-validate timed out")
}
})
})
})