mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 00:52:01 -07:00
Compare commits
61
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98029f74f8 | ||
|
|
9348ccb7de | ||
|
|
6a6f5fa5fa | ||
|
|
9863fad6a6 | ||
|
|
3a2f38bcf0 | ||
|
|
bc5c7052a1 | ||
|
|
89d5171e92 | ||
|
|
c80a0dcde4 | ||
|
|
a2f7ae7fb4 | ||
|
|
4c2c7a4f6f | ||
|
|
6efb08565a | ||
|
|
8a8dcbe593 | ||
|
|
ae58895d71 | ||
|
|
a50ace320b | ||
|
|
b1d583cfe4 | ||
|
|
0b86a19726 | ||
|
|
eb3bc7bcff | ||
|
|
c5361e0516 | ||
|
|
54d9233f41 | ||
|
|
37a5f0e6fa | ||
|
|
2180b14e1f | ||
|
|
40e1704572 | ||
|
|
9539119c57 | ||
|
|
b397381c7d | ||
|
|
6cdb9a0867 | ||
|
|
01f45d7103 | ||
|
|
ba38d3e107 | ||
|
|
cbb0d0a8ca | ||
|
|
7ec8442540 | ||
|
|
be3094fc77 | ||
|
|
d35f0e5473 | ||
|
|
79088fc68d | ||
|
|
fe84729737 | ||
|
|
2ec72e73c1 | ||
|
|
a2c121bf3b | ||
|
|
fd6f58fd45 | ||
|
|
e8b2f88ff5 | ||
|
|
4a11db7c45 | ||
|
|
af118f8a62 | ||
|
|
1f41b67e3b | ||
|
|
054fb9c14e | ||
|
|
1fd4095dcb | ||
|
|
0eb1f74ba1 | ||
|
|
1a13c41c22 | ||
|
|
8b6952b1d4 | ||
|
|
73dd2c010d | ||
|
|
70743876d2 | ||
|
|
ab51afb2a8 | ||
|
|
8a59374831 | ||
|
|
32b8efa7a2 | ||
|
|
b0bdc50193 | ||
|
|
0a897f19bb | ||
|
|
d8e746acd9 | ||
|
|
47bd0d66bc | ||
|
|
6a4a861c07 | ||
|
|
caee2db99e | ||
|
|
392f51b166 | ||
|
|
8facfd69ac | ||
|
|
7f1b87215c | ||
|
|
662e53aaa2 | ||
|
|
98fc4b9530 |
+4
-3
@@ -9,13 +9,14 @@
|
||||
"main": "src/bch-js",
|
||||
"scripts": {
|
||||
"test": "nyc mocha --timeout 30000 test/unit/",
|
||||
"test:integration": "mocha --timeout 30000 test/integration/",
|
||||
"test:integration:testnet": "mocha --timeout 30000 test/integration/testnet/",
|
||||
"test:integration": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/",
|
||||
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/testnet/",
|
||||
"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 test/integration/testnet/ninsight.js",
|
||||
"test:temp": "mocha --timeout 30000 -g '#hydrateUtxos' 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
@@ -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)
|
||||
|
||||
@@ -391,6 +391,204 @@ class ElectrumX {
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.blockHeader() blockHeader()
|
||||
* @apiName ElectrumX Block headers
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Return block headers for a given height
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let headers = await bchjs.Electrumx.blockHeaders(42);
|
||||
* console.log(headers);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* headers = {
|
||||
* "success": true,
|
||||
* "headers": [
|
||||
* "010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6",
|
||||
* "01000000f528fac1bcb685d0cd6c792320af0300a5ce15d687c7149548904e31000000004e8985a786d864f21e9cbb7cbdf4bc9265fe681b7a0893ac55a8e919ce035c2f85de6849ffff001d385ccb7c"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* let headers = await bchjs.Electrumx.blockHeaders(42, 1);
|
||||
* console.log(headers);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* headers = {
|
||||
* "success": true,
|
||||
* "headers": [
|
||||
* "010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6"
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async blockHeader(height, count = 1) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.txData() txData()
|
||||
* @apiName ElectrumX txData
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Returns an object with transaction details of the TXID
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let result = await bchjs.Electrumx.txData('4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251')
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* result = {
|
||||
* "success": true,
|
||||
* "details": {
|
||||
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
|
||||
* "blocktime": 1578327094,
|
||||
* "confirmations": 31861,
|
||||
* "hash": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
* ...
|
||||
* "vin": [
|
||||
* {
|
||||
* "scriptSig": {
|
||||
* ...
|
||||
* "vout": [
|
||||
* {
|
||||
* "n": 0,
|
||||
* "scriptPubKey": {
|
||||
* "addresses": [
|
||||
* "bitcoincash: pqvfecpwxvj53ayqfwkxtjaxsgpvnklcyg8xewk9hl"
|
||||
* ],
|
||||
* }
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* let result = await bchjs.Electrumx.txData(['4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'])
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* result = {
|
||||
* "transactions": [
|
||||
* {
|
||||
* "txid": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
* "details": {
|
||||
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
|
||||
* "blocktime": 1578327094,
|
||||
* "confirmations": 31861,
|
||||
* "hash": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
* ...
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* "txid": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
* "details": {
|
||||
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
|
||||
* "blocktime": 1578327094,
|
||||
* ...
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
async txData(txid) {
|
||||
try {
|
||||
// Handle single transaction.
|
||||
if (typeof txid === "string") {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/tx/data/${txid}`,
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/tx/data`,
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
@@ -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:
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ class Ninsight {
|
||||
// Handle single address.
|
||||
if (typeof address === "string") {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo/${address}`,
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
|
||||
+125
-22
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +426,7 @@ class TokenType1 {
|
||||
|
||||
return slpSendObj
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
// console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
+322
-22
@@ -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,8 +824,11 @@ class Utils {
|
||||
}
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
|
||||
if (cache) cache[txid] = tokenData
|
||||
|
||||
return tokenData
|
||||
} catch (error) {
|
||||
// console.log('decodeOpReturn error: ', error)
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
throw error
|
||||
}
|
||||
@@ -766,18 +847,20 @@ class Utils {
|
||||
* set to false.
|
||||
* If the UTXO is part of an SLP transaction, it will return the UTXO object
|
||||
* with additional SLP information attached. An `isValid` property will be included.
|
||||
* If its value is true, the UTXO is a valid SLP UTXO. If the value is null,
|
||||
* then SLPDB has not yet processed that txid and validity has not been confirmed.
|
||||
* If its value is true, the UTXO is a valid SLP UTXO.
|
||||
* If the isValid value is null,
|
||||
* then SLPDB has not yet processed that txid and validity has not been confirmed,
|
||||
* or a 429 rate-limit error was enountered during the processing of the request.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* const utxos = await bchjs.Blockbook.utxos(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
|
||||
* const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
|
||||
*
|
||||
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
*
|
||||
* console.log(`utxoInfo: ${JSON.stringify(isSLPUtxo,null,2)}`)
|
||||
* console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
|
||||
* } catch (error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -810,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.
|
||||
@@ -867,15 +954,33 @@ 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: `, err)
|
||||
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
|
||||
|
||||
// An error will be thrown if the txid is not SLP.
|
||||
// Mark as false and continue the loop.
|
||||
// outAry.push(false)
|
||||
utxo.isValid = false
|
||||
outAry.push(utxo)
|
||||
// If error is for some other reason, like a 429 error, mark utxo as 'null'
|
||||
// to display the unknown state.
|
||||
if (
|
||||
!err.message ||
|
||||
(err.message.indexOf("scriptpubkey not op_return") === -1 &&
|
||||
err.message.indexOf("lokad id") === -1)
|
||||
) {
|
||||
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
|
||||
|
||||
utxo.isValid = null
|
||||
outAry.push(utxo)
|
||||
|
||||
// If error is thrown because there is no OP_RETURN, then it's not
|
||||
// an SLP UTXO.
|
||||
// Mark as false and continue the loop.
|
||||
} else {
|
||||
utxo.isValid = false
|
||||
outAry.push(utxo)
|
||||
}
|
||||
|
||||
// Halt the execution of the loop and increase to the next index.
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -935,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
|
||||
@@ -983,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)}`)
|
||||
@@ -1010,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
|
||||
@@ -1023,6 +1138,191 @@ class Utils {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api SLP.Utils.hydrateUtxos() hydrateUtxos()
|
||||
* @apiName hydrateUtxos
|
||||
* @apiGroup SLP Utils
|
||||
* @apiDescription Hydrate a UTXO with SLP token metadata.
|
||||
*
|
||||
* The same as tokenUtxoDetails(), but uses bch-api to do the heavy lifting,
|
||||
* which greatly reduces the number of API calls.
|
||||
*
|
||||
* Expects an array of UTXO objects as input. Returns an array of equal size.
|
||||
* Returns UTXO data hydrated with token information.
|
||||
* If the
|
||||
* UTXO does not belong to a SLP transaction, it will return an `isValid` property
|
||||
* set to false.
|
||||
* If the UTXO is part of an SLP transaction, it will return the UTXO object
|
||||
* with additional SLP information attached. An `isValid` property will be included.
|
||||
* If its value is true, the UTXO is a valid SLP UTXO. \
|
||||
* If the isValid value is null,
|
||||
* then SLPDB has not yet processed that txid and validity has not been confirmed,
|
||||
* or a 429 rate-limit error was enountered during the processing of the request.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* const utxos = await bchjs.Electrumx.utxo([
|
||||
* "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg",
|
||||
* "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89",
|
||||
* "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
|
||||
* ])
|
||||
*
|
||||
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
|
||||
*
|
||||
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
|
||||
* } catch (error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* {
|
||||
* "slpUtxos": [
|
||||
* {
|
||||
* "utxos": [
|
||||
* {
|
||||
* "height": 654522,
|
||||
* "tx_hash": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
|
||||
* "tx_pos": 0,
|
||||
* "value": 6000,
|
||||
* "satoshis": 6000,
|
||||
* "txid": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
|
||||
* "vout": 0,
|
||||
* "isValid": false
|
||||
* }
|
||||
* ],
|
||||
* "address": "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg"
|
||||
* },
|
||||
* {
|
||||
* "utxos": [
|
||||
* {
|
||||
* "height": 654522,
|
||||
* "tx_hash": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
|
||||
* "tx_pos": 1,
|
||||
* "value": 546,
|
||||
* "satoshis": 546,
|
||||
* "txid": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
|
||||
* "vout": 1,
|
||||
* "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"
|
||||
* },
|
||||
* {
|
||||
* "utxos": [
|
||||
* {
|
||||
* "height": 654522,
|
||||
* "tx_hash": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
||||
* "tx_pos": 0,
|
||||
* "value": 6999,
|
||||
* "satoshis": 6999,
|
||||
* "txid": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
||||
* "vout": 0,
|
||||
* "isValid": false
|
||||
* },
|
||||
* {
|
||||
* "height": 654522,
|
||||
* "tx_hash": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
||||
* "tx_pos": 1,
|
||||
* "value": 546,
|
||||
* "satoshis": 546,
|
||||
* "txid": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
||||
* "vout": 1,
|
||||
* "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"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* (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.")
|
||||
|
||||
const response = await axios.post(
|
||||
`${this.restURL}slp/hydrateUtxos`,
|
||||
{
|
||||
utxos: utxos
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Utils
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Integration tests for the Blockbook library.
|
||||
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
//const axios = require("axios")
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#Blockbook`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result.txids)
|
||||
})
|
||||
|
||||
it(`should POST request balances for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"address",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxs",
|
||||
"txs",
|
||||
"txids"
|
||||
])
|
||||
assert.isArray(result[0].txids)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.balance(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Blockbook.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7"
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
|
||||
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0])
|
||||
assert.hasAnyKeys(result[0][0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"value",
|
||||
"height",
|
||||
"confirmations"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Blockbook.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
- getMempoolEntry() only works on TXs in the mempool, so it needs to be part
|
||||
of an e2e test to be properly tested.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#blockchain`, () => {
|
||||
describe(`#getBestBlockHash`, () => {
|
||||
it(`should GET best block hash`, async () => {
|
||||
const result = await bchjs.Blockchain.getBestBlockHash()
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result.length, 64, "Specific hash length")
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getBlockHeader", () => {
|
||||
it(`should GET block header for a single hash`, async () => {
|
||||
const hash =
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should GET block headers for an array of hashes`, async () => {
|
||||
const hash = [
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201",
|
||||
"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(hash)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], [
|
||||
"hash",
|
||||
"confirmations",
|
||||
"height",
|
||||
"version",
|
||||
"versionHex",
|
||||
"merkleroot",
|
||||
"time",
|
||||
"mediantime",
|
||||
"nonce",
|
||||
"bits",
|
||||
"difficulty",
|
||||
"chainwork",
|
||||
"previousblockhash",
|
||||
"nextblockhash",
|
||||
"nTx"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const hash = 12345
|
||||
|
||||
await bchjs.Blockchain.getBlockHeader(hash)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hash must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getMempoolEntry", () => {
|
||||
/*
|
||||
// To run this test, the txid must be unconfirmed.
|
||||
const txid =
|
||||
"defea04c38ee00cf73ad402984714ed22dc0dd99b2ae5cb50d791d94343ba79b"
|
||||
|
||||
it(`should GET single mempool entry`, async () => {
|
||||
const result = await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"size",
|
||||
"fee",
|
||||
"modifiedfee",
|
||||
"time",
|
||||
"height",
|
||||
"startingpriority",
|
||||
"currentpriority",
|
||||
"descendantcount",
|
||||
"descendantsize",
|
||||
"descendantfees",
|
||||
"ancestorcount",
|
||||
"ancestorsize",
|
||||
"ancestorfees",
|
||||
"depends"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should get an array of mempool entries`, async () => {
|
||||
const result = await bchjs.Blockchain.getMempoolEntry([txid, txid])
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"size",
|
||||
"fee",
|
||||
"modifiedfee",
|
||||
"time",
|
||||
"height",
|
||||
"startingpriority",
|
||||
"currentpriority",
|
||||
"descendantcount",
|
||||
"descendantsize",
|
||||
"descendantfees",
|
||||
"ancestorcount",
|
||||
"ancestorsize",
|
||||
"ancestorfees",
|
||||
"depends"
|
||||
])
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw an error if txid is not in mempool`, async () => {
|
||||
try {
|
||||
const txid =
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, `Transaction not in mempool`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#getTxOutProof`, () => {
|
||||
it(`should get single tx out proof`, async () => {
|
||||
const txid =
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it(`should get an array of tx out proofs`, async () => {
|
||||
const txid = [
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7",
|
||||
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"
|
||||
]
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#verifyTxOutProof`, () => {
|
||||
const mockTxOutProof =
|
||||
"0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01"
|
||||
|
||||
it(`should verify a single proof`, async () => {
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
)
|
||||
})
|
||||
|
||||
it(`should verify an array of proofs`, async () => {
|
||||
const proofs = [mockTxOutProof, mockTxOutProof]
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
assert.equal(
|
||||
result[0],
|
||||
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
|
||||
)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.verifyTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
|
||||
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getTxOut", () => {
|
||||
it("should get information on an unspent tx", async () => {
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8",
|
||||
0,
|
||||
true
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"bestblock",
|
||||
"confirmations",
|
||||
"value",
|
||||
"scriptPubKey",
|
||||
"coinbase"
|
||||
])
|
||||
})
|
||||
|
||||
it("should get information on a spent tx", async () => {
|
||||
const result = await bchjs.Blockchain.getTxOut(
|
||||
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871",
|
||||
0,
|
||||
true
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Integration tests for bchjs control library.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#control`, () => {
|
||||
describe(`#getNetworkInfo`, () => {
|
||||
it("should get info on the full node", async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "version")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,356 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0], "value")
|
||||
})
|
||||
|
||||
it(`should POST request for UTXOs for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_pos")
|
||||
assert.property(result.utxos[0].utxos[0], "value")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Electrumx.utxo(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#balance`, () => {
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balance")
|
||||
assert.property(result.balance, "confirmed")
|
||||
assert.property(result.balance, "unconfirmed")
|
||||
})
|
||||
|
||||
it(`should POST request for balances for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "balances")
|
||||
assert.isArray(result.balances)
|
||||
|
||||
assert.property(result.balances[0], "address")
|
||||
assert.property(result.balances[0], "balance")
|
||||
assert.property(result.balances[0].balance, "confirmed")
|
||||
assert.property(result.balances[0].balance, "unconfirmed")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#transactions`, () => {
|
||||
it(`should GET transaction history for a single address`, async () => {
|
||||
const addr = "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
assert.property(result.transactions[0], "height")
|
||||
assert.property(result.transactions[0], "tx_hash")
|
||||
})
|
||||
|
||||
it(`should POST request for transaction history for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj",
|
||||
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "address")
|
||||
assert.property(result.transactions[0], "transactions")
|
||||
assert.isArray(result.transactions[0].transactions)
|
||||
assert.property(result.transactions[0].transactions[0], "height")
|
||||
assert.property(result.transactions[0].transactions[0], "tx_hash")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0")
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#unconfirmed`, () => {
|
||||
// These tests won't work because unconfirmed transactions are transient in nature.
|
||||
/*
|
||||
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv"
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0], "fee")
|
||||
})
|
||||
|
||||
it(`should POST request for unconfirmed UTXOs (mempool) for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv",
|
||||
"bitcoincash:qpyrtsl9msdu2klgfpcmn2v5r22w9rk24g8pal74ts"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], "utxos")
|
||||
assert.isArray(result.utxos[0].utxos)
|
||||
assert.property(result.utxos[0], "address")
|
||||
|
||||
assert.property(result.utxos[0].utxos[0], "height")
|
||||
assert.property(result.utxos[0].utxos[0], "tx_hash")
|
||||
assert.property(result.utxos[0].utxos[0], "fee")
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
|
||||
|
||||
await bchjs.Electrumx.unconfirmed(addr)
|
||||
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should GET block headers for given height and count`, async () => {
|
||||
const height = 42
|
||||
const count = 2
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height, count)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 2)
|
||||
})
|
||||
|
||||
it(`should GET block headers for given height with default count = 1`, async () => {
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid =
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
const txids = [
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
txids.push(
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
)
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
const txHex =
|
||||
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
assert.property(err, "success")
|
||||
assert.equal(err.success, false)
|
||||
assert.include(
|
||||
err.error,
|
||||
"the transaction was rejected by network rules"
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
const assert = require("chai").assert
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe("#Encryption", () => {
|
||||
describe("#getPubKey", () => {
|
||||
it("should get a public key", async () => {
|
||||
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
assert.property(result, "publicKey")
|
||||
})
|
||||
|
||||
it("should report when public key can not be found", async () => {
|
||||
const addr = "bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh"
|
||||
|
||||
const result = await bchjs.encryption.getPubKey(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, false)
|
||||
assert.property(result, "publicKey")
|
||||
assert.equal(result.publicKey, "not found")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const sinon = require("sinon")
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
|
||||
|
||||
describe(`#Ninsight`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0].utxos[0], "txid")
|
||||
assert.property(result[0].utxos[0], "vout")
|
||||
assert.property(result[0].utxos[0], "amount")
|
||||
assert.property(result[0].utxos[0], "satoshis")
|
||||
assert.property(result[0].utxos[0], "height")
|
||||
assert.property(result[0].utxos[0], "confirmations")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
const addr = [
|
||||
"bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7",
|
||||
"bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr"
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
tests for OpenBazaar library.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#OpenBazaar`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.balance(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.OpenBazaar.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"page",
|
||||
"totalPages",
|
||||
"itemsOnPage",
|
||||
"addrStr",
|
||||
"balance",
|
||||
"totalReceived",
|
||||
"totalSent",
|
||||
"unconfirmedBalance",
|
||||
"unconfirmedTxApperances",
|
||||
"txApperances",
|
||||
"transactions"
|
||||
])
|
||||
assert.isArray(result.transactions)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
const result = await bchjs.OpenBazaar.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAllKeys(result[0], [
|
||||
"txid",
|
||||
"vout",
|
||||
"amount",
|
||||
"height",
|
||||
"confirmations",
|
||||
"satoshis"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.tx(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txid must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET transactions for a single address`, async () => {
|
||||
const addr =
|
||||
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
|
||||
|
||||
const result = await bchjs.OpenBazaar.tx(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"txid",
|
||||
"version",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"blockheight",
|
||||
"confirmations",
|
||||
"blocktime",
|
||||
"valueOut",
|
||||
"valueIn",
|
||||
"fees",
|
||||
"hex",
|
||||
"time"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
const assert = require("chai").assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe("#price", () => {
|
||||
describe("#current", () => {
|
||||
describe("#single currency", () => {
|
||||
it("should get current price for single currency", async () => {
|
||||
const result = await bchjs.Price.current("usd")
|
||||
assert.notEqual(0, result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getUsd", () => {
|
||||
it("should get the USD price of BCH", async () => {
|
||||
const result = await bchjs.Price.getUsd()
|
||||
// console.log(result)
|
||||
|
||||
assert.isNumber(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#rates", () => {
|
||||
it("should get the price of BCH in several currencies", async () => {
|
||||
const result = await bchjs.Price.rates()
|
||||
// console.log(result)
|
||||
|
||||
assert.property(result, "USD")
|
||||
assert.property(result, "CAD")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe("#rawtransaction", () => {
|
||||
describe("#decodeRawTransaction", () => {
|
||||
it("should decode tx for a single hex", async () => {
|
||||
const hex =
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of tx hexes", async () => {
|
||||
const hexes = [
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000",
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout"
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
data.push(
|
||||
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#getRawTransaction", () => {
|
||||
it("should decode a single txid, with concise output", async () => {
|
||||
const txid =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
const verbose = false
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it("should decode a single txid, with verbose output", async () => {
|
||||
const txid =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
const verbose = true
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
])
|
||||
assert.isArray(result.vin)
|
||||
assert.isArray(result.vout)
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a concise output", async () => {
|
||||
const txid = [
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
|
||||
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
|
||||
]
|
||||
const verbose = false
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it("should decode an array of txids, with a verbose output", async () => {
|
||||
const txid = [
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
|
||||
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
|
||||
]
|
||||
const verbose = true
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
verbose
|
||||
)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"hex",
|
||||
"txid",
|
||||
"hash",
|
||||
"size",
|
||||
"version",
|
||||
"locktime",
|
||||
"vin",
|
||||
"vout",
|
||||
"blockhash",
|
||||
"confirmations",
|
||||
"time",
|
||||
"blocktime"
|
||||
])
|
||||
assert.isArray(result[0].vin)
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.getRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#decodeScript", () => {
|
||||
it("should decode script for a single hex", async () => {
|
||||
const hex =
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeScript(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["asm", "type", "p2sh"])
|
||||
})
|
||||
|
||||
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
|
||||
// https://github.com/Bitcoin-com/rest.bitcoin.com/pull/312
|
||||
/*
|
||||
it("should decode an array of tx hexes", async () => {
|
||||
const hexes = [
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16",
|
||||
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.decodeScript(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
*/
|
||||
/*
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
/*
|
||||
Testing sentRawTransaction isn't really possible with an integration test,
|
||||
as the endpoint really needs an e2e test to be properly tested. The tests
|
||||
below expect error messages returned from the server, but at least test
|
||||
that the server is responding on those endpoints, and responds consistently.
|
||||
*/
|
||||
describe("sendRawTransaction", () => {
|
||||
it("should send a single transaction hex", async () => {
|
||||
try {
|
||||
const hex =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
//console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.fail("Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it("should send an array of tx hexes", async () => {
|
||||
try {
|
||||
const hexes = [
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000",
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.fail("Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hex must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock =
|
||||
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,700 @@
|
||||
/*
|
||||
Integration tests for the bchjs covering SLP tokens.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
let bchjs
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 1
|
||||
}
|
||||
|
||||
describe(`#SLP`, () => {
|
||||
// before(() => {
|
||||
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
|
||||
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
|
||||
// })
|
||||
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
await sleep(1000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe("#util", () => {
|
||||
describe("#list", () => {
|
||||
it(`should get information on the Spice token`, async () => {
|
||||
const tokenId = `4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf`
|
||||
|
||||
const result = await bchjs.SLP.Utils.list(tokenId)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"decimals",
|
||||
"timestamp",
|
||||
"timestamp_unix",
|
||||
"versionType",
|
||||
"documentUri",
|
||||
"symbol",
|
||||
"name",
|
||||
"containsBaton",
|
||||
"id",
|
||||
"documentHash",
|
||||
"initialTokenQty",
|
||||
"blockCreated",
|
||||
"blockLastActiveSend",
|
||||
"blockLastActiveMint",
|
||||
"txnsSinceGenesis",
|
||||
"validAddress",
|
||||
"totalMinted",
|
||||
"totalBurned",
|
||||
"circulatingSupply",
|
||||
"mintingBatonStatus"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
// describe("#decodeOpReturn", () => {
|
||||
// it("should decode the OP_RETURN for a SEND txid", async () => {
|
||||
// const txid =
|
||||
// "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
|
||||
//
|
||||
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAllKeys(result, [
|
||||
// "tokenType",
|
||||
// "transactionType",
|
||||
// "tokenId",
|
||||
// "spendData"
|
||||
// ])
|
||||
// })
|
||||
//
|
||||
// it("should decode the OP_RETURN for a GENESIS txid", async () => {
|
||||
// const txid =
|
||||
// "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
//
|
||||
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAllKeys(result, [
|
||||
// "tokenType",
|
||||
// "transactionType",
|
||||
// "ticker",
|
||||
// "name",
|
||||
// "documentUrl",
|
||||
// "documentHash",
|
||||
// "decimals",
|
||||
// "mintBatonVout",
|
||||
// "initialQty",
|
||||
// "tokensSentTo",
|
||||
// "batonHolder"
|
||||
// ])
|
||||
// })
|
||||
//
|
||||
// it("should decode the OP_RETURN for a MINT txid", async () => {
|
||||
// const txid =
|
||||
// "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4"
|
||||
//
|
||||
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAllKeys(result, [
|
||||
// "tokenType",
|
||||
// "transactionType",
|
||||
// "tokenId",
|
||||
// "mintBatonVout",
|
||||
// "batonStillExists",
|
||||
// "quantity",
|
||||
// "tokensSentTo",
|
||||
// "batonHolder"
|
||||
// ])
|
||||
// })
|
||||
//
|
||||
// it("should throw an error for a non-SLP transaction", async () => {
|
||||
// try {
|
||||
// const txid =
|
||||
// "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec"
|
||||
//
|
||||
// await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
//
|
||||
// assert.equal(true, false, "Unexpected result")
|
||||
// } catch (err) {
|
||||
// // console.log(`err: `, err)
|
||||
// assert.include(err.message, "Not an OP_RETURN")
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
describe("#decodeOpReturn", () => {
|
||||
it("should decode the OP_RETURN for a SEND txid", async () => {
|
||||
const txid =
|
||||
"266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
|
||||
|
||||
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"])
|
||||
assert.equal(
|
||||
result.tokenId,
|
||||
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
)
|
||||
|
||||
// Verify outputs
|
||||
assert.equal(result.amounts.length, 2)
|
||||
assert.equal(result.amounts[0], "100000000")
|
||||
assert.equal(result.amounts[1], "99883300000000")
|
||||
})
|
||||
|
||||
it("should decode the OP_RETURN for a GENESIS txid", async () => {
|
||||
const txid =
|
||||
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
|
||||
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"tokenType",
|
||||
"txType",
|
||||
"tokenId",
|
||||
"ticker",
|
||||
"name",
|
||||
"documentUri",
|
||||
"documentHash",
|
||||
"decimals",
|
||||
"mintBatonVout",
|
||||
"qty"
|
||||
])
|
||||
assert.equal(
|
||||
result.tokenId,
|
||||
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
|
||||
)
|
||||
assert.equal(result.txType, "GENESIS")
|
||||
assert.equal(result.ticker, "TOK-CH")
|
||||
assert.equal(result.name, "TokyoCash")
|
||||
})
|
||||
|
||||
it("should decode the OP_RETURN for a MINT txid", async () => {
|
||||
const txid =
|
||||
"65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4"
|
||||
|
||||
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
"tokenType",
|
||||
"txType",
|
||||
"tokenId",
|
||||
"mintBatonVout",
|
||||
"qty"
|
||||
])
|
||||
})
|
||||
|
||||
it("should throw an error for a non-SLP transaction", async () => {
|
||||
try {
|
||||
const txid =
|
||||
"3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec"
|
||||
|
||||
await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
|
||||
assert.equal(true, false, "Unexpected result")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, "scriptpubkey not op_return")
|
||||
}
|
||||
})
|
||||
|
||||
// Note: This TX is interpreted as valid by the original decodeOpReturn().
|
||||
// Fixing this issue and related issues was the reason for creating the
|
||||
// decodeOpReturn2() method using the slp-parser library.
|
||||
it("should throw error for invalid SLP transaction", async () => {
|
||||
try {
|
||||
const txid =
|
||||
"a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415"
|
||||
|
||||
await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, "amount string size not 8 bytes")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#tokenUtxoDetails", () => {
|
||||
// // This captures an important corner-case. When an SLP token is created, the
|
||||
// // change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
|
||||
it("should return details on minting baton from genesis transaction", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
||||
vout: 3,
|
||||
amount: 0.00002015,
|
||||
satoshis: 2015,
|
||||
height: 594892,
|
||||
confirmations: 5
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
|
||||
vout: 2,
|
||||
amount: 0.00000546,
|
||||
satoshis: 546,
|
||||
height: 594892,
|
||||
confirmations: 5
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.equal(data[0].isValid, false, "Change UTXO marked as false.")
|
||||
|
||||
assert.property(data[1], "txid")
|
||||
assert.property(data[1], "vout")
|
||||
assert.property(data[1], "amount")
|
||||
assert.property(data[1], "satoshis")
|
||||
assert.property(data[1], "height")
|
||||
assert.property(data[1], "confirmations")
|
||||
assert.property(data[1], "utxoType")
|
||||
assert.property(data[1], "tokenId")
|
||||
assert.property(data[1], "tokenTicker")
|
||||
assert.property(data[1], "tokenName")
|
||||
assert.property(data[1], "tokenDocumentUrl")
|
||||
assert.property(data[1], "tokenDocumentHash")
|
||||
assert.property(data[1], "decimals")
|
||||
assert.property(data[1], "isValid")
|
||||
assert.equal(data[1].isValid, true)
|
||||
})
|
||||
|
||||
it("should return details for a MINT token utxo", async () => {
|
||||
// Mock the call to REST API
|
||||
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
|
||||
vout: 1,
|
||||
amount: 0.00000546,
|
||||
satoshis: 546,
|
||||
height: 600297,
|
||||
confirmations: 76
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.property(data[0], "txid")
|
||||
assert.property(data[0], "vout")
|
||||
assert.property(data[0], "amount")
|
||||
assert.property(data[0], "satoshis")
|
||||
assert.property(data[0], "height")
|
||||
assert.property(data[0], "confirmations")
|
||||
assert.property(data[0], "utxoType")
|
||||
assert.property(data[0], "transactionType")
|
||||
assert.property(data[0], "tokenId")
|
||||
assert.property(data[0], "tokenTicker")
|
||||
assert.property(data[0], "tokenName")
|
||||
assert.property(data[0], "tokenDocumentUrl")
|
||||
assert.property(data[0], "tokenDocumentHash")
|
||||
assert.property(data[0], "decimals")
|
||||
assert.property(data[0], "mintBatonVout")
|
||||
assert.property(data[0], "tokenQty")
|
||||
assert.property(data[0], "isValid")
|
||||
assert.equal(data[0].isValid, true)
|
||||
})
|
||||
|
||||
it("should return details for a simple SEND SLP token utxo", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
|
||||
vout: 1,
|
||||
amount: 0.00000546,
|
||||
satoshis: 546,
|
||||
height: 596089,
|
||||
confirmations: 748
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.property(data[0], "txid")
|
||||
assert.property(data[0], "vout")
|
||||
assert.property(data[0], "amount")
|
||||
assert.property(data[0], "satoshis")
|
||||
assert.property(data[0], "height")
|
||||
assert.property(data[0], "confirmations")
|
||||
assert.property(data[0], "utxoType")
|
||||
assert.property(data[0], "tokenId")
|
||||
assert.property(data[0], "tokenTicker")
|
||||
assert.property(data[0], "tokenName")
|
||||
assert.property(data[0], "tokenDocumentUrl")
|
||||
assert.property(data[0], "tokenDocumentHash")
|
||||
assert.property(data[0], "decimals")
|
||||
assert.property(data[0], "tokenQty")
|
||||
assert.property(data[0], "isValid")
|
||||
assert.equal(data[0].isValid, true)
|
||||
})
|
||||
|
||||
it("should handle BCH and SLP utxos in the same TX", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 3,
|
||||
value: "6816",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 6816
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 2,
|
||||
value: "546",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 546
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 2)
|
||||
assert.equal(result[0].isValid, false)
|
||||
assert.equal(result[1].isValid, true)
|
||||
})
|
||||
|
||||
it("should handle problematic utxos", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959",
|
||||
vout: 0,
|
||||
amount: 0.00018362,
|
||||
satoshis: 18362,
|
||||
height: 613483,
|
||||
confirmations: 124
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
|
||||
vout: 1,
|
||||
amount: 0.00000546,
|
||||
satoshis: 546,
|
||||
height: 612075,
|
||||
confirmations: 1532
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 2)
|
||||
assert.equal(result[0].isValid, false)
|
||||
assert.equal(result[1].isValid, true)
|
||||
})
|
||||
|
||||
it("should return false for BCH-only UTXOs", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
"a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b",
|
||||
vout: 0,
|
||||
amount: 0.00001,
|
||||
satoshis: 1000,
|
||||
confirmations: 0,
|
||||
ts: 1578424704
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44",
|
||||
vout: 0,
|
||||
amount: 0.00001,
|
||||
satoshis: 1000,
|
||||
confirmations: 0,
|
||||
ts: 1578424634
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.isArray(data)
|
||||
assert.equal(data[0].isValid, false)
|
||||
assert.equal(data[1].isValid, false)
|
||||
})
|
||||
|
||||
it("should handle a dust attack", async () => {
|
||||
// it("#dustattack", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
height: 655965,
|
||||
tx_hash:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
tx_pos: 151,
|
||||
value: 547,
|
||||
satoshis: 547,
|
||||
txid:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
vout: 151,
|
||||
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
|
||||
hdIndex: 11
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.equal(data[0].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#balancesForAddress", () => {
|
||||
it(`should throw an error if input is not a string or array of strings`, async () => {
|
||||
try {
|
||||
const address = 1234
|
||||
|
||||
await bchjs.SLP.Utils.balancesForAddress(address)
|
||||
|
||||
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
//console.log(`Error: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
||||
const balances = await bchjs.SLP.Utils.balancesForAddress(
|
||||
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
|
||||
)
|
||||
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
assert.isArray(balances)
|
||||
assert.hasAllKeys(balances[0], [
|
||||
"tokenId",
|
||||
"balanceString",
|
||||
"balance",
|
||||
"decimalCount",
|
||||
"slpAddress"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should fetch balances for multiple addresses`, async () => {
|
||||
const addresses = [
|
||||
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9",
|
||||
"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57"
|
||||
]
|
||||
|
||||
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
|
||||
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
|
||||
|
||||
assert.isArray(balances)
|
||||
assert.isArray(balances[0])
|
||||
assert.hasAllKeys(balances[0][0], [
|
||||
"tokenId",
|
||||
"balanceString",
|
||||
"balance",
|
||||
"decimalCount",
|
||||
"slpAddress"
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hydrateUtxos", () => {
|
||||
it("should throw an error if input is not an array", async () => {
|
||||
try {
|
||||
const utxos = 1234
|
||||
|
||||
await bchjs.SLP.Utils.hydrateUtxos(utxos)
|
||||
|
||||
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
// console.log(`Error: `, err)
|
||||
assert.include(err.message, `Input must be an array.`)
|
||||
}
|
||||
})
|
||||
|
||||
it("should hydrate UTXOs", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 3,
|
||||
value: "6816",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 6816
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 2,
|
||||
value: "546",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 546
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Test the general structure of the output.
|
||||
assert.isArray(result.slpUtxos)
|
||||
assert.equal(result.slpUtxos.length, 1)
|
||||
assert.equal(result.slpUtxos[0].utxos.length, 2)
|
||||
|
||||
// Test the non-slp UTXO.
|
||||
assert.property(result.slpUtxos[0].utxos[0], "txid")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "vout")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "value")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "height")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "confirmations")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "satoshis")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "isValid")
|
||||
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
||||
|
||||
// Test the slp UTXO.
|
||||
assert.property(result.slpUtxos[0].utxos[1], "txid")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "vout")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "value")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "height")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "confirmations")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "satoshis")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "isValid")
|
||||
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
|
||||
assert.property(result.slpUtxos[0].utxos[1], "transactionType")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenId")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenTicker")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenName")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentUrl")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentHash")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "decimals")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenType")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenQty")
|
||||
})
|
||||
|
||||
it("should process data directly from Electrumx", async () => {
|
||||
const addrs = [
|
||||
"bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp",
|
||||
"bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5",
|
||||
"bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495"
|
||||
]
|
||||
|
||||
const utxos = await bchjs.Electrumx.utxo(addrs)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Test the general structure of the output.
|
||||
assert.isArray(result.slpUtxos)
|
||||
assert.equal(result.slpUtxos.length, 3)
|
||||
assert.equal(result.slpUtxos[0].utxos.length, 1)
|
||||
assert.equal(result.slpUtxos[1].utxos.length, 1)
|
||||
assert.equal(result.slpUtxos[2].utxos.length, 2)
|
||||
|
||||
try {
|
||||
// Test the expected values.
|
||||
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
||||
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
|
||||
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT")
|
||||
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
|
||||
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
|
||||
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE")
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
|
||||
)
|
||||
console.log("Error: ", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#validateTxid2", () => {
|
||||
it("should invalidate a known invalid TXID", async () => {
|
||||
const txid =
|
||||
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "txid")
|
||||
assert.equal(result.txid, txid)
|
||||
|
||||
assert.property(result, "isValid")
|
||||
assert.equal(result.isValid, false)
|
||||
})
|
||||
|
||||
it("should validate a known valid TXID", async () => {
|
||||
const txid =
|
||||
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "txid")
|
||||
assert.equal(result.txid, txid)
|
||||
|
||||
assert.property(result, "isValid")
|
||||
assert.equal(result.isValid, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#tokentype1", () => {
|
||||
describe("#getHexOpReturn", () => {
|
||||
it("should return OP_RETURN object ", async () => {
|
||||
const tokenUtxos = [
|
||||
{
|
||||
tokenId:
|
||||
"0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1",
|
||||
decimals: 0,
|
||||
tokenQty: 2
|
||||
}
|
||||
]
|
||||
const sendQty = 1.5
|
||||
|
||||
const result = await bchjs.SLP.TokenType1.getHexOpReturn(
|
||||
tokenUtxos,
|
||||
sendQty
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "script")
|
||||
assert.isString(result.script)
|
||||
|
||||
assert.property(result, "outputs")
|
||||
assert.isNumber(result.outputs)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
*/
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#util`, () => {
|
||||
describe(`#validateAddress`, () => {
|
||||
it(`should return false for testnet addr on mainnet`, async () => {
|
||||
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should return false for bad address`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAllKeys(result, ["isvalid"])
|
||||
assert.equal(result.isvalid, false)
|
||||
})
|
||||
|
||||
it(`should validate valid address`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.hasAnyKeys(result, [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
assert.equal(result.isvalid, true)
|
||||
})
|
||||
|
||||
it(`should validate an array of addresses`, async () => {
|
||||
const address = [
|
||||
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`,
|
||||
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
]
|
||||
|
||||
const result = await bchjs.Util.validateAddress(address)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.hasAnyKeys(result[0], [
|
||||
"isvalid",
|
||||
"address",
|
||||
"scriptPubKey",
|
||||
//"ismine",
|
||||
//"iswatchonly",
|
||||
"isscript"
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const address = 15432
|
||||
|
||||
await bchjs.Util.validateAddress(address)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
const result = await bchjs.Util.validateAddress(data)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -18,6 +18,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -67,21 +71,6 @@ describe(`#Blockbook`, () => {
|
||||
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 = []
|
||||
@@ -136,21 +125,6 @@ describe(`#Blockbook`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
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 = []
|
||||
@@ -168,3 +142,7 @@ describe(`#Blockbook`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
describe(`#blockchain`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#getBestBlockHash`, () => {
|
||||
it(`should GET best block hash`, async () => {
|
||||
const result = await bchjs.Blockchain.getBestBlockHash()
|
||||
@@ -85,20 +89,6 @@ describe(`#blockchain`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const hash = 12345
|
||||
|
||||
await bchjs.Blockchain.getBlockHeader(hash)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hash must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
@@ -185,20 +175,6 @@ describe(`#blockchain`, () => {
|
||||
assert.include(err.error, `Transaction not in mempool`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#getTxOutProof`, () => {
|
||||
@@ -224,20 +200,6 @@ describe(`#blockchain`, () => {
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#verifyTxOutProof`, () => {
|
||||
@@ -269,20 +231,6 @@ describe(`#blockchain`, () => {
|
||||
)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.verifyTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
@@ -329,3 +277,7 @@ describe(`#blockchain`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@ const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#control`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#getNetworkInfo`, () => {
|
||||
it("should get info on the full node", async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
@@ -17,3 +21,7 @@ describe(`#control`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@ const bchjs = new BCHJS()
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
@@ -240,4 +246,121 @@ describe(`#ElectrumX`, () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should GET block headers for given height and count`, async () => {
|
||||
const height = 42
|
||||
const count = 2
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height, count)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 2)
|
||||
})
|
||||
|
||||
it(`should GET block headers for given height with default count = 1`, async () => {
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid =
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
const txids = [
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
txids.push(
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
)
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
const txHex =
|
||||
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
assert.property(err, "success")
|
||||
assert.equal(err.success, false)
|
||||
assert.include(
|
||||
err.error,
|
||||
"the transaction was rejected by network rules"
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe("#Encryption", () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#getPubKey", () => {
|
||||
it("should get a public key", async () => {
|
||||
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
|
||||
@@ -29,3 +33,7 @@ describe("#Encryption", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@ const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
|
||||
|
||||
describe(`#Ninsight`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
@@ -17,21 +23,21 @@ describe(`#Ninsight`, () => {
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, "legacyAddress")
|
||||
assert.property(result, "cashAddress")
|
||||
assert.property(result, "slpAddress")
|
||||
assert.property(result, "scriptPubKey")
|
||||
assert.property(result, "asm")
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result.utxos[0], "txid")
|
||||
assert.property(result.utxos[0], "vout")
|
||||
assert.property(result.utxos[0], "amount")
|
||||
assert.property(result.utxos[0], "satoshis")
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "confirmations")
|
||||
assert.property(result[0].utxos[0], "txid")
|
||||
assert.property(result[0].utxos[0], "vout")
|
||||
assert.property(result[0].utxos[0], "amount")
|
||||
assert.property(result[0].utxos[0], "satoshis")
|
||||
assert.property(result[0].utxos[0], "height")
|
||||
assert.property(result[0].utxos[0], "confirmations")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
@@ -55,3 +61,7 @@ describe(`#Ninsight`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -8,19 +8,11 @@ const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe(`#OpenBazaar`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#Balance`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.balance(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET balance for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
@@ -45,18 +37,6 @@ describe(`#OpenBazaar`, () => {
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET utxos for a single address`, async () => {
|
||||
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
|
||||
|
||||
@@ -76,18 +56,6 @@ describe(`#OpenBazaar`, () => {
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.tx(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txid must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET transactions for a single address`, async () => {
|
||||
const addr =
|
||||
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
|
||||
@@ -115,3 +83,7 @@ describe(`#OpenBazaar`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
const assert = require("assert")
|
||||
const assert = require("chai").assert
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe("#price", () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#current", () => {
|
||||
describe("#single currency", () => {
|
||||
it("should get current price for single currency", async () => {
|
||||
@@ -11,4 +15,27 @@ 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")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
describe("#rawtransaction", () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#decodeRawTransaction", () => {
|
||||
it("should decode tx for a single hex", async () => {
|
||||
const hex =
|
||||
@@ -63,21 +67,6 @@ describe("#rawtransaction", () => {
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
@@ -232,22 +221,6 @@ describe("#rawtransaction", () => {
|
||||
const result = await bchjs.RawTransactions.decodeScript(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
*/
|
||||
/*
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
@@ -271,7 +244,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,22 +261,7 @@ describe("#rawtransaction", () => {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hex must be a string or array of strings`
|
||||
)
|
||||
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -325,3 +283,7 @@ describe("#rawtransaction", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
+160
-18
@@ -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")
|
||||
@@ -24,7 +24,9 @@ describe(`#SLP`, () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe("#util", () => {
|
||||
@@ -437,25 +439,33 @@ 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", () => {
|
||||
it(`should throw an error if input is not a string or array of strings`, async () => {
|
||||
try {
|
||||
const address = 1234
|
||||
|
||||
await bchjs.SLP.Utils.balancesForAddress(address)
|
||||
|
||||
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
//console.log(`Error: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
||||
const balances = await bchjs.SLP.Utils.balancesForAddress(
|
||||
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
|
||||
@@ -492,6 +502,138 @@ describe(`#SLP`, () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hydrateUtxos", () => {
|
||||
it("should hydrate UTXOs", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 3,
|
||||
value: "6816",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 6816
|
||||
},
|
||||
{
|
||||
txid:
|
||||
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
vout: 2,
|
||||
value: "546",
|
||||
height: 606848,
|
||||
confirmations: 13,
|
||||
satoshis: 546
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Test the general structure of the output.
|
||||
assert.isArray(result.slpUtxos)
|
||||
assert.equal(result.slpUtxos.length, 1)
|
||||
assert.equal(result.slpUtxos[0].utxos.length, 2)
|
||||
|
||||
// Test the non-slp UTXO.
|
||||
assert.property(result.slpUtxos[0].utxos[0], "txid")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "vout")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "value")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "height")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "confirmations")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "satoshis")
|
||||
assert.property(result.slpUtxos[0].utxos[0], "isValid")
|
||||
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
||||
|
||||
// Test the slp UTXO.
|
||||
assert.property(result.slpUtxos[0].utxos[1], "txid")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "vout")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "value")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "height")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "confirmations")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "satoshis")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "isValid")
|
||||
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
|
||||
assert.property(result.slpUtxos[0].utxos[1], "transactionType")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenId")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenTicker")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenName")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentUrl")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentHash")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "decimals")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenType")
|
||||
assert.property(result.slpUtxos[0].utxos[1], "tokenQty")
|
||||
})
|
||||
|
||||
it("should process data directly from Electrumx", async () => {
|
||||
const addrs = [
|
||||
"bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp",
|
||||
"bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5",
|
||||
"bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495"
|
||||
]
|
||||
|
||||
const utxos = await bchjs.Electrumx.utxo(addrs)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Test the general structure of the output.
|
||||
assert.isArray(result.slpUtxos)
|
||||
assert.equal(result.slpUtxos.length, 3)
|
||||
assert.equal(result.slpUtxos[0].utxos.length, 1)
|
||||
assert.equal(result.slpUtxos[1].utxos.length, 1)
|
||||
assert.equal(result.slpUtxos[2].utxos.length, 2)
|
||||
|
||||
try {
|
||||
// Test the expected values.
|
||||
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
|
||||
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
|
||||
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT")
|
||||
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
|
||||
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
|
||||
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE")
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
|
||||
)
|
||||
console.log("Error: ", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#validateTxid2", () => {
|
||||
it("should invalidate a known invalid TXID", async () => {
|
||||
const txid =
|
||||
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "txid")
|
||||
assert.equal(result.txid, txid)
|
||||
|
||||
assert.property(result, "isValid")
|
||||
assert.equal(result.isValid, false)
|
||||
})
|
||||
|
||||
it("should validate a known valid TXID", async () => {
|
||||
const txid =
|
||||
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "txid")
|
||||
assert.equal(result.txid, txid)
|
||||
|
||||
assert.property(result, "isValid")
|
||||
assert.equal(result.isValid, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("#tokentype1", () => {
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
export RESTURL=https://bchn-free-main.fullstack.cash/v3/
|
||||
#export RESTURL=http://localhost:3000/v3/
|
||||
export IS_USING_FREE_TIER=true
|
||||
|
||||
cd test/integration/bchn/
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
export RESTURL=https://free-main.fullstack.cash/v3/
|
||||
#export RESTURL=http://localhost:3000/v3/
|
||||
export IS_USING_FREE_TIER=true
|
||||
|
||||
cd test/integration/
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
|
||||
|
||||
@@ -26,6 +26,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
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"
|
||||
@@ -165,7 +169,7 @@ describe(`#Blockbook`, () => {
|
||||
for (let i = 0; i < 25; i++)
|
||||
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
|
||||
|
||||
const result = await bchjs.Blockbook.utxo(addr)
|
||||
await bchjs.Blockbook.utxo(addr)
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
@@ -176,3 +180,7 @@ describe(`#Blockbook`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
: "https://tapi.fullstack.cash/v3/"
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
@@ -27,11 +27,15 @@ util.inspect.defaultOptions = {
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe(`#blockchain`, () => {
|
||||
describe(`#getBestBlockHash`, () => {
|
||||
it(`should GET best block hash`, async () => {
|
||||
describe("#blockchain", () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#getBestBlockHash", () => {
|
||||
it("should GET best block hash", async () => {
|
||||
const result = await bchjs.Blockchain.getBestBlockHash()
|
||||
//console.log(`result: ${util.inspect(result)}`)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
|
||||
assert.isString(result)
|
||||
assert.equal(result.length, 64, "Specific hash length")
|
||||
@@ -39,7 +43,7 @@ describe(`#blockchain`, () => {
|
||||
})
|
||||
|
||||
describe("#getBlockHeader", () => {
|
||||
it(`should GET block header for a single hash`, async () => {
|
||||
it("should GET block header for a single hash", async () => {
|
||||
const hash =
|
||||
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf"
|
||||
|
||||
@@ -64,7 +68,7 @@ describe(`#blockchain`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
it(`should GET block headers for an array of hashes`, async () => {
|
||||
it("should GET block headers for an array of hashes", async () => {
|
||||
const hash = [
|
||||
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf",
|
||||
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2"
|
||||
@@ -92,21 +96,7 @@ describe(`#blockchain`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const hash = 12345
|
||||
|
||||
await bchjs.Blockchain.getBlockHeader(hash)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hash must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it("should throw error on array size rate limit", async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
@@ -115,7 +105,7 @@ describe(`#blockchain`, () => {
|
||||
)
|
||||
}
|
||||
|
||||
const result = await bchjs.Blockchain.getBlockHeader(data)
|
||||
await bchjs.Blockchain.getBlockHeader(data)
|
||||
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
@@ -178,7 +168,7 @@ describe(`#blockchain`, () => {
|
||||
})
|
||||
*/
|
||||
|
||||
it(`should throw an error if txid is not in mempool`, async () => {
|
||||
it("should throw an error if txid is not in mempool", async () => {
|
||||
try {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
@@ -187,39 +177,25 @@ describe(`#blockchain`, () => {
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: ${util.inspect(err)}`)
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, `Transaction not in mempool`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getMempoolEntry(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
assert.include(err.error, "Transaction not in mempool")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#getTxOutProof`, () => {
|
||||
it(`should get single tx out proof`, async () => {
|
||||
describe("#getTxOutProof", () => {
|
||||
it("should get single tx out proof", async () => {
|
||||
const txid =
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
|
||||
|
||||
const result = await bchjs.Blockchain.getTxOutProof(txid)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
|
||||
it(`should get an array of tx out proofs`, async () => {
|
||||
it("should get an array of tx out proofs", async () => {
|
||||
const txid = [
|
||||
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04",
|
||||
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752"
|
||||
@@ -231,27 +207,13 @@ describe(`#blockchain`, () => {
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.getTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#verifyTxOutProof`, () => {
|
||||
describe("#verifyTxOutProof", () => {
|
||||
const mockTxOutProof =
|
||||
"00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135"
|
||||
|
||||
it(`should verify a single proof`, async () => {
|
||||
it("should verify a single proof", async () => {
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
@@ -263,10 +225,10 @@ describe(`#blockchain`, () => {
|
||||
)
|
||||
})
|
||||
|
||||
it(`should verify an array of proofs`, async () => {
|
||||
it("should verify an array of proofs", async () => {
|
||||
const proofs = [mockTxOutProof, mockTxOutProof]
|
||||
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
|
||||
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isString(result[0])
|
||||
@@ -276,21 +238,7 @@ describe(`#blockchain`, () => {
|
||||
)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Blockchain.verifyTxOutProof(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
it("should throw error on array size rate limit", async () => {
|
||||
try {
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
|
||||
@@ -306,3 +254,7 @@ describe(`#blockchain`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#control`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#getNetworkInfo`, () => {
|
||||
it("should get info on the full node", async () => {
|
||||
const result = await bchjs.Control.getNetworkInfo()
|
||||
@@ -22,3 +26,7 @@ describe(`#control`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#ElectrumX`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
@@ -244,4 +250,121 @@ describe(`#ElectrumX`, () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should GET block headers for given height and count`, async () => {
|
||||
const height = 42
|
||||
const count = 2
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height, count)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 2)
|
||||
})
|
||||
|
||||
it(`should GET block headers for given height with default count = 1`, async () => {
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "headers")
|
||||
assert.isArray(result.headers)
|
||||
assert.equal(result.headers.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid =
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
const txids = [
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee",
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++) {
|
||||
txids.push(
|
||||
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
|
||||
)
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ["error"])
|
||||
assert.include(err.error, "Array too large")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
const txHex =
|
||||
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
assert.property(err, "success")
|
||||
assert.equal(err.success, false)
|
||||
assert.include(
|
||||
err.error,
|
||||
"the transaction was rejected by network rules"
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,13 @@ const bchjs = new BCHJS({ ninsightURL: "https://trest.bitcoin.com/v2" })
|
||||
|
||||
describe(`#Ninsight`, () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
@@ -17,21 +23,21 @@ describe(`#Ninsight`, () => {
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "utxos")
|
||||
assert.property(result, "legacyAddress")
|
||||
assert.property(result, "cashAddress")
|
||||
assert.property(result, "slpAddress")
|
||||
assert.property(result, "scriptPubKey")
|
||||
assert.property(result, "asm")
|
||||
assert.property(result[0], "utxos")
|
||||
assert.property(result[0], "legacyAddress")
|
||||
assert.property(result[0], "cashAddress")
|
||||
assert.property(result[0], "slpAddress")
|
||||
assert.property(result[0], "scriptPubKey")
|
||||
assert.property(result[0], "asm")
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result.utxos[0], "txid")
|
||||
assert.property(result.utxos[0], "vout")
|
||||
assert.property(result.utxos[0], "amount")
|
||||
assert.property(result.utxos[0], "satoshis")
|
||||
assert.property(result.utxos[0], "height")
|
||||
assert.property(result.utxos[0], "confirmations")
|
||||
assert.property(result[0].utxos[0], "txid")
|
||||
assert.property(result[0].utxos[0], "vout")
|
||||
assert.property(result[0].utxos[0], "amount")
|
||||
assert.property(result[0].utxos[0], "satoshis")
|
||||
assert.property(result[0].utxos[0], "height")
|
||||
assert.property(result[0].utxos[0], "confirmations")
|
||||
})
|
||||
|
||||
it(`should POST utxo details for an array of addresses`, async () => {
|
||||
@@ -63,3 +69,7 @@ describe(`#Ninsight`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
tests for OpenBazaar library.
|
||||
*/
|
||||
|
||||
// Force testnet call to OB1 servers.
|
||||
process.env.NETWORK = "testnet"
|
||||
|
||||
const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: `https://tapi.fullstack.cash/v3/`
|
||||
|
||||
const BCHJS = require("../../../src/bch-js")
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe(`#OpenBazaar`, () => {
|
||||
describe(`#Balance`, () => {
|
||||
// it(`should GET balance for a single address`, async () => {
|
||||
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.balance(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAllKeys(result, [
|
||||
// "page",
|
||||
// "totalPages",
|
||||
// "itemsOnPage",
|
||||
// "addrStr",
|
||||
// "balance",
|
||||
// "totalReceived",
|
||||
// "totalSent",
|
||||
// "unconfirmedBalance",
|
||||
// "unconfirmedTxApperances",
|
||||
// "txApperances",
|
||||
// "transactions"
|
||||
// ])
|
||||
// assert.isArray(result.transactions)
|
||||
// })
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.balance(addr)
|
||||
// assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#utxo`, () => {
|
||||
// it(`should GET utxos for a single address`, async () => {
|
||||
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.utxo(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.isArray(result)
|
||||
// assert.hasAllKeys(result[0], [
|
||||
// "txid",
|
||||
// "vout",
|
||||
// "amount",
|
||||
// "height",
|
||||
// "confirmations",
|
||||
// "satoshis"
|
||||
// ])
|
||||
// })
|
||||
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.utxo(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input address must be a string`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#tx`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.OpenBazaar.tx(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txid must be a string`)
|
||||
}
|
||||
})
|
||||
|
||||
// it(`should GET transactions for a single address`, async () => {
|
||||
// const addr =
|
||||
// "ed4692f50a4553527dd26cd8674ca06a0ab2d366f3135ca3668310467ead3cbf"
|
||||
//
|
||||
// const result = await bchjs.OpenBazaar.tx(addr)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.hasAnyKeys(result, [
|
||||
// "txid",
|
||||
// "version",
|
||||
// "vin",
|
||||
// "vout",
|
||||
// "blockhash",
|
||||
// "blockheight",
|
||||
// "confirmations",
|
||||
// "blocktime",
|
||||
// "valueOut",
|
||||
// "valueIn",
|
||||
// "fees",
|
||||
// "hex"
|
||||
// ])
|
||||
// assert.isArray(result.vin)
|
||||
// assert.isArray(result.vout)
|
||||
// })
|
||||
})
|
||||
})
|
||||
@@ -26,6 +26,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
describe("#rawtransaction", () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#decodeRawTransaction", () => {
|
||||
it("should decode tx for a single hex", async () => {
|
||||
const hex =
|
||||
@@ -70,21 +74,6 @@ describe("#rawtransaction", () => {
|
||||
assert.isArray(result[0].vout)
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const data = []
|
||||
@@ -239,22 +228,6 @@ describe("#rawtransaction", () => {
|
||||
const result = await bchjs.RawTransactions.decodeScript(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
*/
|
||||
/*
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.decodeRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
@@ -278,7 +251,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,22 +268,7 @@ describe("#rawtransaction", () => {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ["error"])
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(addr)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input hex must be a string or array of strings`
|
||||
)
|
||||
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -332,3 +290,7 @@ describe("#rawtransaction", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
|
||||
describe(`#SLP`, () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe("#util", () => {
|
||||
@@ -73,23 +73,7 @@ describe(`#SLP`, () => {
|
||||
})
|
||||
|
||||
describe("#balancesForAddress", () => {
|
||||
it(`should throw an error if input is not a string or array of strings`, async () => {
|
||||
try {
|
||||
const address = 1234
|
||||
|
||||
await bchjs.SLP.Utils.balancesForAddress(address)
|
||||
|
||||
assert.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
//console.log(`Error: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input address must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
||||
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 +123,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
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
export RESTURL=https://free-test.fullstack.cash/v3/
|
||||
export NETWORK=testnet
|
||||
#export RESTURL=http://localhost:3000/v3/
|
||||
export IS_USING_FREE_TIER=true
|
||||
|
||||
cd test/integration/testnet/
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js openbazaar.js rawtransaction.js slp.js util.js
|
||||
mocha --timeout 30000 blockchain.js control.js electrumx.js rawtransaction.js slp.js util.js
|
||||
#mocha --timeout 30000 blockchain.js
|
||||
|
||||
@@ -24,6 +24,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
describe(`#util`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#validateAddress`, () => {
|
||||
it(`should return false for testnet addr on mainnet`, async () => {
|
||||
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
|
||||
@@ -82,21 +86,6 @@ describe(`#util`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const address = 15432
|
||||
|
||||
await bchjs.Util.validateAddress(address)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock = `bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh`
|
||||
@@ -114,3 +103,7 @@ describe(`#util`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ util.inspect.defaultOptions = {
|
||||
}
|
||||
|
||||
describe(`#util`, () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
describe(`#validateAddress`, () => {
|
||||
it(`should return false for testnet addr on mainnet`, async () => {
|
||||
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
|
||||
@@ -75,21 +79,6 @@ describe(`#util`, () => {
|
||||
])
|
||||
})
|
||||
|
||||
it(`should throw an error for improper single input`, async () => {
|
||||
try {
|
||||
const address = 15432
|
||||
|
||||
await bchjs.Util.validateAddress(address)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
//console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input must be a string or array of strings.`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
|
||||
@@ -107,3 +96,7 @@ describe(`#util`, () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
@@ -260,4 +260,162 @@ describe(`#ElectrumX`, () => {
|
||||
assert.property(result.utxos[0].utxos[0], "fee")
|
||||
})
|
||||
})
|
||||
|
||||
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.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
|
||||
|
||||
await bchjs.Electrumx.blockHeader(height, count)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, `count must be a positive number`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET block headers for a given height`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders })
|
||||
|
||||
const height = 42
|
||||
|
||||
const result = await bchjs.Electrumx.blockHeader(height)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 2)
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Electrumx.txData(txid)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
`Input txId must be a string or array of strings`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should GET details data for a single transaction`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.details })
|
||||
|
||||
const txid =
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "details")
|
||||
assert.isObject(result.details)
|
||||
|
||||
assert.property(result.details, "blockhash")
|
||||
assert.property(result.details, "hash")
|
||||
assert.property(result.details, "hex")
|
||||
assert.property(result.details, "vin")
|
||||
assert.property(result.details, "vout")
|
||||
assert.equal(result.details.hash, txid)
|
||||
})
|
||||
|
||||
it(`should POST details for an array of transactions`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray })
|
||||
|
||||
const txids = [
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
]
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "transactions")
|
||||
assert.isArray(result.transactions)
|
||||
|
||||
assert.property(result.transactions[0], "txid")
|
||||
assert.property(result.transactions[0], "details")
|
||||
|
||||
assert.property(result.transactions[0].details, "blockhash")
|
||||
assert.property(result.transactions[0].details, "hash")
|
||||
assert.property(result.transactions[0].details, "hex")
|
||||
assert.property(result.transactions[0].details, "vin")
|
||||
assert.property(result.transactions[0].details, "vout")
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -158,6 +158,103 @@ const unconfirmedArray = {
|
||||
]
|
||||
}
|
||||
|
||||
const blockHeaders = [
|
||||
'010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6',
|
||||
'01000000f528fac1bcb685d0cd6c792320af0300a5ce15d687c7149548904e31000000004e8985a786d864f21e9cbb7cbdf4bc9265fe681b7a0893ac55a8e919ce035c2f85de6849ffff001d385ccb7c'
|
||||
]
|
||||
|
||||
const txDetails = {
|
||||
blockhash: '0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc',
|
||||
blocktime: 1578327094,
|
||||
confirmations: 31861,
|
||||
hash: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
|
||||
hex: '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000',
|
||||
locktime: 0,
|
||||
size: 392,
|
||||
time: 1578327094,
|
||||
txid: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
|
||||
version: 2,
|
||||
vin: [
|
||||
{
|
||||
scriptSig: {
|
||||
asm: 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309',
|
||||
hex: '41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
|
||||
vout: 1
|
||||
},
|
||||
{
|
||||
scriptSig: {
|
||||
asm: '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954',
|
||||
hex: '41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
|
||||
vout: 0
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash: pqvfecpwxvj53ayqfwkxtjaxsgpvnklcyg8xewk9hl'],
|
||||
asm: 'OP_HASH160 189ce02e332548f4804bac65cba68202c9dbf822 OP_EQUAL',
|
||||
hex: 'a914189ce02e332548f4804bac65cba68202c9dbf82287',
|
||||
reqSigs: 1,
|
||||
type: 'scripthash'
|
||||
},
|
||||
value: 0.0562057
|
||||
},
|
||||
{
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash: qq59hv6s3qdjrtyfwfxxldkuj9xsjmx48vrz882knz'],
|
||||
asm: 'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914285bb350881b21ac89724c6fb6dc914d096cd53b88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash'
|
||||
},
|
||||
value: 0.00589197
|
||||
},
|
||||
{
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
addresses: ['bitcoincash: qpzlruwy4xu5rxjs3z37nsj29y7h59gwvsu4ddp0u4'],
|
||||
asm: 'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash'
|
||||
},
|
||||
value: 0.03272697
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const details = {
|
||||
success: true,
|
||||
details: txDetails
|
||||
}
|
||||
|
||||
const detailsArray = {
|
||||
success: true,
|
||||
transactions: [
|
||||
{
|
||||
txid: txDetails.txid,
|
||||
details: txDetails
|
||||
},
|
||||
{
|
||||
txid: txDetails.txid,
|
||||
details: txDetails
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const broadcast = {
|
||||
success: true,
|
||||
txid: txDetails.txid
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
utxo,
|
||||
utxos,
|
||||
@@ -166,5 +263,9 @@ module.exports = {
|
||||
transaction,
|
||||
transactions,
|
||||
unconfirmed,
|
||||
unconfirmedArray
|
||||
unconfirmedArray,
|
||||
blockHeaders,
|
||||
details,
|
||||
detailsArray,
|
||||
broadcast
|
||||
}
|
||||
|
||||
@@ -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
@@ -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")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1695,6 +1695,76 @@ describe("#SLP Utils", () => {
|
||||
|
||||
assert2.equal(data[2].isValid, false)
|
||||
})
|
||||
|
||||
it("should return null value when 429 recieved", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
height: 654522,
|
||||
tx_hash:
|
||||
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
||||
tx_pos: 0,
|
||||
value: 6999,
|
||||
satoshis: 6999,
|
||||
txid:
|
||||
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
|
||||
vout: 0
|
||||
},
|
||||
{
|
||||
height: 654522,
|
||||
tx_hash:
|
||||
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
||||
tx_pos: 1,
|
||||
value: 546,
|
||||
satoshis: 546,
|
||||
txid:
|
||||
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
|
||||
vout: 1
|
||||
}
|
||||
]
|
||||
|
||||
sandbox.stub(slp.Utils, "decodeOpReturn").rejects({
|
||||
error:
|
||||
"Too many requests. Your limits are currently 3 requests per minute. Increase rate limits at https://fullstack.cash"
|
||||
})
|
||||
|
||||
const data = await slp.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
// Values should be 'null' to signal that a determination could not be made
|
||||
// due to a throttling issue.
|
||||
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", () => {
|
||||
@@ -1760,4 +1830,118 @@ describe("#SLP Utils", () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hydrateUtxos", () => {
|
||||
it("should throw an error if input is not an array", async () => {
|
||||
try {
|
||||
const utxos = 1234
|
||||
|
||||
await slp.Utils.hydrateUtxos(utxos)
|
||||
|
||||
assert2.equal(true, false, "Uh oh. Code path should not end here.")
|
||||
} catch (err) {
|
||||
// console.log(`Error: `, err)
|
||||
assert2.include(err.message, `Input must be an array.`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user