diff --git a/src/utxo.js b/src/utxo.js index 0a0ec59..9845f05 100644 --- a/src/utxo.js +++ b/src/utxo.js @@ -109,6 +109,11 @@ class UTXO { } } + // Get the sync status of the SLP indexer + const syncStatus = await this.psfSlpIndexer.status() + const slpIndexerHeight = syncStatus.status.syncedBlockHeight + const chainBlockHeight = syncStatus.status.chainBlockHeight + // Loop through the Fulcrum UTXOs. for (let i = 0; i < utxos.length; i++) { const thisUtxo = utxos[i] @@ -152,6 +157,15 @@ class UTXO { thisUtxo.isSlp = false } // console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`) + + // If the SLP indexer more than 1 block behind the full node, then + // move any BCH UTXOs of 600 sats or less into the null array. This + // protects token UTXOs from being burned accidentally. + if (slpIndexerHeight < chainBlockHeight - 1) { + if (thisUtxo.value < 601) { + thisUtxo.isSlp = null + } + } } } diff --git a/test/e2e/utxo/unsynced-indexer.js b/test/e2e/utxo/unsynced-indexer.js new file mode 100644 index 0000000..bf3128f --- /dev/null +++ b/test/e2e/utxo/unsynced-indexer.js @@ -0,0 +1,27 @@ +/* + This test is used to interrogate the behavior of bch-js when a psf-slp-indexer + panicks and starts indexing from SLP genesis. In this corner-case, bch-js + should detect the indexer is out of sync, and protect any token UTXOs by + moving any UTXO under 600 sats into the null array. + + TO RUN THIS TEST: + - Reset a local instance of psf-slp-indexer to sync from genesis. + - Start a local copy of bch-api +*/ + +const BCHJS = require('../../../src/bch-js.js') +const bchjs = new BCHJS({ + restURL: 'http://localhost:3000/v5/' +}) + +async function startTest () { + try { + const addr = 'bitcoincash:qzkvas58zag9tjry693mflkjze2m20ps7vx7uw7z9d' + + const result = await bchjs.Utxo.get(addr) + console.log(`result: ${JSON.stringify(result, null, 2)}`) + } catch (err) { + console.error('Error in startTest(): ', err) + } +} +startTest() diff --git a/test/unit/utxo-unit.js b/test/unit/utxo-unit.js index aab01ef..7490f6c 100644 --- a/test/unit/utxo-unit.js +++ b/test/unit/utxo-unit.js @@ -121,6 +121,8 @@ describe('#utxo', () => { sandbox .stub(bchjs.Utxo.psfSlpIndexer, 'tx') .resolves({ txData: { isValidSlp: false } }) + sandbox.stub(bchjs.Utxo.psfSlpIndexer, 'status') + .resolves({ status: { syncedBlockHeight: 600000, chainBlockHeight: 600000 } }) // Mock function to return the same input. Good enough for this test. sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x) @@ -154,6 +156,8 @@ describe('#utxo', () => { sandbox .stub(bchjs.Utxo.psfSlpIndexer, 'tx') .resolves({ txData: { isValidSlp: false } }) + sandbox.stub(bchjs.Utxo.psfSlpIndexer, 'status') + .resolves({ status: { syncedBlockHeight: 600000, chainBlockHeight: 600000 } }) // Force psf-slp-indexer to return no UTXOs sandbox