Skip to content

Commit 6860120

Browse files
committed
feat(Utxo.isValid()): can test if a UTXO has been spent
1 parent cfb7b9f commit 6860120

File tree

3 files changed

+145
-1
lines changed

3 files changed

+145
-1
lines changed

src/utxo.js

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const Electrumx = require('./electrumx')
1010
const Slp = require('./slp/slp')
1111
const PsfSlpIndexer = require('./psf-slp-indexer')
1212
const BigNumber = require('bignumber.js')
13+
const Blockchain = require('./blockchain')
1314

1415
class UTXO {
1516
constructor (config = {}) {
@@ -18,6 +19,7 @@ class UTXO {
1819
this.slp = new Slp(config)
1920
this.psfSlpIndexer = new PsfSlpIndexer(config)
2021
this.BigNumber = BigNumber
22+
this.blockchain = new Blockchain(config)
2123
}
2224

2325
/**
@@ -207,7 +209,7 @@ class UTXO {
207209

208210
return outObj
209211
} catch (err) {
210-
console.error('Error in bchjs.Utxo.get(): ', err)
212+
console.error('Error in bchjs.Utxo.get()')
211213

212214
if (err.error) throw new Error(err.error)
213215
throw err
@@ -336,6 +338,57 @@ class UTXO {
336338

337339
return utxos[largestIndex]
338340
}
341+
342+
/**
343+
* @api Utxo.isValid() isValid()
344+
* @apiName isValid
345+
* @apiGroup UTXO
346+
* @apiDescription Validate that UTXO exists and is still spendable.
347+
*
348+
* Given a UTXO, this method will return true if the UTXO is still in the
349+
* mempool and still valid for spending. It will return false if the UTXO
350+
* has been spent.
351+
*
352+
* @apiExample Example usage:
353+
* (async () => {
354+
* try {
355+
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
356+
* const isValid = bchjs.Utxo.isValid(utxos.bchUtxos[0])
357+
* console.log(isValid);
358+
* } catch(error) {
359+
* console.error(error)
360+
* }
361+
* })()
362+
*
363+
* // returns
364+
* true
365+
*/
366+
async isValid (utxo) {
367+
try {
368+
// console.log('utxo: ', utxo)
369+
370+
// Convert different properties from different indexers
371+
const txid = utxo.txid || utxo.tx_hash
372+
const vout = utxo.vout | utxo.tx_pos
373+
374+
// Query the full node
375+
const txOut = await this.blockchain.getTxOut(txid, vout, true)
376+
// console.log('txOut: ', txOut)
377+
378+
// Simplify results to either true or false.
379+
let isValid = null
380+
if (txOut === null) {
381+
isValid = false
382+
} else {
383+
isValid = true
384+
}
385+
386+
return isValid
387+
} catch (err) {
388+
console.log('Error in Utxo.isValid()')
389+
throw err
390+
}
391+
}
339392
}
340393

341394
module.exports = UTXO

test/integration/chains/bchn/utxo-integration.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,65 @@ describe('#UTXO', () => {
122122
assert.isAbove(result.slpUtxos.nft.tokens.length, 0)
123123
})
124124
})
125+
126+
describe('#isValid', () => {
127+
it('should return true for valid UTXO with fullnode properties', async () => {
128+
const utxo = {
129+
txid: 'b94e1ff82eb5781f98296f0af2488ff06202f12ee92b0175963b8dba688d1b40',
130+
vout: 0
131+
}
132+
133+
const result = await bchjs.Utxo.isValid(utxo)
134+
// console.log('result: ', result)
135+
136+
assert.equal(result, true)
137+
})
138+
139+
it('should return true for valid UTXO with fulcrum properties', async () => {
140+
const utxo = {
141+
tx_hash: 'b94e1ff82eb5781f98296f0af2488ff06202f12ee92b0175963b8dba688d1b40',
142+
tx_pos: 0
143+
}
144+
145+
const result = await bchjs.Utxo.isValid(utxo)
146+
// console.log('result: ', result)
147+
148+
assert.equal(result, true)
149+
})
150+
151+
it('should return true for valid UTXO with fullnode properties', async () => {
152+
const utxo = {
153+
txid: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
154+
vout: 0
155+
}
156+
157+
const result = await bchjs.Utxo.isValid(utxo)
158+
// console.log('result: ', result)
159+
160+
assert.equal(result, false)
161+
})
162+
163+
it('should return true for valid UTXO with fulcrum properties', async () => {
164+
const utxo = {
165+
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
166+
tx_pos: 0
167+
}
168+
169+
const result = await bchjs.Utxo.isValid(utxo)
170+
// console.log('result: ', result
171+
172+
assert.equal(result, false)
173+
})
174+
175+
it('should process output of Utxo.get()', async () => {
176+
const utxo = await bchjs.Utxo.get('bitcoincash:qr4yscpw9jgq8ltajfeknpj32kamkf9knujffcdhyq')
177+
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
178+
179+
const result = await bchjs.Utxo.isValid(utxo.bchUtxos[0])
180+
181+
assert.equal(result, true)
182+
})
183+
})
125184
})
126185

127186
function sleep (ms) {

test/unit/utxo-unit.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,36 @@ describe('#utxo', () => {
174174
assert.equal(result.nullUtxos.length, 0)
175175
})
176176
})
177+
178+
describe('#isValid', () => {
179+
it('should return false if getTxOut() returns null', async () => {
180+
// Mock dependencies
181+
sandbox.stub(bchjs.Utxo.blockchain, 'getTxOut').resolves(null)
182+
183+
const utxo = {
184+
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
185+
tx_pos: 0
186+
}
187+
188+
const result = await bchjs.Utxo.isValid(utxo)
189+
// console.log('result: ', result
190+
191+
assert.equal(result, false)
192+
})
193+
194+
it('should return true if getTxOut() returns non-null output', async () => {
195+
// Mock dependencies
196+
sandbox.stub(bchjs.Utxo.blockchain, 'getTxOut').resolves({ a: 'b' })
197+
198+
const utxo = {
199+
tx_hash: 'b94e1ff82eb5781f98296f0af2488ff06202f12ee92b0175963b8dba688d1b40',
200+
tx_pos: 0
201+
}
202+
203+
const result = await bchjs.Utxo.isValid(utxo)
204+
// console.log('result: ', result
205+
206+
assert.equal(result, true)
207+
})
208+
})
177209
})

0 commit comments

Comments
 (0)