@bsv/wallet-toolbox 1.6.15 → 1.6.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bsv/wallet-toolbox",
3
- "version": "1.6.15",
3
+ "version": "1.6.17",
4
4
  "description": "BRC100 conforming wallet, wallet storage and wallet signer components",
5
5
  "main": "./out/src/index.js",
6
6
  "types": "./out/src/index.d.ts",
@@ -54,7 +54,7 @@ export class WalletError extends Error implements WalletErrorObject {
54
54
  *
55
55
  */
56
56
  static fromUnknown(err: unknown): WalletError {
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ if (err instanceof WalletError) return err
58
58
  let name = 'WERR_UNKNOWN'
59
59
  let message = ''
60
60
  let stack: string | undefined
@@ -14,6 +14,8 @@ import { HeightRange, HeightRanges } from './util/HeightRange'
14
14
  import { SingleWriterMultiReaderLock } from './util/SingleWriterMultiReaderLock'
15
15
  import { ChaintracksFsApi } from './Api/ChaintracksFsApi'
16
16
  import { randomBytesBase64, wait } from '../../../utility/utilityHelpers'
17
+ import { WalletError } from '../../../sdk/WalletError'
18
+ import { CWIStyleWalletManager } from '../../../CWIStyleWalletManager'
17
19
 
18
20
  export class Chaintracks implements ChaintracksManagementApi {
19
21
  static createOptions(chain: Chain): ChaintracksOptions {
@@ -47,6 +49,7 @@ export class Chaintracks implements ChaintracksManagementApi {
47
49
  private addLiveRecursionLimit = 11
48
50
 
49
51
  private available = false
52
+ private startupError: WalletError | null = null
50
53
 
51
54
  private subscriberCallbacksEnabled = false
52
55
  private stopMainThread = true
@@ -169,9 +172,11 @@ export class Chaintracks implements ChaintracksManagementApi {
169
172
  this.promises.push(this.mainThreadShiftLiveHeaders())
170
173
 
171
174
  // Wait for the main thread to finish initial sync.
172
- while (!this.available) {
175
+ while (!this.available && !this.startupError) {
173
176
  await wait(100)
174
177
  }
178
+
179
+ if (this.startupError) throw this.startupError
175
180
  })
176
181
  }
177
182
 
@@ -324,6 +329,7 @@ export class Chaintracks implements ChaintracksManagementApi {
324
329
 
325
330
  let done = false
326
331
  for (; !done; ) {
332
+ let bulkSyncError: WalletError | undefined
327
333
  for (const bulk of this.bulkIngestors) {
328
334
  try {
329
335
  const r = await bulk.synchronize(presentHeight, before, newLiveHeaders)
@@ -340,23 +346,30 @@ export class Chaintracks implements ChaintracksManagementApi {
340
346
  done = true
341
347
  break
342
348
  }
343
- } catch (uerr: unknown) {
344
- console.error(uerr)
349
+ } catch (eu: unknown) {
350
+ const e = (bulkSyncError = WalletError.fromUnknown(eu))
351
+ this.log(`bulk sync error: ${e.message}`)
345
352
  }
346
353
  }
354
+ if (!bulkDone && !this.available && bulkSyncError) {
355
+ this.startupError = bulkSyncError
356
+ break
357
+ }
347
358
  if (bulkDone) break
348
359
  }
349
360
 
350
- this.liveHeaders.unshift(...newLiveHeaders)
361
+ if (!this.startupError) {
362
+ this.liveHeaders.unshift(...newLiveHeaders)
351
363
 
352
- added = after.bulk.above(initialRanges.bulk)
364
+ added = after.bulk.above(initialRanges.bulk)
353
365
 
354
- this.log(`syncBulkStorage done
366
+ this.log(`syncBulkStorage done
355
367
  Before sync: bulk ${initialRanges.bulk}, live ${initialRanges.live}
356
368
  After sync: bulk ${after.bulk}, live ${after.live}
357
369
  ${added.length} headers added to bulk storage
358
370
  ${this.liveHeaders.length} headers forwarded to live header storage
359
371
  `)
372
+ }
360
373
  }
361
374
 
362
375
  private async getMissingBlockHeader(hash: string): Promise<BlockHeader | undefined> {
@@ -432,147 +445,159 @@ export class Chaintracks implements ChaintracksManagementApi {
432
445
  const syncCheckRepeatMsecs = 30 * 60 * 1000 // 30 minutes
433
446
 
434
447
  while (!this.stopMainThread) {
435
- // Review the need for bulk sync...
436
- const now = Date.now()
437
- lastSyncCheck = now
448
+ try {
449
+ // Review the need for bulk sync...
450
+ const now = Date.now()
451
+ lastSyncCheck = now
438
452
 
439
- const presentHeight = await this.getPresentHeight()
440
- const before = await this.storage.getAvailableHeightRanges()
453
+ const presentHeight = await this.getPresentHeight()
454
+ const before = await this.storage.getAvailableHeightRanges()
441
455
 
442
- // Skip bulk sync if within less than half the recursion limit of present height
443
- let skipBulkSync = !before.live.isEmpty && before.live.maxHeight >= presentHeight - this.addLiveRecursionLimit / 2
456
+ // Skip bulk sync if within less than half the recursion limit of present height
457
+ let skipBulkSync =
458
+ !before.live.isEmpty && before.live.maxHeight >= presentHeight - this.addLiveRecursionLimit / 2
444
459
 
445
- if (skipBulkSync && now - lastSyncCheck > cdnSyncRepeatMsecs) {
446
- // If we haven't re-synced in a long time, do it just to check for a CDN update.
447
- skipBulkSync = false
448
- }
460
+ if (skipBulkSync && now - lastSyncCheck > cdnSyncRepeatMsecs) {
461
+ // If we haven't re-synced in a long time, do it just to check for a CDN update.
462
+ skipBulkSync = false
463
+ }
449
464
 
450
- this.log(`Chaintracks Update Services: Bulk Header Sync Review
465
+ this.log(`Chaintracks Update Services: Bulk Header Sync Review
451
466
  presentHeight=${presentHeight} addLiveRecursionLimit=${this.addLiveRecursionLimit}
452
467
  Before synchronize: bulk ${before.bulk}, live ${before.live}
453
468
  ${skipBulkSync ? 'Skipping' : 'Starting'} syncBulkStorage.
454
469
  `)
455
470
 
456
- if (!skipBulkSync) {
457
- // Bring bulk storage up-to-date and (re-)initialize liveHeaders
458
- lastBulkSync = now
459
- if (this.available)
460
- // Once available, initial write lock is released, take a new one to update bulk storage.
461
- await this.syncBulkStorage(presentHeight, before)
462
- else
463
- // While still not available, the makeAvailable write lock is held.
464
- await this.syncBulkStorageNoLock(presentHeight, before)
465
- }
471
+ if (!skipBulkSync) {
472
+ // Bring bulk storage up-to-date and (re-)initialize liveHeaders
473
+ lastBulkSync = now
474
+ if (this.available)
475
+ // Once available, initial write lock is released, take a new one to update bulk storage.
476
+ await this.syncBulkStorage(presentHeight, before)
477
+ else
478
+ // While still not available, the makeAvailable write lock is held.
479
+ await this.syncBulkStorageNoLock(presentHeight, before)
480
+ if (this.startupError) throw this.startupError
481
+ }
466
482
 
467
- let count = 0
468
- let liveHeaderDupes = 0
469
- let needSyncCheck = false
470
-
471
- for (; !needSyncCheck && !this.stopMainThread; ) {
472
- let header = this.liveHeaders.shift()
473
- if (header) {
474
- // Process a "live" block header...
475
- let recursions = this.addLiveRecursionLimit
476
- for (; !needSyncCheck && !this.stopMainThread; ) {
477
- //console.log(`Processing liveHeader: height: ${header.height} hash: ${header.hash} ${new Date().toISOString()}`)
478
- const ihr = await this.addLiveHeader(header)
479
- if (this.invalidInsertHeaderResult(ihr)) {
480
- this.log(`Ignoring liveHeader ${header.height} ${header.hash} due to invalid insert result.`)
481
- needSyncCheck = true
482
- } else if (ihr.noPrev) {
483
- // Previous header is unknown, request it by hash from the network and try adding it first...
484
- if (recursions-- <= 0) {
485
- // Ignore this header...
486
- this.log(
487
- `Ignoring liveHeader ${header.height} ${header.hash} addLiveRecursionLimit=${this.addLiveRecursionLimit} exceeded.`
488
- )
483
+ let count = 0
484
+ let liveHeaderDupes = 0
485
+ let needSyncCheck = false
486
+
487
+ for (; !needSyncCheck && !this.stopMainThread; ) {
488
+ let header = this.liveHeaders.shift()
489
+ if (header) {
490
+ // Process a "live" block header...
491
+ let recursions = this.addLiveRecursionLimit
492
+ for (; !needSyncCheck && !this.stopMainThread; ) {
493
+ //console.log(`Processing liveHeader: height: ${header.height} hash: ${header.hash} ${new Date().toISOString()}`)
494
+ const ihr = await this.addLiveHeader(header)
495
+ if (this.invalidInsertHeaderResult(ihr)) {
496
+ this.log(`Ignoring liveHeader ${header.height} ${header.hash} due to invalid insert result.`)
489
497
  needSyncCheck = true
490
- } else {
491
- const hash = header.previousHash
492
- const prevHeader = await this.getMissingBlockHeader(hash)
493
- if (!prevHeader) {
498
+ } else if (ihr.noPrev) {
499
+ // Previous header is unknown, request it by hash from the network and try adding it first...
500
+ if (recursions-- <= 0) {
501
+ // Ignore this header...
494
502
  this.log(
495
- `Ignoring liveHeader ${header.height} ${header.hash} failed to find previous header by hash ${asString(hash)}`
503
+ `Ignoring liveHeader ${header.height} ${header.hash} addLiveRecursionLimit=${this.addLiveRecursionLimit} exceeded.`
496
504
  )
497
505
  needSyncCheck = true
498
506
  } else {
499
- // Switch to trying to add prevHeader, unshifting current header to try it again after prevHeader exists.
500
- this.liveHeaders.unshift(header)
501
- header = prevHeader
507
+ const hash = header.previousHash
508
+ const prevHeader = await this.getMissingBlockHeader(hash)
509
+ if (!prevHeader) {
510
+ this.log(
511
+ `Ignoring liveHeader ${header.height} ${header.hash} failed to find previous header by hash ${asString(hash)}`
512
+ )
513
+ needSyncCheck = true
514
+ } else {
515
+ // Switch to trying to add prevHeader, unshifting current header to try it again after prevHeader exists.
516
+ this.liveHeaders.unshift(header)
517
+ header = prevHeader
518
+ }
502
519
  }
503
- }
504
- } else {
505
- if (this.subscriberCallbacksEnabled)
506
- this.log(
507
- `addLiveHeader ${header.height}${ihr.added ? ' added' : ''}${ihr.dupe ? ' dupe' : ''}${ihr.isActiveTip ? ' isActiveTip' : ''}${ihr.reorgDepth ? ' reorg depth ' + ihr.reorgDepth : ''}${ihr.noPrev ? ' noPrev' : ''}${ihr.noActiveAncestor || ihr.noTip || ihr.badPrev ? ' error' : ''}`
508
- )
509
- if (ihr.dupe) {
510
- liveHeaderDupes++
511
- }
512
- // Header wasn't invalid and previous header is known. If it was successfully added, count it as a win.
513
- if (ihr.added) {
514
- count++
515
- }
516
- break
517
- }
518
- }
519
- } else {
520
- // There are no liveHeaders currently to process, check the out-of-band baseHeaders channel (`addHeader` method called by a client).
521
- const bheader = this.baseHeaders.shift()
522
- if (bheader) {
523
- const prev = await this.storage.findLiveHeaderForBlockHash(bheader.previousHash)
524
- if (!prev) {
525
- // Ignoring attempt to add a baseHeader with unknown previous hash, no attempt made to find previous header(s).
526
- this.log(`Ignoring header with unknown previousHash ${bheader.previousHash} in live storage.`)
527
- // Does not trigger a re-sync.
528
- } else {
529
- const header: BlockHeader = {
530
- ...bheader,
531
- height: prev.height + 1,
532
- hash: blockHash(bheader)
533
- }
534
- const ihr = await this.addLiveHeader(header)
535
- if (this.invalidInsertHeaderResult(ihr)) {
536
- this.log(`Ignoring invalid baseHeader ${header.height} ${header.hash}.`)
537
520
  } else {
538
521
  if (this.subscriberCallbacksEnabled)
539
522
  this.log(
540
- `addBaseHeader ${header.height}${ihr.added ? ' added' : ''}${ihr.dupe ? ' dupe' : ''}${ihr.isActiveTip ? ' isActiveTip' : ''}${ihr.reorgDepth ? ' reorg depth ' + ihr.reorgDepth : ''}${ihr.noPrev ? ' noPrev' : ''}${ihr.noActiveAncestor || ihr.noTip || ihr.badPrev ? ' error' : ''}`
523
+ `addLiveHeader ${header.height}${ihr.added ? ' added' : ''}${ihr.dupe ? ' dupe' : ''}${ihr.isActiveTip ? ' isActiveTip' : ''}${ihr.reorgDepth ? ' reorg depth ' + ihr.reorgDepth : ''}${ihr.noPrev ? ' noPrev' : ''}${ihr.noActiveAncestor || ihr.noTip || ihr.badPrev ? ' error' : ''}`
541
524
  )
542
- // baseHeader was successfully added.
525
+ if (ihr.dupe) {
526
+ liveHeaderDupes++
527
+ }
528
+ // Header wasn't invalid and previous header is known. If it was successfully added, count it as a win.
543
529
  if (ihr.added) {
544
530
  count++
545
531
  }
532
+ break
546
533
  }
547
534
  }
548
535
  } else {
549
- // There are no liveHeaders and no baseHeaders to add,
550
- if (count > 0) {
551
- if (liveHeaderDupes > 0) {
552
- this.log(`${liveHeaderDupes} duplicate headers ignored.`)
553
- liveHeaderDupes = 0
536
+ // There are no liveHeaders currently to process, check the out-of-band baseHeaders channel (`addHeader` method called by a client).
537
+ const bheader = this.baseHeaders.shift()
538
+ if (bheader) {
539
+ const prev = await this.storage.findLiveHeaderForBlockHash(bheader.previousHash)
540
+ if (!prev) {
541
+ // Ignoring attempt to add a baseHeader with unknown previous hash, no attempt made to find previous header(s).
542
+ this.log(`Ignoring header with unknown previousHash ${bheader.previousHash} in live storage.`)
543
+ // Does not trigger a re-sync.
544
+ } else {
545
+ const header: BlockHeader = {
546
+ ...bheader,
547
+ height: prev.height + 1,
548
+ hash: blockHash(bheader)
549
+ }
550
+ const ihr = await this.addLiveHeader(header)
551
+ if (this.invalidInsertHeaderResult(ihr)) {
552
+ this.log(`Ignoring invalid baseHeader ${header.height} ${header.hash}.`)
553
+ } else {
554
+ if (this.subscriberCallbacksEnabled)
555
+ this.log(
556
+ `addBaseHeader ${header.height}${ihr.added ? ' added' : ''}${ihr.dupe ? ' dupe' : ''}${ihr.isActiveTip ? ' isActiveTip' : ''}${ihr.reorgDepth ? ' reorg depth ' + ihr.reorgDepth : ''}${ihr.noPrev ? ' noPrev' : ''}${ihr.noActiveAncestor || ihr.noTip || ihr.badPrev ? ' error' : ''}`
557
+ )
558
+ // baseHeader was successfully added.
559
+ if (ihr.added) {
560
+ count++
561
+ }
562
+ }
554
563
  }
555
- const updated = await this.storage.getAvailableHeightRanges()
556
- this.log(`After adding ${count} live headers
564
+ } else {
565
+ // There are no liveHeaders and no baseHeaders to add,
566
+ if (count > 0) {
567
+ if (liveHeaderDupes > 0) {
568
+ this.log(`${liveHeaderDupes} duplicate headers ignored.`)
569
+ liveHeaderDupes = 0
570
+ }
571
+ const updated = await this.storage.getAvailableHeightRanges()
572
+ this.log(`After adding ${count} live headers
557
573
  After live: bulk ${updated.bulk}, live ${updated.live}
558
574
  `)
559
- count = 0
560
- }
561
- if (!this.subscriberCallbacksEnabled) {
562
- const live = await this.storage.findLiveHeightRange()
563
- if (!live.isEmpty) {
564
- this.subscriberCallbacksEnabled = true
565
- this.log(`listening at height of ${live.maxHeight}`)
575
+ count = 0
566
576
  }
577
+ if (!this.subscriberCallbacksEnabled) {
578
+ const live = await this.storage.findLiveHeightRange()
579
+ if (!live.isEmpty) {
580
+ this.subscriberCallbacksEnabled = true
581
+ this.log(`listening at height of ${live.maxHeight}`)
582
+ }
583
+ }
584
+ if (!this.available) {
585
+ this.available = true
586
+ }
587
+ needSyncCheck = Date.now() - lastSyncCheck > syncCheckRepeatMsecs
588
+ // If we aren't going to review sync, wait before checking input queues again
589
+ if (!needSyncCheck) await wait(1000)
567
590
  }
568
- if (!this.available) {
569
- this.available = true
570
- }
571
- needSyncCheck = Date.now() - lastSyncCheck > syncCheckRepeatMsecs
572
- // If we aren't going to review sync, wait before checking input queues again
573
- if (!needSyncCheck) await wait(1000)
574
591
  }
575
592
  }
593
+ } catch (eu: unknown) {
594
+ const e = WalletError.fromUnknown(eu)
595
+ if (!this.available) {
596
+ this.startupError = e
597
+ this.stopMainThread = true
598
+ } else {
599
+ this.log(`Error occurred during chaintracks main thread processing: ${e.stack || e.message}`)
600
+ }
576
601
  }
577
602
  }
578
603
  }