mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-21 16:51:59 -07:00
fix(PsfSlpIndexer.tx()): Added detection of blacklisted SLP Tokens
This commit is contained in:
+44
-7
@@ -11,11 +11,12 @@ const axios = require('axios')
|
||||
|
||||
// Local libraries
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
|
||||
// let _this
|
||||
|
||||
class PsfSlpIndexer {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
@@ -38,6 +39,7 @@ class PsfSlpIndexer {
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
// _this = this
|
||||
}
|
||||
@@ -308,16 +310,18 @@ class PsfSlpIndexer {
|
||||
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
|
||||
// )
|
||||
|
||||
// Check if this txid belongs to a blacklisted token.
|
||||
const isInBlacklist = await this.checkBlacklist(txid)
|
||||
|
||||
// Get the TX Details from the full node.
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
// TODO: Run the TX through decodeOpReturn() to see if it has a tokenID
|
||||
// that is in the blacklist. If it is, then set isValidSlp = null to
|
||||
// signal that it's state can not be determined.
|
||||
|
||||
// Assumption: transaction is not a valid SLP UTXO.
|
||||
txDetails.isValidSlp = false
|
||||
if (isInBlacklist) {
|
||||
txDetails.isValidSlp = null
|
||||
} else {
|
||||
txDetails.isValidSlp = false
|
||||
}
|
||||
|
||||
const outObj = {
|
||||
txData: txDetails
|
||||
@@ -327,6 +331,39 @@ class PsfSlpIndexer {
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the txid has an OP_RETURN containing a tokenID that is in the
|
||||
// blacklist. In that case, the isValidSlp property should be marked as
|
||||
// null, and not false.
|
||||
async checkBlacklist (txid) {
|
||||
try {
|
||||
// TODO: Add endpoint to psf-slp-indexer to retrieve current blacklist.
|
||||
// This should be done once at startup, and not each time this function
|
||||
// is called.
|
||||
const blacklist = [
|
||||
'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9'
|
||||
]
|
||||
|
||||
const outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log('outTokenData: ', outTokenData)
|
||||
|
||||
// Loop through each token in the blacklist.
|
||||
for (let i = 0; i < blacklist.length; i++) {
|
||||
// If a match is found, return true.
|
||||
if (outTokenData.tokenId === blacklist[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// By default, return false.
|
||||
return false
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
// Exit quietly.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PsfSlpIndexer
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ const Util = require('../util')
|
||||
let _this
|
||||
|
||||
class Utils {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.slpParser = slpParser
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('#psf-slp-indexer', () => {
|
||||
describe('#tx', () => {
|
||||
it('should get hydrated tx data for an SLP transaction', async () => {
|
||||
const txid =
|
||||
'83361c34cac2ea7f9ca287fca57a96cc0763719f0cdf4850f9696c1e68eb635c'
|
||||
'947ccb2a0d62ca287bc4b0993874ab0f9f6afd454193e631e2bf84dca66731fc'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
@@ -67,7 +67,7 @@ describe('#psf-slp-indexer', () => {
|
||||
assert.equal(result.txData.isValidSlp, true)
|
||||
})
|
||||
|
||||
it('should handle non-SLP transaction', async () => {
|
||||
it('should mark non-SLP transaction as false', async () => {
|
||||
const txid =
|
||||
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('#psf-slp-indexer', () => {
|
||||
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
|
||||
// the wallet will burn them. There is a TODO in the code. This test will
|
||||
// need to be changed when it is done.
|
||||
it('should handle a blacklisted token', async () => {
|
||||
it('should mark blacklisted token as null', async () => {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
@@ -94,7 +94,23 @@ describe('#psf-slp-indexer', () => {
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
|
||||
it('should throw error for non-existent txid', async () => {
|
||||
try {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85e'
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert.include(err.message, 'No such mempool or blockchain transaction')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,7 +21,8 @@ const tokenStats = {
|
||||
totalMinted: '1',
|
||||
txs: [
|
||||
{
|
||||
txid: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
txid:
|
||||
'13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
height: 722420,
|
||||
type: 'GENESIS',
|
||||
qty: '1'
|
||||
@@ -39,11 +40,14 @@ const txData = {
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
txid:
|
||||
'8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
asm:
|
||||
'304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
hex:
|
||||
'47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
@@ -58,16 +62,20 @@ const txData = {
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
hex:
|
||||
'6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
type: 'nulldata'
|
||||
},
|
||||
tokenQtyStr: '0',
|
||||
tokenQty: 0
|
||||
}
|
||||
],
|
||||
hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
hex:
|
||||
'0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
blockhash:
|
||||
'0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
confirmations: 97398,
|
||||
time: 1581773131,
|
||||
blocktime: 1581773131,
|
||||
@@ -88,27 +96,32 @@ const balance = {
|
||||
balance: {
|
||||
utxos: [
|
||||
{
|
||||
txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '1800',
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
}
|
||||
],
|
||||
txs: [
|
||||
{
|
||||
txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
txid:
|
||||
'078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
height: 717796
|
||||
},
|
||||
{
|
||||
txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
height: 717832
|
||||
}
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
qty: '1800'
|
||||
}
|
||||
]
|
||||
@@ -122,9 +135,19 @@ const status = {
|
||||
chainBlockHeight: 722679
|
||||
}
|
||||
}
|
||||
|
||||
const tokenData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: 'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9',
|
||||
mintBatonVout: 2,
|
||||
qty: {}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tokenStats,
|
||||
txData,
|
||||
balance,
|
||||
status
|
||||
status,
|
||||
tokenData01
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.isArray(result.balance.txs)
|
||||
assert.isArray(result.balance.balances)
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
@@ -82,6 +83,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.include(err.message, 'Input address must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
@@ -96,6 +98,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
@@ -139,6 +142,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.property(result.tokenData, 'totalMinted')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const tokenId = 12345
|
||||
@@ -150,6 +154,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.include(err.message, 'Input tokenId must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
@@ -164,6 +169,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
@@ -215,6 +221,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.property(result.txData, 'tokenDocHash')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
@@ -226,6 +233,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
assert.include(err.message, 'Input txid must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
@@ -249,6 +257,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(false)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
@@ -259,6 +268,72 @@ describe('#PsfSlpIndexer', () => {
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should return isValidSlp=null for blacklisted token', async () => {
|
||||
// Stub the call to the SLP indexer.
|
||||
const testErr = {
|
||||
response: { data: { error: 'Key not found in database' } }
|
||||
}
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(true)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#checkBlacklist', () => {
|
||||
it('should return true if txid contains token in blacklist', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false if there is an error', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return false if there is no tokenId match', async () => {
|
||||
// Mock dependencies
|
||||
mockData.tokenData01.tokenId = 'abc123'
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user