mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 00:52:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40e1704572 | ||
|
|
9539119c57 | ||
|
|
b397381c7d | ||
|
|
6cdb9a0867 | ||
|
|
01f45d7103 | ||
|
|
ba38d3e107 | ||
|
|
cbb0d0a8ca | ||
|
|
7ec8442540 | ||
|
|
be3094fc77 | ||
|
|
d35f0e5473 | ||
|
|
79088fc68d | ||
|
|
fe84729737 | ||
|
|
af118f8a62 |
+1
-1
@@ -16,7 +16,7 @@
|
||||
"test:integration:free": "bash test/integration/test-free-tier.sh",
|
||||
"test:integration:free:bchn": "bash test/integration/test-free-tier-bchn.sh",
|
||||
"test:integration:free:testnet": "bash test/integration/testnet/test-free-tier.sh",
|
||||
"test:temp": "mocha --timeout 30000 -g '#hydrateUtxos' test/integration/slp.js",
|
||||
"test:temp": "mocha --timeout 30000 -g '#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/",
|
||||
|
||||
+49
-3
@@ -441,8 +441,14 @@ class ElectrumX {
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log("error: ", error)
|
||||
if (error.response && error.response.data) {
|
||||
if (error.response && error.response.data)
|
||||
throw new Error(error.response.data.error)
|
||||
else throw error.response.data
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,7 +521,7 @@ class ElectrumX {
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
*/
|
||||
async txData(txid) {
|
||||
try {
|
||||
// Handle single transaction.
|
||||
@@ -543,6 +549,46 @@ class ElectrumX {
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Electrumx.broadcast() broadcast()
|
||||
* @apiName ElectrumX Broadcast
|
||||
* @apiGroup ElectrumX
|
||||
* @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure.
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
* let result = await bchjs.Electrumx.broadcast(txHex)
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* result = {
|
||||
* "success": true,
|
||||
* "txid": "..."
|
||||
* }
|
||||
*/
|
||||
async broadcast(txHex) {
|
||||
try {
|
||||
if (typeof txHex === "string") {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/tx/broadcast`,
|
||||
{ txHex },
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error(`Input txHex must be a string.`)
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ElectrumX
|
||||
|
||||
+1
-1
@@ -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:
|
||||
|
||||
+112
-8
@@ -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}`
|
||||
@@ -707,7 +767,8 @@ class Utils {
|
||||
// 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`)
|
||||
throw new Error("decodeOpReturn cache parameter must be Object")
|
||||
|
||||
const cachedVal = cache[txid]
|
||||
if (cachedVal) return cachedVal
|
||||
}
|
||||
@@ -715,7 +776,7 @@ class Utils {
|
||||
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`
|
||||
@@ -837,7 +898,7 @@ class Utils {
|
||||
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.
|
||||
@@ -903,7 +964,8 @@ class Utils {
|
||||
// to display the unknown state.
|
||||
if (
|
||||
!err.message ||
|
||||
err.message.indexOf("scriptpubkey not op_return") === -1
|
||||
(err.message.indexOf("scriptpubkey not op_return") === -1 &&
|
||||
err.message.indexOf("lokad id") === -1)
|
||||
) {
|
||||
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
|
||||
|
||||
@@ -1197,13 +1259,55 @@ class Utils {
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* (async () => {
|
||||
* try {
|
||||
* const utxos = [
|
||||
* {
|
||||
* utxos: [
|
||||
* {
|
||||
* txid: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
* vout: 3,
|
||||
* value: "6816",
|
||||
* height: 606848,
|
||||
* confirmations: 13,
|
||||
* satoshis: 6816
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos)
|
||||
*
|
||||
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
|
||||
* } catch (error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* {
|
||||
* "slpUtxos": [
|
||||
* {
|
||||
* "utxos": [
|
||||
* {
|
||||
* "txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
|
||||
* "vout": 3,
|
||||
* "value": "6816",
|
||||
* "height": 606848,
|
||||
* "confirmations": 13,
|
||||
* "satoshis": 6816,
|
||||
* "isValid": false
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* ]
|
||||
*/
|
||||
// Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
|
||||
// do the heavy lifting.
|
||||
async hydrateUtxos(utxos) {
|
||||
try {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(utxos)) throw new Error(`Input must be an array.`)
|
||||
if (!Array.isArray(utxos)) throw new Error("Input must be an array.")
|
||||
|
||||
const response = await axios.post(
|
||||
`${this.restURL}slp/hydrateUtxos`,
|
||||
|
||||
@@ -271,7 +271,8 @@ describe(`#ElectrumX`, () => {
|
||||
|
||||
describe(`#txData`, () => {
|
||||
it(`should GET details for a single transaction`, async () => {
|
||||
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
const txid =
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
@@ -318,8 +319,11 @@ describe(`#ElectrumX`, () => {
|
||||
it(`should throw error on array size rate limit`, async () => {
|
||||
try {
|
||||
const txids = []
|
||||
for (let i = 0; i < 25; i++)
|
||||
txids.push("4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251")
|
||||
for (let i = 0; i < 25; i++) {
|
||||
txids.push(
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
)
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.txData(txids)
|
||||
|
||||
@@ -331,4 +335,19 @@ describe(`#ElectrumX`, () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
const txHex =
|
||||
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
assert.property(err, "success")
|
||||
assert.equal(err.success, false)
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+57
-1
@@ -6,7 +6,7 @@ const chai = require("chai")
|
||||
const assert = chai.assert
|
||||
|
||||
const BCHJS = require("../../src/bch-js")
|
||||
const bchjs = new BCHJS()
|
||||
let bchjs
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require("util")
|
||||
@@ -25,6 +25,8 @@ describe(`#SLP`, () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
await sleep(1000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe("#util", () => {
|
||||
@@ -437,6 +439,30 @@ describe(`#SLP`, () => {
|
||||
assert.equal(data[0].isValid, false)
|
||||
assert.equal(data[1].isValid, false)
|
||||
})
|
||||
|
||||
it("should handle a dust attack", async () => {
|
||||
// it("#dustattack", async () => {
|
||||
const utxos = [
|
||||
{
|
||||
height: 655965,
|
||||
tx_hash:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
tx_pos: 151,
|
||||
value: 547,
|
||||
satoshis: 547,
|
||||
txid:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
vout: 151,
|
||||
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
|
||||
hdIndex: 11
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.equal(data[0].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#balancesForAddress", () => {
|
||||
@@ -607,6 +633,36 @@ describe(`#SLP`, () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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", () => {
|
||||
|
||||
@@ -335,4 +335,18 @@ describe(`#ElectrumX`, () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
|
||||
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
assert.property(err, "success")
|
||||
assert.equal(err.success, false)
|
||||
assert.include(err.error, "Missing inputs")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ describe(`#SLP`, () => {
|
||||
}
|
||||
})
|
||||
|
||||
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
|
||||
it(`should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9`, async () => {
|
||||
// Mock the call to rest.bitcoin.com
|
||||
if (process.env.TEST === "unit") {
|
||||
sandbox
|
||||
@@ -139,6 +139,60 @@ describe(`#SLP`, () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("#tokenUtxoDetails", () => {
|
||||
it("should hydrate UTXOs", async () => {
|
||||
const bchAddr = bchjs.SLP.Address.toCashAddress(
|
||||
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
|
||||
)
|
||||
|
||||
const utxos = await bchjs.Electrumx.utxo([bchAddr])
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(
|
||||
utxos.utxos[0].utxos
|
||||
)
|
||||
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
|
||||
|
||||
assert.isArray(utxoInfo)
|
||||
|
||||
// first UTXO should be a PSF test token.
|
||||
assert.equal(utxoInfo[0].isValid, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#hydrateUtxos", () => {
|
||||
// This test will error out if the LOCAL_RESTURL settings is not set properly
|
||||
// in bch-api.
|
||||
it("should hydrate UTXOs", async () => {
|
||||
const bchAddr = bchjs.SLP.Address.toCashAddress(
|
||||
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
|
||||
)
|
||||
|
||||
const utxos = await bchjs.Electrumx.utxo([bchAddr])
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
|
||||
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
|
||||
|
||||
assert.isArray(utxoInfo.slpUtxos[0].utxos)
|
||||
|
||||
// first UTXO should be a PSF test token.
|
||||
assert.equal(utxoInfo.slpUtxos[0].utxos[0].isValid, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#validateTxid2", () => {
|
||||
it("should validate a token txid", async () => {
|
||||
const txid =
|
||||
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1"
|
||||
|
||||
const validated = await bchjs.SLP.Utils.validateTxid(txid)
|
||||
// console.log(validated)
|
||||
|
||||
assert.equal(validated[0].valid, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
|
||||
+53
-3
@@ -264,17 +264,38 @@ describe(`#ElectrumX`, () => {
|
||||
describe(`#blockHeader`, () => {
|
||||
it(`should throw an error for improper height input`, async () => {
|
||||
try {
|
||||
// Mock network calls.
|
||||
sandbox.stub(axios, "get").rejects({
|
||||
response: {
|
||||
data: {
|
||||
success: false,
|
||||
error: "height must be a positive number"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const height = -10
|
||||
await bchjs.Electrumx.blockHeader(height)
|
||||
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, `height must be a positive number`)
|
||||
assert.include(err.message, `height must be a positive number`)
|
||||
}
|
||||
})
|
||||
|
||||
it(`should throw an error for improper count input`, async () => {
|
||||
try {
|
||||
// Mock network calls.
|
||||
sandbox.stub(axios, "get").rejects({
|
||||
response: {
|
||||
data: {
|
||||
success: false,
|
||||
error: "count must be a positive number"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const height = 42
|
||||
const count = -10
|
||||
|
||||
@@ -282,7 +303,7 @@ describe(`#ElectrumX`, () => {
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.error, `count must be a positive number`)
|
||||
assert.include(err.message, `count must be a positive number`)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -319,7 +340,8 @@ describe(`#ElectrumX`, () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "get").resolves({ data: mockData.details })
|
||||
|
||||
const txid = "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
const txid =
|
||||
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
|
||||
|
||||
const result = await bchjs.Electrumx.txData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
@@ -368,4 +390,32 @@ describe(`#ElectrumX`, () => {
|
||||
assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs")
|
||||
})
|
||||
})
|
||||
describe(`#broadcast`, () => {
|
||||
it(`should throw an error for improper input`, async () => {
|
||||
try {
|
||||
const txHex = 12345
|
||||
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
assert.equal(true, false, "Unexpected result!")
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, `Input txHex must be a string.`)
|
||||
}
|
||||
})
|
||||
it(`should broadcast a single transaction`, async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, "post").resolves({ data: mockData.broadcast })
|
||||
|
||||
const tx = mockData.details
|
||||
const txid = tx.details.txid
|
||||
const result = await bchjs.Electrumx.broadcast(tx.details.hex)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, "success")
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, "txid")
|
||||
assert.equal(result.txid, txid)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -250,6 +250,11 @@ const detailsArray = {
|
||||
]
|
||||
}
|
||||
|
||||
const broadcast = {
|
||||
success: true,
|
||||
txid: txDetails.txid
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
utxo,
|
||||
utxos,
|
||||
@@ -261,5 +266,6 @@ module.exports = {
|
||||
unconfirmedArray,
|
||||
blockHeaders,
|
||||
details,
|
||||
detailsArray
|
||||
detailsArray,
|
||||
broadcast
|
||||
}
|
||||
|
||||
@@ -1735,6 +1735,36 @@ describe("#SLP Utils", () => {
|
||||
assert.equal(data[0].isValid, null)
|
||||
assert.equal(data[1].isValid, null)
|
||||
})
|
||||
|
||||
// it("should handle a dust attack", async () => {
|
||||
it("#dustattack", async () => {
|
||||
// Mock external dependencies.
|
||||
// Stub the calls to decodeOpReturn.
|
||||
sandbox
|
||||
.stub(slp.Utils, "decodeOpReturn")
|
||||
.rejects(new Error("lokad id wrong size"))
|
||||
|
||||
const utxos = [
|
||||
{
|
||||
height: 655965,
|
||||
tx_hash:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
tx_pos: 151,
|
||||
value: 547,
|
||||
satoshis: 547,
|
||||
txid:
|
||||
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
|
||||
vout: 151,
|
||||
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
|
||||
hdIndex: 11
|
||||
}
|
||||
]
|
||||
|
||||
const data = await slp.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
assert.equal(data[0].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("#txDetails", () => {
|
||||
@@ -1815,4 +1845,103 @@ describe("#SLP Utils", () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("#validateTxid2", () => {
|
||||
it("should throw an error if the input is an array", async () => {
|
||||
try {
|
||||
const txid = [
|
||||
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
||||
]
|
||||
|
||||
await slp.Utils.validateTxid2(txid)
|
||||
|
||||
assert2.equal(true, false, "Unexpected result")
|
||||
} catch (err) {
|
||||
// console.log("err: ", err)
|
||||
assert2.include(err.message, "txid must be 64 character string")
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw an error for a malformed txid", async () => {
|
||||
try {
|
||||
const txid =
|
||||
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783"
|
||||
|
||||
await slp.Utils.validateTxid2(txid)
|
||||
|
||||
assert2.equal(true, false, "Unexpected result")
|
||||
} catch (err) {
|
||||
// console.log("err: ", err)
|
||||
assert2.include(err.message, "txid must be 64 character string")
|
||||
}
|
||||
})
|
||||
|
||||
it("should invalidate a known invalid TXID", async () => {
|
||||
const txid =
|
||||
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
|
||||
|
||||
// Mock live network calls.
|
||||
sandbox.stub(axios, "get").resolves({
|
||||
data: {
|
||||
txid: txid,
|
||||
isValid: false,
|
||||
msg: ""
|
||||
}
|
||||
})
|
||||
|
||||
const result = await slp.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert2.property(result, "txid")
|
||||
assert2.equal(result.txid, txid)
|
||||
|
||||
assert2.property(result, "isValid")
|
||||
assert2.equal(result.isValid, false)
|
||||
})
|
||||
|
||||
it("should validate a known valid TXID", async () => {
|
||||
const txid =
|
||||
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
|
||||
|
||||
// Mock live network calls.
|
||||
sandbox.stub(axios, "get").resolves({
|
||||
data: {
|
||||
txid: txid,
|
||||
isValid: true,
|
||||
msg: ""
|
||||
}
|
||||
})
|
||||
|
||||
const result = await slp.Utils.validateTxid2(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert2.property(result, "txid")
|
||||
assert2.equal(result.txid, txid)
|
||||
|
||||
assert2.property(result, "isValid")
|
||||
assert2.equal(result.isValid, true)
|
||||
})
|
||||
|
||||
// slp-validate can take a long time. bch-api cuts it off if it fails to
|
||||
// return is less than 10 seconds. This test case handle this situation.
|
||||
it("should handle timeout errors", async () => {
|
||||
try {
|
||||
const txid =
|
||||
"eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579"
|
||||
|
||||
// Mock live network calls.
|
||||
sandbox.stub(axios, "get").rejects({
|
||||
error:
|
||||
"Network error: Could not communicate with full node or other external service."
|
||||
})
|
||||
|
||||
await slp.Utils.validateTxid2(txid)
|
||||
|
||||
assert2.equal(true, false, "Unexpected result")
|
||||
} catch (err) {
|
||||
// console.log("err: ", err)
|
||||
assert2.include(err.message, "slp-validate timed out")
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user