Compare commits

..
6 Commits
6 changed files with 167 additions and 13 deletions
+1 -1
View File
@@ -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 '#blockHeader' test/unit/electrumx.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
View File
@@ -116,7 +116,7 @@ class HDNode {
/**
* @api SLP.HDNode.toSLPAddress() toSLPAddress()
* @apiName toSLPAddress
* @apiGroup SLP
* @apiGroup HDNode
* @apiDescription Get slp address of HDNode.
*
* @apiExample Example usage:
+53 -9
View File
@@ -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
@@ -499,7 +499,7 @@ class Utils {
typeof txid !== "string" ||
txid.length !== 64
)
throw new Error(`txid must be 64 character string.`)
throw new Error("txid must be 64 character string.")
const path = `${this.restURL}slp/validateTxid2/${txid}`
@@ -700,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}`
@@ -767,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
}
@@ -775,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`
@@ -897,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.
@@ -963,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)
@@ -1257,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`,
+27 -1
View File
@@ -6,7 +6,7 @@ const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
let bchjs
// Inspect utility used for debugging.
const util = require("util")
@@ -25,6 +25,8 @@ describe(`#SLP`, () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
await sleep(1000)
bchjs = new BCHJS()
})
describe("#util", () => {
@@ -437,6 +439,30 @@ describe(`#SLP`, () => {
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it("should handle a dust attack", async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#balancesForAddress", () => {
+55 -1
View File
@@ -89,7 +89,7 @@ describe(`#SLP`, () => {
}
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
it(`should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9`, async () => {
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
@@ -139,6 +139,60 @@ describe(`#SLP`, () => {
])
})
})
describe("#tokenUtxoDetails", () => {
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(
utxos.utxos[0].utxos
)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo[0].isValid, true)
})
})
describe("#hydrateUtxos", () => {
// This test will error out if the LOCAL_RESTURL settings is not set properly
// in bch-api.
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo.slpUtxos[0].utxos)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo.slpUtxos[0].utxos[0].isValid, true)
})
})
describe("#validateTxid2", () => {
it("should validate a token txid", async () => {
const txid =
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1"
const validated = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(validated)
assert.equal(validated[0].valid, true)
})
})
})
// Promise-based sleep function
+30
View File
@@ -1735,6 +1735,36 @@ describe("#SLP Utils", () => {
assert.equal(data[0].isValid, null)
assert.equal(data[1].isValid, null)
})
// it("should handle a dust attack", async () => {
it("#dustattack", async () => {
// Mock external dependencies.
// Stub the calls to decodeOpReturn.
sandbox
.stub(slp.Utils, "decodeOpReturn")
.rejects(new Error("lokad id wrong size"))
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await slp.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#txDetails", () => {