tokenUtxoDetails: prototyped different validation methods

This commit is contained in:
Chris Troutner
2020-12-06 09:49:30 -08:00
parent a9c128f96b
commit 348a67a79b
4 changed files with 167 additions and 11 deletions
+2 -1
View File
@@ -16,10 +16,11 @@
"test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration",
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ bash test/integration/test-bchn-integration.sh",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet",
"test:integration:api": "RESTURL=https://api.fullstack.cash/v4/ mocha --timeout 30000 test/integration",
"test:temp": "RESTURL=https://bchn.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/",
"test:temp2": "RESTURL=https://abc.fullstack.cash/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/",
"test:temp3": "ISBCHN=true RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 -g '#validateTxid' test/integration/bchn/",
"test:integration:api": "RESTURL=https://api.fullstack.cash/v4/ mocha --timeout 30000 test/integration",
"test:temp4": "mocha -g '#SLP Utils' test/unit/",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs"
+68 -7
View File
@@ -14,6 +14,7 @@ class Utils {
this.apiToken = config.apiToken
this.slpParser = slpParser
this.authToken = config.authToken
this.axios = axios
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
@@ -32,6 +33,12 @@ class Utils {
}
_this = this
this.whitelist = []
if (process.env.TEST !== "unit") {
// Get the whitelist.
this.getWhitelist()
}
}
/**
@@ -565,14 +572,19 @@ class Utils {
* }
* ]
*/
async getWhitelist(txid) {
async getWhitelist() {
try {
const path = `${this.restURL}slp/whitelist`
const response = await axios.get(path, _this.axiosOptions)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
// Retrieve the whitelist from the REST API if we haven't gotten it yet.
if (this.whitelist.length === 0) {
const response = await axios.get(path, _this.axiosOptions)
console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data
this.whitelist = response.data
}
return this.whitelist
} catch (error) {
if (error.response && error.response.data) throw error.response.data
throw error
@@ -1074,6 +1086,7 @@ class Utils {
}
}
// Ensure the UTXO has a vout or tx_pos property.
if (!Number.isInteger(utxo.vout)) {
if (Number.isInteger(utxo.tx_pos)) {
utxo.vout = utxo.tx_pos
@@ -1262,16 +1275,64 @@ class Utils {
}
}
// Finally, validate the SLP txid with SLPDB.
// *After* the UTXO has been hydrated with SLP data,
// validate the TXID with SLPDB.
if (outAry[i].tokenType) {
var isValid = cachedTxValidation[utxo.txid]
// Only execute this block if the current UTXO has a 'tokenType'
// property. i.e. it has been hydrated with SLP information.
// If the value has been cached, use the cached version first.
let isValid = cachedTxValidation[utxo.txid]
// If not in the cache, try the general SLPDB.
if (isValid == null) {
isValid = await this.validateTxid(utxo.txid)
isValid = isValid[0].valid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
}
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid[0].valid
// If still null, check the whitelist SLPDB
if (isValid === null) {
console.log(`outAry[${i}]: ${JSON.stringify(outAry[i], null, 2)}`)
// Figure out if the token UTXO is in the whitelist.
let utxoInWhitelist = false
for (let j = 0; j < this.whitelist.length; j++) {
if (outAry[i].tokenId === this.whitelist[j].tokenId) {
utxoInWhitelist = true
break
}
}
// If the utxo.tokenId is in the whitelist, check the validity with
// the whitelist SLPDB. This should still be functioning properly
// if the general SLPDB is not.
if (utxoInWhitelist) {
isValid = await this.validateTxid3(utxo.txid)
isValid = isValid[0].valid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
}
}
// If still null, as a last resort, check it against slp-validate
if (isValid === null) {
isValid = await this.validateTxid2(utxo.txid)
console.log(
`slp-validate isValid: ${JSON.stringify(isValid, null, 2)}`
)
isValid = isValid.valid
// Save the result to the local cache.
cachedTxValidation[utxo.txid] = isValid
}
console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid
}
}
+92
View File
@@ -463,6 +463,98 @@ describe(`#SLP`, () => {
assert.equal(data[0].isValid, false)
})
it("should handle a range of UTXO types", async () => {
const utxos = [
// Malformed SLP tx
{
note: "Malformed SLP tx",
tx_hash:
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a",
tx_pos: 1,
value: 546
},
// Normal TX (non-SLP)
{
note: "Normal TX (non-SLP)",
tx_hash:
"01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0",
tx_pos: 0,
value: 400000
},
// Valid PSF SLP tx
{
note: "Valid PSF SLP tx",
tx_hash:
"daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd",
tx_pos: 1,
value: 546
},
// Valid SLP token not in whitelist
{
note: "Valid SLP token not in whitelist",
tx_hash:
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488",
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: "Token send on BCHN network",
tx_hash:
"402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019",
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: "Token send on ABC network",
tx_hash:
"336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d",
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: "Known invalid SLP token send of PSF tokens",
tx_hash:
"2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74",
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Malformed SLP tx
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, null)
// Normal TX (non-SLP)
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Valid PSF SLP tx
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
// Valid SLP token not in whitelist
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
})
})
describe("#balancesForAddress", () => {
+5 -3
View File
@@ -1737,7 +1737,7 @@ describe("#SLP Utils", () => {
})
// it("should handle a dust attack", async () => {
it("#dustattack", async () => {
it("should handle dust attack UTXOs", async () => {
// Mock external dependencies.
// Stub the calls to decodeOpReturn.
sandbox
@@ -1765,6 +1765,8 @@ describe("#SLP Utils", () => {
assert.equal(data[0].isValid, false)
})
//
})
describe("#txDetails", () => {
@@ -2005,9 +2007,9 @@ describe("#SLP Utils", () => {
await slp.Utils.getWhitelist()
assert2.equal(true, false, "Unexpected result")
assert2.fail("Unexpected result")
} catch (err) {
// console.log("err: ", err)
console.log("err: ", err)
assert2.include(err.error, "Network error")
}
})