@bsv/wallet-toolbox 1.6.15 → 1.6.16
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/docs/client.md +103 -91
- package/docs/services.md +103 -91
- package/docs/wallet.md +103 -91
- package/mobile/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts +1 -0
- package/mobile/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts.map +1 -1
- package/mobile/out/src/services/chaintracker/chaintracks/Chaintracks.js +125 -109
- package/mobile/out/src/services/chaintracker/chaintracks/Chaintracks.js.map +1 -1
- package/mobile/package-lock.json +2 -2
- package/mobile/package.json +1 -1
- package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts +1 -0
- package/out/src/services/chaintracker/chaintracks/Chaintracks.d.ts.map +1 -1
- package/out/src/services/chaintracker/chaintracks/Chaintracks.js +125 -109
- package/out/src/services/chaintracker/chaintracks/Chaintracks.js.map +1 -1
- package/out/tsconfig.all.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/services/chaintracker/chaintracks/Chaintracks.ts +124 -109
package/package.json
CHANGED
|
@@ -14,6 +14,7 @@ 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'
|
|
17
18
|
|
|
18
19
|
export class Chaintracks implements ChaintracksManagementApi {
|
|
19
20
|
static createOptions(chain: Chain): ChaintracksOptions {
|
|
@@ -47,6 +48,7 @@ export class Chaintracks implements ChaintracksManagementApi {
|
|
|
47
48
|
private addLiveRecursionLimit = 11
|
|
48
49
|
|
|
49
50
|
private available = false
|
|
51
|
+
private startupError: WalletError | null = null
|
|
50
52
|
|
|
51
53
|
private subscriberCallbacksEnabled = false
|
|
52
54
|
private stopMainThread = true
|
|
@@ -169,9 +171,11 @@ export class Chaintracks implements ChaintracksManagementApi {
|
|
|
169
171
|
this.promises.push(this.mainThreadShiftLiveHeaders())
|
|
170
172
|
|
|
171
173
|
// Wait for the main thread to finish initial sync.
|
|
172
|
-
while (!this.available) {
|
|
174
|
+
while (!this.available && !this.startupError) {
|
|
173
175
|
await wait(100)
|
|
174
176
|
}
|
|
177
|
+
|
|
178
|
+
if (this.startupError) throw this.startupError
|
|
175
179
|
})
|
|
176
180
|
}
|
|
177
181
|
|
|
@@ -432,147 +436,158 @@ export class Chaintracks implements ChaintracksManagementApi {
|
|
|
432
436
|
const syncCheckRepeatMsecs = 30 * 60 * 1000 // 30 minutes
|
|
433
437
|
|
|
434
438
|
while (!this.stopMainThread) {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
439
|
+
try {
|
|
440
|
+
// Review the need for bulk sync...
|
|
441
|
+
const now = Date.now()
|
|
442
|
+
lastSyncCheck = now
|
|
438
443
|
|
|
439
|
-
|
|
440
|
-
|
|
444
|
+
const presentHeight = await this.getPresentHeight()
|
|
445
|
+
const before = await this.storage.getAvailableHeightRanges()
|
|
441
446
|
|
|
442
|
-
|
|
443
|
-
|
|
447
|
+
// Skip bulk sync if within less than half the recursion limit of present height
|
|
448
|
+
let skipBulkSync =
|
|
449
|
+
!before.live.isEmpty && before.live.maxHeight >= presentHeight - this.addLiveRecursionLimit / 2
|
|
444
450
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
451
|
+
if (skipBulkSync && now - lastSyncCheck > cdnSyncRepeatMsecs) {
|
|
452
|
+
// If we haven't re-synced in a long time, do it just to check for a CDN update.
|
|
453
|
+
skipBulkSync = false
|
|
454
|
+
}
|
|
449
455
|
|
|
450
|
-
|
|
456
|
+
this.log(`Chaintracks Update Services: Bulk Header Sync Review
|
|
451
457
|
presentHeight=${presentHeight} addLiveRecursionLimit=${this.addLiveRecursionLimit}
|
|
452
458
|
Before synchronize: bulk ${before.bulk}, live ${before.live}
|
|
453
459
|
${skipBulkSync ? 'Skipping' : 'Starting'} syncBulkStorage.
|
|
454
460
|
`)
|
|
455
461
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
462
|
+
if (!skipBulkSync) {
|
|
463
|
+
// Bring bulk storage up-to-date and (re-)initialize liveHeaders
|
|
464
|
+
lastBulkSync = now
|
|
465
|
+
if (this.available)
|
|
466
|
+
// Once available, initial write lock is released, take a new one to update bulk storage.
|
|
467
|
+
await this.syncBulkStorage(presentHeight, before)
|
|
468
|
+
else
|
|
469
|
+
// While still not available, the makeAvailable write lock is held.
|
|
470
|
+
await this.syncBulkStorageNoLock(presentHeight, before)
|
|
471
|
+
}
|
|
466
472
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
)
|
|
473
|
+
let count = 0
|
|
474
|
+
let liveHeaderDupes = 0
|
|
475
|
+
let needSyncCheck = false
|
|
476
|
+
|
|
477
|
+
for (; !needSyncCheck && !this.stopMainThread; ) {
|
|
478
|
+
let header = this.liveHeaders.shift()
|
|
479
|
+
if (header) {
|
|
480
|
+
// Process a "live" block header...
|
|
481
|
+
let recursions = this.addLiveRecursionLimit
|
|
482
|
+
for (; !needSyncCheck && !this.stopMainThread; ) {
|
|
483
|
+
//console.log(`Processing liveHeader: height: ${header.height} hash: ${header.hash} ${new Date().toISOString()}`)
|
|
484
|
+
const ihr = await this.addLiveHeader(header)
|
|
485
|
+
if (this.invalidInsertHeaderResult(ihr)) {
|
|
486
|
+
this.log(`Ignoring liveHeader ${header.height} ${header.hash} due to invalid insert result.`)
|
|
489
487
|
needSyncCheck = true
|
|
490
|
-
} else {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
488
|
+
} else if (ihr.noPrev) {
|
|
489
|
+
// Previous header is unknown, request it by hash from the network and try adding it first...
|
|
490
|
+
if (recursions-- <= 0) {
|
|
491
|
+
// Ignore this header...
|
|
494
492
|
this.log(
|
|
495
|
-
`Ignoring liveHeader ${header.height} ${header.hash}
|
|
493
|
+
`Ignoring liveHeader ${header.height} ${header.hash} addLiveRecursionLimit=${this.addLiveRecursionLimit} exceeded.`
|
|
496
494
|
)
|
|
497
495
|
needSyncCheck = true
|
|
498
496
|
} else {
|
|
499
|
-
|
|
500
|
-
this.
|
|
501
|
-
|
|
497
|
+
const hash = header.previousHash
|
|
498
|
+
const prevHeader = await this.getMissingBlockHeader(hash)
|
|
499
|
+
if (!prevHeader) {
|
|
500
|
+
this.log(
|
|
501
|
+
`Ignoring liveHeader ${header.height} ${header.hash} failed to find previous header by hash ${asString(hash)}`
|
|
502
|
+
)
|
|
503
|
+
needSyncCheck = true
|
|
504
|
+
} else {
|
|
505
|
+
// Switch to trying to add prevHeader, unshifting current header to try it again after prevHeader exists.
|
|
506
|
+
this.liveHeaders.unshift(header)
|
|
507
|
+
header = prevHeader
|
|
508
|
+
}
|
|
502
509
|
}
|
|
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
510
|
} else {
|
|
538
511
|
if (this.subscriberCallbacksEnabled)
|
|
539
512
|
this.log(
|
|
540
|
-
`
|
|
513
|
+
`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
514
|
)
|
|
542
|
-
|
|
515
|
+
if (ihr.dupe) {
|
|
516
|
+
liveHeaderDupes++
|
|
517
|
+
}
|
|
518
|
+
// Header wasn't invalid and previous header is known. If it was successfully added, count it as a win.
|
|
543
519
|
if (ihr.added) {
|
|
544
520
|
count++
|
|
545
521
|
}
|
|
522
|
+
break
|
|
546
523
|
}
|
|
547
524
|
}
|
|
548
525
|
} else {
|
|
549
|
-
// There are no liveHeaders
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
526
|
+
// There are no liveHeaders currently to process, check the out-of-band baseHeaders channel (`addHeader` method called by a client).
|
|
527
|
+
const bheader = this.baseHeaders.shift()
|
|
528
|
+
if (bheader) {
|
|
529
|
+
const prev = await this.storage.findLiveHeaderForBlockHash(bheader.previousHash)
|
|
530
|
+
if (!prev) {
|
|
531
|
+
// Ignoring attempt to add a baseHeader with unknown previous hash, no attempt made to find previous header(s).
|
|
532
|
+
this.log(`Ignoring header with unknown previousHash ${bheader.previousHash} in live storage.`)
|
|
533
|
+
// Does not trigger a re-sync.
|
|
534
|
+
} else {
|
|
535
|
+
const header: BlockHeader = {
|
|
536
|
+
...bheader,
|
|
537
|
+
height: prev.height + 1,
|
|
538
|
+
hash: blockHash(bheader)
|
|
539
|
+
}
|
|
540
|
+
const ihr = await this.addLiveHeader(header)
|
|
541
|
+
if (this.invalidInsertHeaderResult(ihr)) {
|
|
542
|
+
this.log(`Ignoring invalid baseHeader ${header.height} ${header.hash}.`)
|
|
543
|
+
} else {
|
|
544
|
+
if (this.subscriberCallbacksEnabled)
|
|
545
|
+
this.log(
|
|
546
|
+
`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' : ''}`
|
|
547
|
+
)
|
|
548
|
+
// baseHeader was successfully added.
|
|
549
|
+
if (ihr.added) {
|
|
550
|
+
count++
|
|
551
|
+
}
|
|
552
|
+
}
|
|
554
553
|
}
|
|
555
|
-
|
|
556
|
-
|
|
554
|
+
} else {
|
|
555
|
+
// There are no liveHeaders and no baseHeaders to add,
|
|
556
|
+
if (count > 0) {
|
|
557
|
+
if (liveHeaderDupes > 0) {
|
|
558
|
+
this.log(`${liveHeaderDupes} duplicate headers ignored.`)
|
|
559
|
+
liveHeaderDupes = 0
|
|
560
|
+
}
|
|
561
|
+
const updated = await this.storage.getAvailableHeightRanges()
|
|
562
|
+
this.log(`After adding ${count} live headers
|
|
557
563
|
After live: bulk ${updated.bulk}, live ${updated.live}
|
|
558
564
|
`)
|
|
559
|
-
|
|
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}`)
|
|
565
|
+
count = 0
|
|
566
566
|
}
|
|
567
|
+
if (!this.subscriberCallbacksEnabled) {
|
|
568
|
+
const live = await this.storage.findLiveHeightRange()
|
|
569
|
+
if (!live.isEmpty) {
|
|
570
|
+
this.subscriberCallbacksEnabled = true
|
|
571
|
+
this.log(`listening at height of ${live.maxHeight}`)
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (!this.available) {
|
|
575
|
+
this.available = true
|
|
576
|
+
}
|
|
577
|
+
needSyncCheck = Date.now() - lastSyncCheck > syncCheckRepeatMsecs
|
|
578
|
+
// If we aren't going to review sync, wait before checking input queues again
|
|
579
|
+
if (!needSyncCheck) await wait(1000)
|
|
567
580
|
}
|
|
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
581
|
}
|
|
575
582
|
}
|
|
583
|
+
} catch (eu: unknown) {
|
|
584
|
+
const e = WalletError.fromUnknown(eu)
|
|
585
|
+
if (!this.available) {
|
|
586
|
+
this.startupError = e
|
|
587
|
+
this.stopMainThread = true
|
|
588
|
+
} else {
|
|
589
|
+
this.log(`Error occurred during chaintracks main thread processing: ${e.stack || e.message}`)
|
|
590
|
+
}
|
|
576
591
|
}
|
|
577
592
|
}
|
|
578
593
|
}
|