@psf/bch-js 6.1.0 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/slp/utils.js +0 -521
- package/src/transaction.js +9 -467
- package/src/utxo.js +3 -0
- package/test/unit/slp-utils.js +0 -1381
- package/test/unit/transaction-unit.js +4 -688
package/package.json
CHANGED
package/src/slp/utils.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// Public npm libraries
|
|
4
4
|
const axios = require('axios')
|
|
5
5
|
const slpParser = require('slp-parser')
|
|
6
|
-
const BigNumber = require('bignumber.js')
|
|
7
6
|
|
|
8
7
|
// Local libraries
|
|
9
8
|
const Util = require('../util')
|
|
@@ -183,526 +182,6 @@ class Utils {
|
|
|
183
182
|
throw error
|
|
184
183
|
}
|
|
185
184
|
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* @api SLP.Utils.tokenUtxoDetails() tokenUtxoDetails()
|
|
189
|
-
* @apiName tokenUtxoDetails
|
|
190
|
-
* @apiGroup SLP Utils
|
|
191
|
-
* @apiDescription Hydrate a UTXO with SLP token metadata.
|
|
192
|
-
*
|
|
193
|
-
* Expects an array of UTXO objects as input. Returns an array of equal size.
|
|
194
|
-
* Returns UTXO data hydrated with token information.
|
|
195
|
-
*
|
|
196
|
-
* - If the UTXO does not belong to a SLP transaction, it will return an
|
|
197
|
-
* `isValid` property set to `false`.
|
|
198
|
-
*
|
|
199
|
-
* - If the UTXO is part of an SLP transaction, it will return the UTXO object
|
|
200
|
-
* with additional SLP information attached. An `isValid` property will be
|
|
201
|
-
* included.
|
|
202
|
-
* - If the `isValid` property is `true`, the UTXO is a valid SLP UTXO.
|
|
203
|
-
* - If the `isValid` property is `null`, then SLPDB has not yet processed
|
|
204
|
-
* that txid and validity has not been confirmed, or a 429 rate-limit error
|
|
205
|
-
* was enountered during the processing of the request.
|
|
206
|
-
*
|
|
207
|
-
* An optional second input object, `usrObj`, allows the user to inject an
|
|
208
|
-
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
|
|
209
|
-
* a number, the call will delay by that number of milliseconds between
|
|
210
|
-
* processing UTXOs.
|
|
211
|
-
*
|
|
212
|
-
* This is an API-heavy call. If you get a lot of `null` values, then slow down
|
|
213
|
-
* the calls by using the usrObj.utxoDelay property, or request info on fewer
|
|
214
|
-
* UTXOs at a
|
|
215
|
-
* time. `null` indicates that the UTXO can *not* be safely spent, because
|
|
216
|
-
* a judgement as to weather it is a token UTXO has not been made. Spending it
|
|
217
|
-
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* @apiExample Example usage:
|
|
221
|
-
*
|
|
222
|
-
* (async () => {
|
|
223
|
-
* try {
|
|
224
|
-
* const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
|
|
225
|
-
*
|
|
226
|
-
* // Delay 100mS between processing UTXOs, to prevent rate-limit errors.
|
|
227
|
-
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos, { utxoDelay: 100 })
|
|
228
|
-
*
|
|
229
|
-
* console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
|
|
230
|
-
* } catch (error) {
|
|
231
|
-
* console.error(error)
|
|
232
|
-
* }
|
|
233
|
-
* })()
|
|
234
|
-
*
|
|
235
|
-
* // returns
|
|
236
|
-
* {
|
|
237
|
-
* "txid": "fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
|
|
238
|
-
* "vout": 1,
|
|
239
|
-
* "amount": 0.00000546,
|
|
240
|
-
* "satoshis": 546,
|
|
241
|
-
* "height": 596089,
|
|
242
|
-
* "confirmations": 748,
|
|
243
|
-
* "utxoType": "token",
|
|
244
|
-
* "tokenId": "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7",
|
|
245
|
-
* "tokenTicker": "TOK-CH",
|
|
246
|
-
* "tokenName": "TokyoCash",
|
|
247
|
-
* "tokenDocumentUrl": "",
|
|
248
|
-
* "tokenDocumentHash": "",
|
|
249
|
-
* "decimals": 8,
|
|
250
|
-
* "tokenQty": 2,
|
|
251
|
-
* "isValid": true,
|
|
252
|
-
* "tokenType": 1
|
|
253
|
-
* }
|
|
254
|
-
*/
|
|
255
|
-
async tokenUtxoDetails (utxos, usrObj = null) {
|
|
256
|
-
try {
|
|
257
|
-
// Throw error if input is not an array.
|
|
258
|
-
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
|
|
259
|
-
|
|
260
|
-
// console.log(`tokenUtxoDetails usrObj: ${JSON.stringify(usrObj, null, 2)}`)
|
|
261
|
-
|
|
262
|
-
// Loop through each element in the array and validate the input before
|
|
263
|
-
// further processing.
|
|
264
|
-
for (let i = 0; i < utxos.length; i++) {
|
|
265
|
-
const utxo = utxos[i]
|
|
266
|
-
|
|
267
|
-
// Ensure the UTXO has a txid or tx_hash property.
|
|
268
|
-
if (!utxo.txid) {
|
|
269
|
-
// If Electrumx, convert the tx_hash property to txid.
|
|
270
|
-
if (utxo.tx_hash) {
|
|
271
|
-
utxo.txid = utxo.tx_hash
|
|
272
|
-
} else {
|
|
273
|
-
// If there is neither a txid or tx_hash property, throw an error.
|
|
274
|
-
throw new Error(
|
|
275
|
-
`utxo ${i} does not have a txid or tx_hash property.`
|
|
276
|
-
)
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Ensure the UTXO has a vout or tx_pos property.
|
|
281
|
-
if (!Number.isInteger(utxo.vout)) {
|
|
282
|
-
if (Number.isInteger(utxo.tx_pos)) {
|
|
283
|
-
utxo.vout = utxo.tx_pos
|
|
284
|
-
} else {
|
|
285
|
-
throw new Error(
|
|
286
|
-
`utxo ${i} does not have a vout or tx_pos property.`
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Hydrate each UTXO with data from SLP OP_REUTRNs.
|
|
293
|
-
const outAry = await this._hydrateUtxo(utxos, usrObj)
|
|
294
|
-
// console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`)
|
|
295
|
-
|
|
296
|
-
// *After* each UTXO has been hydrated with SLP data,
|
|
297
|
-
// validate the TXID with SLPDB.
|
|
298
|
-
for (let i = 0; i < outAry.length; i++) {
|
|
299
|
-
const utxo = outAry[i]
|
|
300
|
-
|
|
301
|
-
// *After* the UTXO has been hydrated with SLP data,
|
|
302
|
-
// validate the TXID with SLPDB.
|
|
303
|
-
if (utxo.tokenType) {
|
|
304
|
-
// Only execute this code-path if the current UTXO has a 'tokenType'
|
|
305
|
-
// property. i.e. it has been successfully hydrated with SLP
|
|
306
|
-
// information.
|
|
307
|
-
|
|
308
|
-
// Validate using a 'waterfall' of validators.
|
|
309
|
-
utxo.isValid = await this.waterfallValidateTxid(utxo.txid, usrObj)
|
|
310
|
-
// console.log(`isValid: ${JSON.stringify(utxo.isValid, null, 2)}`)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
return outAry
|
|
315
|
-
} catch (error) {
|
|
316
|
-
// console.log('Error in tokenUtxoDetails()')
|
|
317
|
-
if (error.response && error.response.data) throw error.response.data
|
|
318
|
-
throw error
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// This is a private function that is called by tokenUtxoDetails().
|
|
323
|
-
// It loops through an array of UTXOs and tries to hydrate them with SLP
|
|
324
|
-
// token information from the OP_RETURN data.
|
|
325
|
-
//
|
|
326
|
-
// This function makes several calls to decodeOpReturn() to retrieve SLP
|
|
327
|
-
// token data. If that call throws an error due to hitting rate limits, this
|
|
328
|
-
// function will not throw an error. Instead, it will mark the `isValid`
|
|
329
|
-
// property as `null`
|
|
330
|
-
//
|
|
331
|
-
// Exception to the above: It *will* throw an error if decodeOpReturn() throws
|
|
332
|
-
// an error while trying to get the Genesis transaction for a Send or Mint
|
|
333
|
-
// transaction. However, that is a rare occurence since the cache of
|
|
334
|
-
// decodeOpReturn() will minimize API calls for this case. This behavior
|
|
335
|
-
// could be changed, but right now it's a corner case of a corner case.
|
|
336
|
-
//
|
|
337
|
-
// If the usrObj has a utxoDelay property, then it will delay the loop for
|
|
338
|
-
// each UTXO by that many milliseconds.
|
|
339
|
-
async _hydrateUtxo (utxos, usrObj = null) {
|
|
340
|
-
try {
|
|
341
|
-
const decodeOpReturnCache = {}
|
|
342
|
-
|
|
343
|
-
// console.log(`_hydrateUtxo usrObj: ${JSON.stringify(usrObj, null, 2)}`)
|
|
344
|
-
|
|
345
|
-
// Output Array
|
|
346
|
-
const outAry = []
|
|
347
|
-
|
|
348
|
-
// Loop through each utxo
|
|
349
|
-
for (let i = 0; i < utxos.length; i++) {
|
|
350
|
-
const utxo = utxos[i]
|
|
351
|
-
|
|
352
|
-
// If the user passes in a delay, then wait.
|
|
353
|
-
if (usrObj && usrObj.utxoDelay && !isNaN(Number(usrObj.utxoDelay))) {
|
|
354
|
-
const delayMs = Number(usrObj.utxoDelay)
|
|
355
|
-
await this.util.sleep(delayMs)
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Get raw transaction data from the full node and attempt to decode
|
|
359
|
-
// the OP_RETURN data.
|
|
360
|
-
// If there is no OP_RETURN, mark the UTXO as false.
|
|
361
|
-
let slpData = false
|
|
362
|
-
try {
|
|
363
|
-
slpData = await this.decodeOpReturn(
|
|
364
|
-
utxo.txid,
|
|
365
|
-
decodeOpReturnCache,
|
|
366
|
-
usrObj // pass user data when making an internal call.
|
|
367
|
-
)
|
|
368
|
-
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
|
|
369
|
-
} catch (err) {
|
|
370
|
-
// console.log(
|
|
371
|
-
// `error in _hydrateUtxo() from decodeOpReturn(${utxo.txid}): `,
|
|
372
|
-
// err
|
|
373
|
-
// )
|
|
374
|
-
|
|
375
|
-
// An error will be thrown if the txid is not SLP.
|
|
376
|
-
// If error is for some other reason, like a 429 error, mark utxo as 'null'
|
|
377
|
-
// to display the unknown state.
|
|
378
|
-
if (
|
|
379
|
-
!err.message ||
|
|
380
|
-
(err.message.indexOf('scriptpubkey not op_return') === -1 &&
|
|
381
|
-
err.message.indexOf('lokad id') === -1 &&
|
|
382
|
-
err.message.indexOf('trailing data') === -1)
|
|
383
|
-
) {
|
|
384
|
-
// console.log(
|
|
385
|
-
// "unknown error from decodeOpReturn(). Marking as 'null'",
|
|
386
|
-
// err
|
|
387
|
-
// )
|
|
388
|
-
|
|
389
|
-
utxo.isValid = null
|
|
390
|
-
outAry.push(utxo)
|
|
391
|
-
|
|
392
|
-
// If error is thrown because there is no OP_RETURN, then it's not
|
|
393
|
-
// an SLP UTXO.
|
|
394
|
-
// Mark as false and continue the loop.
|
|
395
|
-
} else {
|
|
396
|
-
// console.log('marking as invalid')
|
|
397
|
-
utxo.isValid = false
|
|
398
|
-
outAry.push(utxo)
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Halt the execution of the loop and increase to the next index.
|
|
402
|
-
continue
|
|
403
|
-
}
|
|
404
|
-
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
|
|
405
|
-
|
|
406
|
-
const txType = slpData.txType.toLowerCase()
|
|
407
|
-
|
|
408
|
-
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
|
409
|
-
|
|
410
|
-
// If there is an OP_RETURN, attempt to decode it.
|
|
411
|
-
// Handle Genesis SLP transactions.
|
|
412
|
-
if (txType === 'genesis') {
|
|
413
|
-
if (
|
|
414
|
-
utxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
|
|
415
|
-
utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
|
|
416
|
-
) {
|
|
417
|
-
// Can safely be marked as false.
|
|
418
|
-
utxo.isValid = false
|
|
419
|
-
outAry[i] = utxo
|
|
420
|
-
} else {
|
|
421
|
-
// If this is a valid SLP UTXO, then return the decoded OP_RETURN data.
|
|
422
|
-
// Minting Baton
|
|
423
|
-
if (utxo.vout === slpData.mintBatonVout) {
|
|
424
|
-
utxo.utxoType = 'minting-baton'
|
|
425
|
-
} else {
|
|
426
|
-
// Tokens
|
|
427
|
-
|
|
428
|
-
utxo.utxoType = 'token'
|
|
429
|
-
utxo.tokenQty = new BigNumber(slpData.qty)
|
|
430
|
-
.div(Math.pow(10, slpData.decimals))
|
|
431
|
-
.toString()
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
utxo.tokenId = utxo.txid
|
|
435
|
-
utxo.tokenTicker = slpData.ticker
|
|
436
|
-
utxo.tokenName = slpData.name
|
|
437
|
-
utxo.tokenDocumentUrl = slpData.documentUri
|
|
438
|
-
utxo.tokenDocumentHash = slpData.documentHash
|
|
439
|
-
utxo.decimals = slpData.decimals
|
|
440
|
-
utxo.tokenType = slpData.tokenType
|
|
441
|
-
|
|
442
|
-
// Initial value is null until UTXO can be validated and confirmed
|
|
443
|
-
// to be valid (true) or not (false).
|
|
444
|
-
utxo.isValid = null
|
|
445
|
-
|
|
446
|
-
outAry[i] = utxo
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// Handle Mint SLP transactions.
|
|
451
|
-
if (txType === 'mint') {
|
|
452
|
-
if (
|
|
453
|
-
utxo.vout !== slpData.mintBatonVout && // UTXO is not a mint baton output.
|
|
454
|
-
utxo.vout !== 1 // UTXO is not the reciever of the genesis or mint tokens.
|
|
455
|
-
) {
|
|
456
|
-
// Can safely be marked as false.
|
|
457
|
-
utxo.isValid = false
|
|
458
|
-
|
|
459
|
-
outAry[i] = utxo
|
|
460
|
-
} else {
|
|
461
|
-
// If UTXO passes validation, then return formatted token data.
|
|
462
|
-
|
|
463
|
-
const genesisData = await this.decodeOpReturn(
|
|
464
|
-
slpData.tokenId,
|
|
465
|
-
decodeOpReturnCache,
|
|
466
|
-
usrObj // pass user data when making an internal call.
|
|
467
|
-
)
|
|
468
|
-
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
|
|
469
|
-
|
|
470
|
-
// Minting Baton
|
|
471
|
-
if (utxo.vout === slpData.mintBatonVout) {
|
|
472
|
-
utxo.utxoType = 'minting-baton'
|
|
473
|
-
} else {
|
|
474
|
-
// Tokens
|
|
475
|
-
|
|
476
|
-
utxo.utxoType = 'token'
|
|
477
|
-
utxo.tokenQty = new BigNumber(slpData.qty)
|
|
478
|
-
.div(Math.pow(10, genesisData.decimals))
|
|
479
|
-
.toString()
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// Hydrate the UTXO object with information about the SLP token.
|
|
483
|
-
utxo.transactionType = 'mint'
|
|
484
|
-
utxo.tokenId = slpData.tokenId
|
|
485
|
-
utxo.tokenType = slpData.tokenType
|
|
486
|
-
|
|
487
|
-
utxo.tokenTicker = genesisData.ticker
|
|
488
|
-
utxo.tokenName = genesisData.name
|
|
489
|
-
utxo.tokenDocumentUrl = genesisData.documentUri
|
|
490
|
-
utxo.tokenDocumentHash = genesisData.documentHash
|
|
491
|
-
utxo.decimals = genesisData.decimals
|
|
492
|
-
|
|
493
|
-
utxo.mintBatonVout = slpData.mintBatonVout
|
|
494
|
-
|
|
495
|
-
// Initial value is null until UTXO can be validated and confirmed
|
|
496
|
-
// to be valid (true) or not (false).
|
|
497
|
-
utxo.isValid = null
|
|
498
|
-
|
|
499
|
-
outAry[i] = utxo
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Handle Send SLP transactions.
|
|
504
|
-
if (txType === 'send') {
|
|
505
|
-
// Filter out any vouts that match.
|
|
506
|
-
// const voutMatch = slpData.spendData.filter(x => utxo.vout === x.vout)
|
|
507
|
-
// console.log(`voutMatch: ${JSON.stringify(voutMatch, null, 2)}`)
|
|
508
|
-
|
|
509
|
-
// Figure out what token quantity is represented by this utxo.
|
|
510
|
-
const tokenQty = slpData.amounts[utxo.vout - 1]
|
|
511
|
-
// console.log('tokenQty: ', tokenQty)
|
|
512
|
-
|
|
513
|
-
if (!tokenQty) {
|
|
514
|
-
utxo.isValid = false
|
|
515
|
-
|
|
516
|
-
outAry[i] = utxo
|
|
517
|
-
} else {
|
|
518
|
-
// If UTXO passes validation, then return formatted token data.
|
|
519
|
-
|
|
520
|
-
const genesisData = await this.decodeOpReturn(
|
|
521
|
-
slpData.tokenId,
|
|
522
|
-
decodeOpReturnCache,
|
|
523
|
-
usrObj // pass user data when making an internal call.
|
|
524
|
-
)
|
|
525
|
-
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
|
|
526
|
-
|
|
527
|
-
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
|
528
|
-
|
|
529
|
-
// Hydrate the UTXO object with information about the SLP token.
|
|
530
|
-
utxo.utxoType = 'token'
|
|
531
|
-
utxo.transactionType = 'send'
|
|
532
|
-
utxo.tokenId = slpData.tokenId
|
|
533
|
-
utxo.tokenTicker = genesisData.ticker
|
|
534
|
-
utxo.tokenName = genesisData.name
|
|
535
|
-
utxo.tokenDocumentUrl = genesisData.documentUri
|
|
536
|
-
utxo.tokenDocumentHash = genesisData.documentHash
|
|
537
|
-
utxo.decimals = genesisData.decimals
|
|
538
|
-
utxo.tokenType = slpData.tokenType
|
|
539
|
-
|
|
540
|
-
// Initial value is null until UTXO can be validated and confirmed
|
|
541
|
-
// to be valid (true) or not (false).
|
|
542
|
-
utxo.isValid = null
|
|
543
|
-
|
|
544
|
-
// Calculate the real token quantity.
|
|
545
|
-
|
|
546
|
-
const tokenQtyBig = new BigNumber(tokenQty).div(
|
|
547
|
-
Math.pow(10, genesisData.decimals)
|
|
548
|
-
)
|
|
549
|
-
// console.log(`tokenQtyBig`, tokenQtyBig.toString())
|
|
550
|
-
utxo.tokenQty = tokenQtyBig.toString()
|
|
551
|
-
|
|
552
|
-
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
|
|
553
|
-
|
|
554
|
-
outAry[i] = utxo
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
return outAry
|
|
560
|
-
} catch (error) {
|
|
561
|
-
// console.log('_hydrateUtxo error: ', error)
|
|
562
|
-
throw error
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* @api SLP.Utils.waterfallValidateTxid() waterfallValidateTxid()
|
|
568
|
-
* @apiName waterfallValidateTxid
|
|
569
|
-
* @apiGroup SLP Utils
|
|
570
|
-
* @apiDescription Use multiple validators to validate an SLP TXID.
|
|
571
|
-
*
|
|
572
|
-
* This function aggregates all the available SLP token validation sources.
|
|
573
|
-
* It starts with the fastest, most-efficient source first, and continues
|
|
574
|
-
* to other validation sources until the txid is validated (true or false).
|
|
575
|
-
* If the txid goes through all sources and can't be validated, it will
|
|
576
|
-
* return null.
|
|
577
|
-
*
|
|
578
|
-
* Validation sources from most efficient to least efficient:
|
|
579
|
-
* - SLPDB with whitelist filter
|
|
580
|
-
* - SLPDB general purpose
|
|
581
|
-
* - slp-api
|
|
582
|
-
*
|
|
583
|
-
* Currently only supports a single txid at a time.
|
|
584
|
-
*
|
|
585
|
-
* @apiExample Example usage:
|
|
586
|
-
*
|
|
587
|
-
* // validate single SLP txid
|
|
588
|
-
* (async () => {
|
|
589
|
-
* try {
|
|
590
|
-
* let validated = await bchjs.SLP.Utils.waterfallValidateTxid(
|
|
591
|
-
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
|
|
592
|
-
* );
|
|
593
|
-
* console.log(validated);
|
|
594
|
-
* } catch (error) {
|
|
595
|
-
* console.error(error);
|
|
596
|
-
* }
|
|
597
|
-
* })();
|
|
598
|
-
*
|
|
599
|
-
* // returns
|
|
600
|
-
* true
|
|
601
|
-
*/
|
|
602
|
-
async waterfallValidateTxid (txid, usrObj = null) {
|
|
603
|
-
try {
|
|
604
|
-
// console.log('txid: ', txid)
|
|
605
|
-
|
|
606
|
-
const cachedTxValidation = {}
|
|
607
|
-
|
|
608
|
-
// If the value has been cached, use the cached version first.
|
|
609
|
-
let isValid = cachedTxValidation[txid]
|
|
610
|
-
if (!isValid && isValid !== false) {
|
|
611
|
-
isValid = null
|
|
612
|
-
} else {
|
|
613
|
-
return isValid
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// There are two possible responses from SLPDB. If SLPDB is functioning
|
|
617
|
-
// correctly, then validateTxid() will return this:
|
|
618
|
-
// isValid: [
|
|
619
|
-
// {
|
|
620
|
-
// "txid": "ff0c0354f8d3ddb34fa36f73494eb58ea24f8b8da6904aa8ed43b7a74886c583",
|
|
621
|
-
// "valid": true
|
|
622
|
-
// }
|
|
623
|
-
// ]
|
|
624
|
-
//
|
|
625
|
-
// If SLPDB has fallen behind real-time processing, it will return this:
|
|
626
|
-
// isValid: [
|
|
627
|
-
// null
|
|
628
|
-
// ]
|
|
629
|
-
//
|
|
630
|
-
// Note: validateTxid3() has the same output as validateTxid().
|
|
631
|
-
// validateTxid2() uses slp-validate, which has a different output format.
|
|
632
|
-
|
|
633
|
-
// Validate against the whitelist SLPDB first.
|
|
634
|
-
const whitelistResult = await this.validateTxid3(txid, usrObj)
|
|
635
|
-
// console.log(
|
|
636
|
-
// `whitelist-SLPDB for ${txid}: ${JSON.stringify(
|
|
637
|
-
// whitelistResult,
|
|
638
|
-
// null,
|
|
639
|
-
// 2
|
|
640
|
-
// )}`
|
|
641
|
-
// )
|
|
642
|
-
|
|
643
|
-
// Safely retrieve the returned value.
|
|
644
|
-
if (whitelistResult[0] !== null) isValid = whitelistResult[0].valid
|
|
645
|
-
|
|
646
|
-
// Exit if isValid is not null.
|
|
647
|
-
if (isValid !== null) {
|
|
648
|
-
// Save to the cache.
|
|
649
|
-
cachedTxValidation[txid] = isValid
|
|
650
|
-
|
|
651
|
-
return isValid
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// Try the general SLPDB, if the whitelist returned null.
|
|
655
|
-
const generalResult = await this.validateTxid(txid, usrObj)
|
|
656
|
-
// console.log(
|
|
657
|
-
// `validateTxid() isValid: ${JSON.stringify(generalResult, null, 2)}`
|
|
658
|
-
// )
|
|
659
|
-
|
|
660
|
-
// Safely retrieve the returned value.
|
|
661
|
-
if (generalResult[0] !== null) isValid = generalResult[0].valid
|
|
662
|
-
|
|
663
|
-
// Exit if isValid is not null.
|
|
664
|
-
if (isValid !== null) {
|
|
665
|
-
// Save to the cache.
|
|
666
|
-
cachedTxValidation[txid] = isValid
|
|
667
|
-
|
|
668
|
-
return isValid
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// If still null, as a last resort, check it against slp-validate
|
|
672
|
-
let slpValidateResult = null
|
|
673
|
-
try {
|
|
674
|
-
slpValidateResult = await this.validateTxid2(txid)
|
|
675
|
-
} catch (err) {
|
|
676
|
-
/* exit quietly */
|
|
677
|
-
}
|
|
678
|
-
// console.log(
|
|
679
|
-
// `slpValidateResult: ${JSON.stringify(slpValidateResult, null, 2)}`
|
|
680
|
-
// )
|
|
681
|
-
|
|
682
|
-
// Exit if isValid is not null.
|
|
683
|
-
if (slpValidateResult !== null) {
|
|
684
|
-
isValid = slpValidateResult.isValid
|
|
685
|
-
|
|
686
|
-
// Save to the cache.
|
|
687
|
-
cachedTxValidation[txid] = isValid
|
|
688
|
-
|
|
689
|
-
return isValid
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
// If isValid is still null, return that value, signaling that the txid
|
|
693
|
-
// could not be validated.
|
|
694
|
-
return isValid
|
|
695
|
-
} catch (error) {
|
|
696
|
-
// This case handles rate limit errors.
|
|
697
|
-
if (error.response && error.response.data && error.response.data.error) {
|
|
698
|
-
throw new Error(error.response.data.error)
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// console.log('Error in waterfallValidateTxid()')
|
|
702
|
-
if (error.response && error.response.data) throw error.response.data
|
|
703
|
-
throw error
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
185
|
}
|
|
707
186
|
|
|
708
187
|
module.exports = Utils
|