@bopen-io/wallet-toolbox 1.7.18
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/.claude/settings.local.json +10 -0
- package/.env.template +22 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/discussion.md +24 -0
- package/.github/pull_request_template.md +22 -0
- package/.github/workflows/push.yaml +145 -0
- package/.prettierrc +10 -0
- package/CHANGELOG.md +280 -0
- package/CONTRIBUTING.md +89 -0
- package/README.md +43 -0
- package/docs/README.md +85 -0
- package/docs/client.md +19627 -0
- package/docs/monitor.md +953 -0
- package/docs/open-rpc/index.html +46 -0
- package/docs/services.md +6377 -0
- package/docs/setup.md +1268 -0
- package/docs/storage.md +5367 -0
- package/docs/wallet.md +19626 -0
- package/jest.config.ts +25 -0
- package/license.md +28 -0
- package/out/tsconfig.all.tsbuildinfo +1 -0
- package/package.json +63 -0
- package/src/CWIStyleWalletManager.ts +1999 -0
- package/src/Setup.ts +579 -0
- package/src/SetupClient.ts +322 -0
- package/src/SetupWallet.ts +108 -0
- package/src/SimpleWalletManager.ts +526 -0
- package/src/Wallet.ts +1169 -0
- package/src/WalletAuthenticationManager.ts +153 -0
- package/src/WalletLogger.ts +213 -0
- package/src/WalletPermissionsManager.ts +3660 -0
- package/src/WalletSettingsManager.ts +114 -0
- package/src/__tests/CWIStyleWalletManager.test.d.ts.map +1 -0
- package/src/__tests/CWIStyleWalletManager.test.js.map +1 -0
- package/src/__tests/CWIStyleWalletManager.test.ts +675 -0
- package/src/__tests/WalletPermissionsManager.callbacks.test.ts +323 -0
- package/src/__tests/WalletPermissionsManager.checks.test.ts +844 -0
- package/src/__tests/WalletPermissionsManager.encryption.test.ts +412 -0
- package/src/__tests/WalletPermissionsManager.fixtures.ts +307 -0
- package/src/__tests/WalletPermissionsManager.flows.test.ts +462 -0
- package/src/__tests/WalletPermissionsManager.initialization.test.ts +300 -0
- package/src/__tests/WalletPermissionsManager.pmodules.test.ts +798 -0
- package/src/__tests/WalletPermissionsManager.proxying.test.ts +724 -0
- package/src/__tests/WalletPermissionsManager.tokens.test.ts +503 -0
- package/src/index.all.ts +27 -0
- package/src/index.client.ts +25 -0
- package/src/index.mobile.ts +21 -0
- package/src/index.ts +1 -0
- package/src/monitor/Monitor.ts +412 -0
- package/src/monitor/MonitorDaemon.ts +188 -0
- package/src/monitor/README.md +3 -0
- package/src/monitor/__test/MonitorDaemon.man.test.ts +45 -0
- package/src/monitor/tasks/TaskCheckForProofs.ts +243 -0
- package/src/monitor/tasks/TaskCheckNoSends.ts +73 -0
- package/src/monitor/tasks/TaskClock.ts +33 -0
- package/src/monitor/tasks/TaskFailAbandoned.ts +54 -0
- package/src/monitor/tasks/TaskMonitorCallHistory.ts +26 -0
- package/src/monitor/tasks/TaskNewHeader.ts +93 -0
- package/src/monitor/tasks/TaskPurge.ts +68 -0
- package/src/monitor/tasks/TaskReorg.ts +89 -0
- package/src/monitor/tasks/TaskReviewStatus.ts +48 -0
- package/src/monitor/tasks/TaskSendWaiting.ts +122 -0
- package/src/monitor/tasks/TaskSyncWhenIdle.ts +26 -0
- package/src/monitor/tasks/TaskUnFail.ts +151 -0
- package/src/monitor/tasks/WalletMonitorTask.ts +47 -0
- package/src/sdk/CertOpsWallet.ts +18 -0
- package/src/sdk/PrivilegedKeyManager.ts +372 -0
- package/src/sdk/README.md +13 -0
- package/src/sdk/WERR_errors.ts +234 -0
- package/src/sdk/WalletError.ts +170 -0
- package/src/sdk/WalletErrorFromJson.ts +80 -0
- package/src/sdk/WalletServices.interfaces.ts +700 -0
- package/src/sdk/WalletSigner.interfaces.ts +11 -0
- package/src/sdk/WalletStorage.interfaces.ts +606 -0
- package/src/sdk/__test/CertificateLifeCycle.test.ts +131 -0
- package/src/sdk/__test/PrivilegedKeyManager.test.ts +738 -0
- package/src/sdk/__test/WalletError.test.ts +318 -0
- package/src/sdk/__test/validationHelpers.test.ts +21 -0
- package/src/sdk/index.ts +10 -0
- package/src/sdk/types.ts +226 -0
- package/src/services/README.md +11 -0
- package/src/services/ServiceCollection.ts +248 -0
- package/src/services/Services.ts +603 -0
- package/src/services/__tests/ARC.man.test.ts +123 -0
- package/src/services/__tests/ARC.timeout.man.test.ts +79 -0
- package/src/services/__tests/ArcGorillaPool.man.test.ts +108 -0
- package/src/services/__tests/arcServices.test.ts +8 -0
- package/src/services/__tests/bitrails.test.ts +56 -0
- package/src/services/__tests/getMerklePath.test.ts +15 -0
- package/src/services/__tests/getRawTx.test.ts +13 -0
- package/src/services/__tests/postBeef.test.ts +104 -0
- package/src/services/__tests/verifyBeef.test.ts +50 -0
- package/src/services/chaintracker/BHServiceClient.ts +212 -0
- package/src/services/chaintracker/ChaintracksChainTracker.ts +71 -0
- package/src/services/chaintracker/__tests/ChaintracksChainTracker.test.ts +33 -0
- package/src/services/chaintracker/__tests/ChaintracksServiceClient.test.ts +29 -0
- package/src/services/chaintracker/chaintracks/Api/BlockHeaderApi.ts +72 -0
- package/src/services/chaintracker/chaintracks/Api/BulkIngestorApi.ts +83 -0
- package/src/services/chaintracker/chaintracks/Api/BulkStorageApi.ts +92 -0
- package/src/services/chaintracker/chaintracks/Api/ChaintracksApi.ts +64 -0
- package/src/services/chaintracker/chaintracks/Api/ChaintracksClientApi.ts +189 -0
- package/src/services/chaintracker/chaintracks/Api/ChaintracksFetchApi.ts +18 -0
- package/src/services/chaintracker/chaintracks/Api/ChaintracksFsApi.ts +58 -0
- package/src/services/chaintracker/chaintracks/Api/ChaintracksStorageApi.ts +386 -0
- package/src/services/chaintracker/chaintracks/Api/LiveIngestorApi.ts +25 -0
- package/src/services/chaintracker/chaintracks/Chaintracks.ts +609 -0
- package/src/services/chaintracker/chaintracks/ChaintracksService.ts +199 -0
- package/src/services/chaintracker/chaintracks/ChaintracksServiceClient.ts +154 -0
- package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorBase.ts +176 -0
- package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDN.ts +174 -0
- package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorCDNBabbage.ts +18 -0
- package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainCdn.ts +113 -0
- package/src/services/chaintracker/chaintracks/Ingest/BulkIngestorWhatsOnChainWs.ts +81 -0
- package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorBase.ts +86 -0
- package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorTeranodeP2P.ts +59 -0
- package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainPoll.ts +104 -0
- package/src/services/chaintracker/chaintracks/Ingest/LiveIngestorWhatsOnChainWs.ts +66 -0
- package/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainIngestorWs.ts +566 -0
- package/src/services/chaintracker/chaintracks/Ingest/WhatsOnChainServices.ts +219 -0
- package/src/services/chaintracker/chaintracks/Ingest/__tests/BulkIngestorCDNBabbage.test.ts +54 -0
- package/src/services/chaintracker/chaintracks/Ingest/__tests/LiveIngestorWhatsOnChainPoll.test.ts +33 -0
- package/src/services/chaintracker/chaintracks/Ingest/__tests/WhatsOnChainServices.test.ts +124 -0
- package/src/services/chaintracker/chaintracks/Storage/BulkStorageBase.ts +92 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksKnexMigrations.ts +104 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageBase.ts +382 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageIdb.ts +574 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageKnex.ts +438 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageMemory.ts +29 -0
- package/src/services/chaintracker/chaintracks/Storage/ChaintracksStorageNoDb.ts +304 -0
- package/src/services/chaintracker/chaintracks/Storage/__tests/ChaintracksStorageIdb.test.ts +102 -0
- package/src/services/chaintracker/chaintracks/Storage/__tests/ChaintracksStorageKnex.test.ts +45 -0
- package/src/services/chaintracker/chaintracks/__tests/Chaintracks.test.ts +77 -0
- package/src/services/chaintracker/chaintracks/__tests/ChaintracksClientApi.test.ts +192 -0
- package/src/services/chaintracker/chaintracks/__tests/LocalCdnServer.ts +75 -0
- package/src/services/chaintracker/chaintracks/__tests/createIdbChaintracks.test.ts +62 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNetBlockHeaders.json +1 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_0.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_1.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_2.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest349/mainNet_3.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNetBlockHeaders.json +1 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_0.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_1.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_2.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest379/mainNet_3.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNetBlockHeaders.json +1 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_0.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_1.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_2.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest399/mainNet_3.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNetBlockHeaders.json +1 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_0.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_1.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_2.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_3.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest402/mainNet_4.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNetBlockHeaders.json +1 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_0.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_1.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_2.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_3.headers +0 -0
- package/src/services/chaintracker/chaintracks/__tests/data/cdnTest499/mainNet_4.headers +0 -0
- package/src/services/chaintracker/chaintracks/createDefaultIdbChaintracksOptions.ts +92 -0
- package/src/services/chaintracker/chaintracks/createDefaultKnexChaintracksOptions.ts +111 -0
- package/src/services/chaintracker/chaintracks/createDefaultNoDbChaintracksOptions.ts +91 -0
- package/src/services/chaintracker/chaintracks/createIdbChaintracks.ts +60 -0
- package/src/services/chaintracker/chaintracks/createKnexChaintracks.ts +65 -0
- package/src/services/chaintracker/chaintracks/createNoDbChaintracks.ts +60 -0
- package/src/services/chaintracker/chaintracks/index.all.ts +12 -0
- package/src/services/chaintracker/chaintracks/index.client.ts +4 -0
- package/src/services/chaintracker/chaintracks/index.mobile.ts +37 -0
- package/src/services/chaintracker/chaintracks/util/BulkFileDataManager.ts +975 -0
- package/src/services/chaintracker/chaintracks/util/BulkFileDataReader.ts +60 -0
- package/src/services/chaintracker/chaintracks/util/BulkFilesReader.ts +336 -0
- package/src/services/chaintracker/chaintracks/util/BulkHeaderFile.ts +247 -0
- package/src/services/chaintracker/chaintracks/util/ChaintracksFetch.ts +69 -0
- package/src/services/chaintracker/chaintracks/util/ChaintracksFs.ts +141 -0
- package/src/services/chaintracker/chaintracks/util/HeightRange.ts +153 -0
- package/src/services/chaintracker/chaintracks/util/SingleWriterMultiReaderLock.ts +76 -0
- package/src/services/chaintracker/chaintracks/util/__tests/BulkFileDataManager.test.ts +304 -0
- package/src/services/chaintracker/chaintracks/util/__tests/ChaintracksFetch.test.ts +60 -0
- package/src/services/chaintracker/chaintracks/util/__tests/HeightRange.test.ts +67 -0
- package/src/services/chaintracker/chaintracks/util/__tests/SingleWriterMultiReaderLock.test.ts +49 -0
- package/src/services/chaintracker/chaintracks/util/blockHeaderUtilities.ts +573 -0
- package/src/services/chaintracker/chaintracks/util/dirtyHashes.ts +29 -0
- package/src/services/chaintracker/chaintracks/util/validBulkHeaderFilesByFileHash.ts +432 -0
- package/src/services/chaintracker/index.all.ts +4 -0
- package/src/services/chaintracker/index.client.ts +4 -0
- package/src/services/chaintracker/index.mobile.ts +4 -0
- package/src/services/createDefaultWalletServicesOptions.ts +77 -0
- package/src/services/index.ts +1 -0
- package/src/services/processingErrors/arcSuccessError.json +76 -0
- package/src/services/providers/ARC.ts +350 -0
- package/src/services/providers/Bitails.ts +256 -0
- package/src/services/providers/SdkWhatsOnChain.ts +83 -0
- package/src/services/providers/WhatsOnChain.ts +883 -0
- package/src/services/providers/__tests/WhatsOnChain.test.ts +242 -0
- package/src/services/providers/__tests/exchangeRates.test.ts +18 -0
- package/src/services/providers/exchangeRates.ts +265 -0
- package/src/services/providers/getBeefForTxid.ts +369 -0
- package/src/signer/README.md +5 -0
- package/src/signer/WalletSigner.ts +17 -0
- package/src/signer/methods/acquireDirectCertificate.ts +52 -0
- package/src/signer/methods/buildSignableTransaction.ts +183 -0
- package/src/signer/methods/completeSignedTransaction.ts +117 -0
- package/src/signer/methods/createAction.ts +172 -0
- package/src/signer/methods/internalizeAction.ts +106 -0
- package/src/signer/methods/proveCertificate.ts +43 -0
- package/src/signer/methods/signAction.ts +54 -0
- package/src/storage/README.md +14 -0
- package/src/storage/StorageIdb.ts +2304 -0
- package/src/storage/StorageKnex.ts +1425 -0
- package/src/storage/StorageProvider.ts +810 -0
- package/src/storage/StorageReader.ts +194 -0
- package/src/storage/StorageReaderWriter.ts +432 -0
- package/src/storage/StorageSyncReader.ts +34 -0
- package/src/storage/WalletStorageManager.ts +943 -0
- package/src/storage/__test/StorageIdb.test.ts +43 -0
- package/src/storage/__test/WalletStorageManager.test.ts +275 -0
- package/src/storage/__test/adminStats.man.test.ts +89 -0
- package/src/storage/__test/getBeefForTransaction.test.ts +385 -0
- package/src/storage/index.all.ts +11 -0
- package/src/storage/index.client.ts +7 -0
- package/src/storage/index.mobile.ts +6 -0
- package/src/storage/methods/ListActionsSpecOp.ts +70 -0
- package/src/storage/methods/ListOutputsSpecOp.ts +129 -0
- package/src/storage/methods/__test/GenerateChange/generateChangeSdk.test.ts +1057 -0
- package/src/storage/methods/__test/GenerateChange/randomValsUsed1.ts +20 -0
- package/src/storage/methods/__test/offsetKey.test.ts +274 -0
- package/src/storage/methods/attemptToPostReqsToNetwork.ts +389 -0
- package/src/storage/methods/createAction.ts +947 -0
- package/src/storage/methods/generateChange.ts +556 -0
- package/src/storage/methods/getBeefForTransaction.ts +139 -0
- package/src/storage/methods/getSyncChunk.ts +293 -0
- package/src/storage/methods/internalizeAction.ts +562 -0
- package/src/storage/methods/listActionsIdb.ts +183 -0
- package/src/storage/methods/listActionsKnex.ts +226 -0
- package/src/storage/methods/listCertificates.ts +73 -0
- package/src/storage/methods/listOutputsIdb.ts +203 -0
- package/src/storage/methods/listOutputsKnex.ts +263 -0
- package/src/storage/methods/offsetKey.ts +89 -0
- package/src/storage/methods/processAction.ts +420 -0
- package/src/storage/methods/purgeData.ts +251 -0
- package/src/storage/methods/purgeDataIdb.ts +10 -0
- package/src/storage/methods/reviewStatus.ts +101 -0
- package/src/storage/methods/reviewStatusIdb.ts +43 -0
- package/src/storage/methods/utils.Buffer.ts +33 -0
- package/src/storage/methods/utils.ts +56 -0
- package/src/storage/remoting/StorageClient.ts +567 -0
- package/src/storage/remoting/StorageMobile.ts +544 -0
- package/src/storage/remoting/StorageServer.ts +291 -0
- package/src/storage/remoting/__test/StorageClient.test.ts +113 -0
- package/src/storage/schema/KnexMigrations.ts +489 -0
- package/src/storage/schema/StorageIdbSchema.ts +150 -0
- package/src/storage/schema/entities/EntityBase.ts +210 -0
- package/src/storage/schema/entities/EntityCertificate.ts +188 -0
- package/src/storage/schema/entities/EntityCertificateField.ts +136 -0
- package/src/storage/schema/entities/EntityCommission.ts +148 -0
- package/src/storage/schema/entities/EntityOutput.ts +290 -0
- package/src/storage/schema/entities/EntityOutputBasket.ts +153 -0
- package/src/storage/schema/entities/EntityOutputTag.ts +121 -0
- package/src/storage/schema/entities/EntityOutputTagMap.ts +123 -0
- package/src/storage/schema/entities/EntityProvenTx.ts +319 -0
- package/src/storage/schema/entities/EntityProvenTxReq.ts +580 -0
- package/src/storage/schema/entities/EntitySyncState.ts +389 -0
- package/src/storage/schema/entities/EntityTransaction.ts +306 -0
- package/src/storage/schema/entities/EntityTxLabel.ts +121 -0
- package/src/storage/schema/entities/EntityTxLabelMap.ts +123 -0
- package/src/storage/schema/entities/EntityUser.ts +112 -0
- package/src/storage/schema/entities/MergeEntity.ts +73 -0
- package/src/storage/schema/entities/__tests/CertificateFieldTests.test.ts +353 -0
- package/src/storage/schema/entities/__tests/CertificateTests.test.ts +354 -0
- package/src/storage/schema/entities/__tests/CommissionTests.test.ts +371 -0
- package/src/storage/schema/entities/__tests/OutputBasketTests.test.ts +278 -0
- package/src/storage/schema/entities/__tests/OutputTagMapTests.test.ts +242 -0
- package/src/storage/schema/entities/__tests/OutputTagTests.test.ts +288 -0
- package/src/storage/schema/entities/__tests/OutputTests.test.ts +464 -0
- package/src/storage/schema/entities/__tests/ProvenTxReqTests.test.ts +340 -0
- package/src/storage/schema/entities/__tests/ProvenTxTests.test.ts +504 -0
- package/src/storage/schema/entities/__tests/SyncStateTests.test.ts +288 -0
- package/src/storage/schema/entities/__tests/TransactionTests.test.ts +604 -0
- package/src/storage/schema/entities/__tests/TxLabelMapTests.test.ts +361 -0
- package/src/storage/schema/entities/__tests/TxLabelTests.test.ts +198 -0
- package/src/storage/schema/entities/__tests/stampLogTests.test.ts +90 -0
- package/src/storage/schema/entities/__tests/usersTests.test.ts +340 -0
- package/src/storage/schema/entities/index.ts +16 -0
- package/src/storage/schema/tables/TableCertificate.ts +21 -0
- package/src/storage/schema/tables/TableCertificateField.ts +12 -0
- package/src/storage/schema/tables/TableCommission.ts +13 -0
- package/src/storage/schema/tables/TableMonitorEvent.ts +9 -0
- package/src/storage/schema/tables/TableOutput.ts +64 -0
- package/src/storage/schema/tables/TableOutputBasket.ts +12 -0
- package/src/storage/schema/tables/TableOutputTag.ts +10 -0
- package/src/storage/schema/tables/TableOutputTagMap.ts +9 -0
- package/src/storage/schema/tables/TableProvenTx.ts +14 -0
- package/src/storage/schema/tables/TableProvenTxReq.ts +65 -0
- package/src/storage/schema/tables/TableSettings.ts +17 -0
- package/src/storage/schema/tables/TableSyncState.ts +18 -0
- package/src/storage/schema/tables/TableTransaction.ts +54 -0
- package/src/storage/schema/tables/TableTxLabel.ts +10 -0
- package/src/storage/schema/tables/TableTxLabelMap.ts +9 -0
- package/src/storage/schema/tables/TableUser.ts +16 -0
- package/src/storage/schema/tables/index.ts +16 -0
- package/src/storage/sync/StorageMySQLDojoReader.ts +696 -0
- package/src/storage/sync/index.ts +1 -0
- package/src/utility/Format.ts +133 -0
- package/src/utility/README.md +3 -0
- package/src/utility/ReaderUint8Array.ts +187 -0
- package/src/utility/ScriptTemplateBRC29.ts +73 -0
- package/src/utility/__tests/utilityHelpers.noBuffer.test.ts +109 -0
- package/src/utility/aggregateResults.ts +68 -0
- package/src/utility/identityUtils.ts +159 -0
- package/src/utility/index.all.ts +7 -0
- package/src/utility/index.client.ts +7 -0
- package/src/utility/parseTxScriptOffsets.ts +29 -0
- package/src/utility/stampLog.ts +69 -0
- package/src/utility/tscProofToMerklePath.ts +48 -0
- package/src/utility/utilityHelpers.buffer.ts +34 -0
- package/src/utility/utilityHelpers.noBuffer.ts +60 -0
- package/src/utility/utilityHelpers.ts +275 -0
- package/src/wab-client/WABClient.ts +94 -0
- package/src/wab-client/__tests/WABClient.man.test.ts +59 -0
- package/src/wab-client/auth-method-interactors/AuthMethodInteractor.ts +47 -0
- package/src/wab-client/auth-method-interactors/DevConsoleInteractor.ts +73 -0
- package/src/wab-client/auth-method-interactors/PersonaIDInteractor.ts +35 -0
- package/src/wab-client/auth-method-interactors/TwilioPhoneInteractor.ts +72 -0
- package/syncVersions.js +71 -0
- package/test/Wallet/StorageClient/storageClient.man.test.ts +75 -0
- package/test/Wallet/action/abortAction.test.ts +47 -0
- package/test/Wallet/action/createAction.test.ts +299 -0
- package/test/Wallet/action/createAction2.test.ts +1273 -0
- package/test/Wallet/action/createActionToGenerateBeefs.man.test.ts +293 -0
- package/test/Wallet/action/internalizeAction.a.test.ts +286 -0
- package/test/Wallet/action/internalizeAction.test.ts +682 -0
- package/test/Wallet/action/relinquishOutput.test.ts +37 -0
- package/test/Wallet/certificate/acquireCertificate.test.ts +298 -0
- package/test/Wallet/certificate/listCertificates.test.ts +346 -0
- package/test/Wallet/construct/Wallet.constructor.test.ts +57 -0
- package/test/Wallet/get/getHeaderForHeight.test.ts +82 -0
- package/test/Wallet/get/getHeight.test.ts +52 -0
- package/test/Wallet/get/getKnownTxids.test.ts +86 -0
- package/test/Wallet/get/getNetwork.test.ts +27 -0
- package/test/Wallet/get/getVersion.test.ts +27 -0
- package/test/Wallet/list/listActions.test.ts +279 -0
- package/test/Wallet/list/listActions2.test.ts +1381 -0
- package/test/Wallet/list/listCertificates.test.ts +118 -0
- package/test/Wallet/list/listOutputs.test.ts +447 -0
- package/test/Wallet/live/walletLive.man.test.ts +521 -0
- package/test/Wallet/local/localWallet.man.test.ts +93 -0
- package/test/Wallet/local/localWallet2.man.test.ts +277 -0
- package/test/Wallet/signAction/mountaintop.man.test.ts +130 -0
- package/test/Wallet/specOps/specOps.man.test.ts +220 -0
- package/test/Wallet/support/janitor.man.test.ts +40 -0
- package/test/Wallet/support/operations.man.test.ts +407 -0
- package/test/Wallet/support/reqErrorReview.2025.05.06.man.test.ts +347 -0
- package/test/Wallet/sync/Wallet.sync.test.ts +215 -0
- package/test/Wallet/sync/Wallet.updateWalletLegacyTestData.man.test.ts +203 -0
- package/test/Wallet/sync/setActive.test.ts +170 -0
- package/test/WalletClient/LocalKVStore.man.test.ts +114 -0
- package/test/WalletClient/WERR.man.test.ts +35 -0
- package/test/bsv-ts-sdk/LocalKVStore.test.ts +102 -0
- package/test/checkDB.ts +57 -0
- package/test/checkdb +0 -0
- package/test/examples/backup.man.test.ts +59 -0
- package/test/examples/pushdrop.test.ts +282 -0
- package/test/monitor/Monitor.test.ts +620 -0
- package/test/services/Services.test.ts +263 -0
- package/test/storage/KnexMigrations.test.ts +86 -0
- package/test/storage/StorageMySQLDojoReader.man.test.ts +60 -0
- package/test/storage/count.test.ts +177 -0
- package/test/storage/find.test.ts +195 -0
- package/test/storage/findLegacy.test.ts +67 -0
- package/test/storage/idb/allocateChange.test.ts +251 -0
- package/test/storage/idb/count.test.ts +158 -0
- package/test/storage/idb/find.test.ts +177 -0
- package/test/storage/idb/idbSpeed.test.ts +36 -0
- package/test/storage/idb/insert.test.ts +268 -0
- package/test/storage/idb/transactionAbort.test.ts +108 -0
- package/test/storage/idb/update.test.ts +999 -0
- package/test/storage/insert.test.ts +278 -0
- package/test/storage/update.test.ts +1021 -0
- package/test/storage/update2.test.ts +897 -0
- package/test/utils/TestUtilsWalletStorage.ts +2526 -0
- package/test/utils/localWalletMethods.ts +363 -0
- package/test/utils/removeFailedFromDatabase.sql +17 -0
- package/ts2md.json +44 -0
- package/tsconfig.all.json +31 -0
- package/tsconfig.client.json +29 -0
- package/tsconfig.json +17 -0
- package/tsconfig.mobile.json +28 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { Chaintracks } from './Chaintracks'
|
|
2
|
+
|
|
3
|
+
import { IncomingMessage, Server, ServerResponse } from 'http'
|
|
4
|
+
import express, { Request, Response } from 'express'
|
|
5
|
+
import bodyParser from 'body-parser'
|
|
6
|
+
import { Chain } from '../../../sdk/types'
|
|
7
|
+
import { createDefaultNoDbChaintracksOptions } from './createDefaultNoDbChaintracksOptions'
|
|
8
|
+
import { Services } from '../../Services'
|
|
9
|
+
import { FiatExchangeRates, WERR_INVALID_PARAMETER } from '../../../sdk'
|
|
10
|
+
import { ChaintracksInfoApi } from './Api/ChaintracksClientApi'
|
|
11
|
+
import { wait } from '../../../utility/utilityHelpers'
|
|
12
|
+
import { BaseBlockHeader, BlockHeader } from './Api/BlockHeaderApi'
|
|
13
|
+
|
|
14
|
+
export interface ChaintracksServiceOptions {
|
|
15
|
+
chain: Chain
|
|
16
|
+
/**
|
|
17
|
+
* prepended to the path of each registered service endpoint
|
|
18
|
+
*/
|
|
19
|
+
routingPrefix: string
|
|
20
|
+
/**
|
|
21
|
+
* Defaults to default configured Chaintracks instance with NoDb storage.
|
|
22
|
+
*/
|
|
23
|
+
chaintracks?: Chaintracks
|
|
24
|
+
services?: Services
|
|
25
|
+
port?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ChaintracksService {
|
|
29
|
+
static createChaintracksServiceOptions(chain: Chain): ChaintracksServiceOptions {
|
|
30
|
+
const options: ChaintracksServiceOptions = {
|
|
31
|
+
chain,
|
|
32
|
+
routingPrefix: ''
|
|
33
|
+
}
|
|
34
|
+
return options
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
chain: Chain
|
|
38
|
+
options: ChaintracksServiceOptions
|
|
39
|
+
port?: number
|
|
40
|
+
chaintracks: Chaintracks
|
|
41
|
+
services: Services
|
|
42
|
+
server?: Server<typeof IncomingMessage, typeof ServerResponse>
|
|
43
|
+
|
|
44
|
+
constructor(options: ChaintracksServiceOptions) {
|
|
45
|
+
this.options = { ...options }
|
|
46
|
+
this.port = options.port
|
|
47
|
+
this.chain = options.chain
|
|
48
|
+
this.chaintracks = options.chaintracks || new Chaintracks(createDefaultNoDbChaintracksOptions(this.chain))
|
|
49
|
+
this.services = options.services || new Services(this.chain)
|
|
50
|
+
// Prevent recursion...
|
|
51
|
+
this.services.updateFiatExchangeRateServices.remove('ChaintracksService')
|
|
52
|
+
if (this.chaintracks.chain !== this.chain || this.services.chain !== this.chain) {
|
|
53
|
+
throw new WERR_INVALID_PARAMETER(
|
|
54
|
+
'chain',
|
|
55
|
+
`All components (chaintracks and services) must be on chain ${this.chain}`
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async stopJsonRpcServer(): Promise<void> {
|
|
61
|
+
this.server?.close()
|
|
62
|
+
await this.chaintracks?.destroy()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async startJsonRpcServer(port?: number): Promise<void> {
|
|
66
|
+
await this.chaintracks.makeAvailable()
|
|
67
|
+
|
|
68
|
+
port ||= this.port || 3011
|
|
69
|
+
this.port = port
|
|
70
|
+
|
|
71
|
+
const app = express()
|
|
72
|
+
app.use(bodyParser.json())
|
|
73
|
+
|
|
74
|
+
// This allows the API to be used when CORS is enforced
|
|
75
|
+
app.use((req, res, next) => {
|
|
76
|
+
res.header('Access-Control-Allow-Origin', '*')
|
|
77
|
+
res.header('Access-Control-Allow-Headers', '*')
|
|
78
|
+
res.header('Access-Control-Allow-Methods', '*')
|
|
79
|
+
res.header('Access-Control-Expose-Headers', '*')
|
|
80
|
+
res.header('Access-Control-Allow-Private-Network', 'true')
|
|
81
|
+
if (req.method === 'OPTIONS') {
|
|
82
|
+
res.sendStatus(200)
|
|
83
|
+
} else {
|
|
84
|
+
next()
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
app.get(`/robots.txt`, (req: Request, res: Response) => {
|
|
89
|
+
res.type('text/plain')
|
|
90
|
+
res.send(`User-agent: *\nDisallow: /`)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
app.get(`/`, (req: Request, res: Response) => {
|
|
94
|
+
res.type('text/plain')
|
|
95
|
+
res.send(`Chaintracks ${this.chain}Net Block Header Service`)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
const handleErr = (err: any, res: any) => {
|
|
99
|
+
res.status(500).json({
|
|
100
|
+
status: 'error',
|
|
101
|
+
code: 'ERR_INTERNAL',
|
|
102
|
+
description: err?.message || 'An internal error has occurred.'
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const appGetVoid = (path: string, action: (q: any) => Promise<void>, noCache = false) => {
|
|
107
|
+
app['get'](this.options.routingPrefix + path, async (req, res) => {
|
|
108
|
+
if (noCache) {
|
|
109
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
110
|
+
res.setHeader('Pragma', 'no-cache')
|
|
111
|
+
res.setHeader('Expires', '0')
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
console.log(`request ${path}`)
|
|
115
|
+
await action(req.query)
|
|
116
|
+
res.status(200).json({ status: 'success' })
|
|
117
|
+
} catch (err) {
|
|
118
|
+
handleErr(err, res)
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const appGet = <T>(path: string, action: (q: any) => Promise<T>, noCache = false) => {
|
|
124
|
+
app['get'](this.options.routingPrefix + path, async (req, res) => {
|
|
125
|
+
if (noCache) {
|
|
126
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
127
|
+
res.setHeader('Pragma', 'no-cache')
|
|
128
|
+
res.setHeader('Expires', '0')
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
const r = await action(req.query)
|
|
132
|
+
console.log('request', path, JSON.stringify(req.query), '->', JSON.stringify(r))
|
|
133
|
+
res.status(200).json({ status: 'success', value: r })
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.log(`request ${path} -> error`)
|
|
136
|
+
handleErr(err, res)
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const appPostVoid = <T>(path: string, action: (p: T) => Promise<void>) => {
|
|
142
|
+
app['post'](this.options.routingPrefix + path, async (req, res) => {
|
|
143
|
+
try {
|
|
144
|
+
console.log(`request POST ${path}`)
|
|
145
|
+
await action(<T>req.body)
|
|
146
|
+
res.status(200).json({ status: 'success' })
|
|
147
|
+
} catch (err) {
|
|
148
|
+
handleErr(err, res)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
appGet<Chain>('/getChain', async () => await this.chaintracks.getChain())
|
|
154
|
+
appGet<ChaintracksInfoApi>(
|
|
155
|
+
'/getInfo',
|
|
156
|
+
async q => {
|
|
157
|
+
if (q.wait) await wait(Number(q.wait))
|
|
158
|
+
const r = await this.chaintracks.getInfo()
|
|
159
|
+
if (q.wait) r['wait'] = q.wait
|
|
160
|
+
return r
|
|
161
|
+
},
|
|
162
|
+
true
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
appGet<FiatExchangeRates>(
|
|
166
|
+
'/getFiatExchangeRates',
|
|
167
|
+
async () => {
|
|
168
|
+
// update if needed
|
|
169
|
+
await this.services.getFiatExchangeRate('GBP')
|
|
170
|
+
// return current values
|
|
171
|
+
return this.services.options.fiatExchangeRates
|
|
172
|
+
},
|
|
173
|
+
true
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
appPostVoid('/addHeaderHex', async (header: BaseBlockHeader) => {
|
|
177
|
+
await this.chaintracks.addHeader(header)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
appGet<number>('/getPresentHeight', async () => await this.chaintracks.getPresentHeight(), true)
|
|
181
|
+
appGet<string>('/findChainTipHashHex', async () => (await this.chaintracks.findChainTipHash()) || '', true)
|
|
182
|
+
appGet<BlockHeader>('/findChainTipHeaderHex', async () => await this.chaintracks.findChainTipHeader(), true)
|
|
183
|
+
|
|
184
|
+
appGet<BlockHeader | undefined>('/findHeaderHexForHeight', async q => {
|
|
185
|
+
return await this.chaintracks.findHeaderForHeight(Number(q.height))
|
|
186
|
+
})
|
|
187
|
+
appGet<BlockHeader | undefined>('/findHeaderHexForBlockHash', async q => {
|
|
188
|
+
return await this.chaintracks.findLiveHeaderForBlockHash(q.hash)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
appGet<string>('/getHeaders', async q => {
|
|
192
|
+
return await this.chaintracks.getHeaders(Number(q.height), Number(q.count))
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
this.server = app.listen(this.port, () => {
|
|
196
|
+
console.log(`ChaintracksService listening on port ${this.port}`)
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Chain } from '../../../sdk/types'
|
|
2
|
+
import { asString } from '../../../utility/utilityHelpers.noBuffer'
|
|
3
|
+
import { BaseBlockHeader, BlockHeader } from './Api/BlockHeaderApi'
|
|
4
|
+
import { ChaintracksClientApi, ChaintracksInfoApi, HeaderListener, ReorgListener } from './Api/ChaintracksClientApi'
|
|
5
|
+
|
|
6
|
+
interface FetchStatus<T> {
|
|
7
|
+
status: 'success' | 'error'
|
|
8
|
+
code?: string
|
|
9
|
+
description?: string
|
|
10
|
+
value?: T
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ChaintracksServiceClientOptions {}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Connects to a ChaintracksService to implement 'ChaintracksClientApi'
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export class ChaintracksServiceClient implements ChaintracksClientApi {
|
|
20
|
+
static createChaintracksServiceClientOptions(): ChaintracksServiceClientOptions {
|
|
21
|
+
const options: ChaintracksServiceClientOptions = {
|
|
22
|
+
useAuthrite: false
|
|
23
|
+
}
|
|
24
|
+
return options
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
options: ChaintracksServiceClientOptions
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
public chain: Chain,
|
|
31
|
+
public serviceUrl: string,
|
|
32
|
+
options?: ChaintracksServiceClientOptions
|
|
33
|
+
) {
|
|
34
|
+
this.options = options || ChaintracksServiceClient.createChaintracksServiceClientOptions()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
subscribeHeaders(listener: HeaderListener): Promise<string> {
|
|
38
|
+
throw new Error('Method not implemented.')
|
|
39
|
+
}
|
|
40
|
+
subscribeReorgs(listener: ReorgListener): Promise<string> {
|
|
41
|
+
throw new Error('Method not implemented.')
|
|
42
|
+
}
|
|
43
|
+
unsubscribe(subscriptionId: string): Promise<boolean> {
|
|
44
|
+
throw new Error('Method not implemented.')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async currentHeight(): Promise<number> {
|
|
48
|
+
return await this.getPresentHeight()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async isValidRootForHeight(root: string, height: number): Promise<boolean> {
|
|
52
|
+
const r = await this.findHeaderForHeight(height)
|
|
53
|
+
if (!r) return false
|
|
54
|
+
const isValid = root === asString(r.merkleRoot)
|
|
55
|
+
return isValid
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getJsonOrUndefined<T>(path: string): Promise<T | undefined> {
|
|
59
|
+
let e: Error | undefined = undefined
|
|
60
|
+
for (let retry = 0; retry < 3; retry++) {
|
|
61
|
+
try {
|
|
62
|
+
const r = await fetch(`${this.serviceUrl}${path}`)
|
|
63
|
+
const v = <FetchStatus<T>>await r.json()
|
|
64
|
+
if (v.status === 'success') return v.value
|
|
65
|
+
else e = new Error(JSON.stringify(v))
|
|
66
|
+
} catch (eu: unknown) {
|
|
67
|
+
e = eu as Error
|
|
68
|
+
}
|
|
69
|
+
if (e && e.name !== 'ECONNRESET') break
|
|
70
|
+
}
|
|
71
|
+
if (e) throw e
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async getJson<T>(path: string): Promise<T> {
|
|
75
|
+
const r = await this.getJsonOrUndefined<T>(path)
|
|
76
|
+
if (r === undefined) throw new Error('Value was undefined. Requested object may not exist.')
|
|
77
|
+
return r
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async postJsonVoid<T>(path: string, params: T): Promise<void> {
|
|
81
|
+
const headers = {}
|
|
82
|
+
headers['Content-Type'] = 'application/json'
|
|
83
|
+
const r = await fetch(`${this.serviceUrl}${path}`, {
|
|
84
|
+
body: JSON.stringify(params),
|
|
85
|
+
method: 'POST',
|
|
86
|
+
headers
|
|
87
|
+
//cache: 'no-cache',
|
|
88
|
+
})
|
|
89
|
+
try {
|
|
90
|
+
const s = <FetchStatus<void>>await r.json()
|
|
91
|
+
if (s.status === 'success') return
|
|
92
|
+
throw new Error(JSON.stringify(s))
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.log(`Exception: ${JSON.stringify(e)}`)
|
|
95
|
+
throw new Error(JSON.stringify(e))
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//
|
|
100
|
+
// HTTP API FUNCTIONS
|
|
101
|
+
//
|
|
102
|
+
|
|
103
|
+
async addHeader(header: BaseBlockHeader): Promise<void> {
|
|
104
|
+
const r = await this.postJsonVoid('/addHeaderHex', header)
|
|
105
|
+
if (typeof r === 'string') throw new Error(r)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async startListening(): Promise<void> {
|
|
109
|
+
await this.getPresentHeight()
|
|
110
|
+
}
|
|
111
|
+
async listening(): Promise<void> {
|
|
112
|
+
await this.getPresentHeight()
|
|
113
|
+
}
|
|
114
|
+
async getChain(): Promise<Chain> {
|
|
115
|
+
return this.chain
|
|
116
|
+
//return await this.getJson('/getChain')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async isListening(): Promise<boolean> {
|
|
120
|
+
try {
|
|
121
|
+
await this.getPresentHeight()
|
|
122
|
+
return true
|
|
123
|
+
} catch {
|
|
124
|
+
return false
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async isSynchronized(): Promise<boolean> {
|
|
128
|
+
return await this.isListening()
|
|
129
|
+
}
|
|
130
|
+
async getPresentHeight(): Promise<number> {
|
|
131
|
+
return await this.getJson('/getPresentHeight')
|
|
132
|
+
}
|
|
133
|
+
async getInfo(): Promise<ChaintracksInfoApi> {
|
|
134
|
+
return await this.getJson('/getInfo')
|
|
135
|
+
}
|
|
136
|
+
async findChainTipHeader(): Promise<BlockHeader> {
|
|
137
|
+
return await this.getJson('/findChainTipHeaderHex')
|
|
138
|
+
}
|
|
139
|
+
async findChainTipHash(): Promise<string> {
|
|
140
|
+
return await this.getJson('/findChainTipHashHex')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async getHeaders(height: number, count: number): Promise<string> {
|
|
144
|
+
return await this.getJson<string>(`/getHeaders?height=${height}&count=${count}`)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async findHeaderForHeight(height: number): Promise<BlockHeader | undefined> {
|
|
148
|
+
return await this.getJsonOrUndefined(`/findHeaderHexForHeight?height=${height}`)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async findHeaderForBlockHash(hash: string): Promise<BlockHeader | undefined> {
|
|
152
|
+
return await this.getJsonOrUndefined(`/findHeaderHexForBlockHash?hash=${hash}`)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { BulkIngestorApi, BulkIngestorBaseOptions, BulkSyncResult } from '../Api/BulkIngestorApi'
|
|
2
|
+
import { ChaintracksStorageApi } from '../Api/ChaintracksStorageApi'
|
|
3
|
+
|
|
4
|
+
import { BulkHeaderFilesInfo } from '../util/BulkHeaderFile'
|
|
5
|
+
import { HeightRange, HeightRanges } from '../util/HeightRange'
|
|
6
|
+
import { Chain } from '../../../../sdk/types'
|
|
7
|
+
import { BlockHeader } from '../Api/BlockHeaderApi'
|
|
8
|
+
import { ChaintracksStorageBase } from '../Storage/ChaintracksStorageBase'
|
|
9
|
+
|
|
10
|
+
export abstract class BulkIngestorBase implements BulkIngestorApi {
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param chain
|
|
14
|
+
* @param localCachePath defaults to './data/ingest_headers/'
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
17
|
+
static createBulkIngestorBaseOptions(chain: Chain) {
|
|
18
|
+
const options: BulkIngestorBaseOptions = {
|
|
19
|
+
chain,
|
|
20
|
+
jsonResource: `${chain}NetBlockHeaders.json`
|
|
21
|
+
}
|
|
22
|
+
return options
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
chain: Chain
|
|
26
|
+
jsonFilename: string
|
|
27
|
+
log: (...args: any[]) => void = () => {}
|
|
28
|
+
|
|
29
|
+
constructor(options: BulkIngestorBaseOptions) {
|
|
30
|
+
if (!options.jsonResource) throw new Error('The jsonFilename options property is required.')
|
|
31
|
+
this.chain = options.chain
|
|
32
|
+
this.jsonFilename = options.jsonResource
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private storageEngine: ChaintracksStorageBase | undefined
|
|
36
|
+
|
|
37
|
+
async setStorage(storage: ChaintracksStorageBase, log: (...args: any[]) => void): Promise<void> {
|
|
38
|
+
this.storageEngine = storage
|
|
39
|
+
this.log = log
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async shutdown(): Promise<void> {}
|
|
43
|
+
|
|
44
|
+
storageOrUndefined(): ChaintracksStorageApi | undefined {
|
|
45
|
+
return this.storageEngine
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
storage(): ChaintracksStorageBase {
|
|
49
|
+
if (!this.storageEngine) throw new Error('storageEngine must be set.')
|
|
50
|
+
return this.storageEngine
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* information about locally cached bulk header files managed by this bulk ingestor
|
|
55
|
+
*/
|
|
56
|
+
filesInfo: BulkHeaderFilesInfo | undefined
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* At least one derived BulkIngestor must override this method to provide the current height of the active chain tip.
|
|
60
|
+
* @returns undefined unless overridden
|
|
61
|
+
*/
|
|
62
|
+
async getPresentHeight(): Promise<number | undefined> {
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A BulkIngestor fetches and updates storage with bulk headers in bulkRange.
|
|
68
|
+
*
|
|
69
|
+
* If it can, it must also fetch live headers in fetch range that are not in bulkRange and return them as an array.
|
|
70
|
+
*
|
|
71
|
+
* The storage methods `insertBulkFile`, `updateBulkFile`, and `addBulkHeaders` should be used to add bulk headers to storage.
|
|
72
|
+
*
|
|
73
|
+
* @param before bulk and live range of headers before ingesting any new headers.
|
|
74
|
+
* @param fetchRange range of headers still needed, includes both missing bulk and live headers.
|
|
75
|
+
* @param bulkRange range of bulk headers still needed
|
|
76
|
+
* @param priorLiveHeaders any headers accumulated by prior bulk ingestor(s) that are too recent for bulk storage.
|
|
77
|
+
* @returns new live headers: headers in fetchRange but not in bulkRange
|
|
78
|
+
*/
|
|
79
|
+
abstract fetchHeaders(
|
|
80
|
+
before: HeightRanges,
|
|
81
|
+
fetchRange: HeightRange,
|
|
82
|
+
bulkRange: HeightRange,
|
|
83
|
+
priorLiveHeaders: BlockHeader[]
|
|
84
|
+
): Promise<BlockHeader[]>
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A BulkIngestor has two potential goals:
|
|
88
|
+
* 1. To source missing bulk headers and include them in bulk storage.
|
|
89
|
+
* 2. To source missing live headers to be forwarded to live storage.
|
|
90
|
+
*
|
|
91
|
+
* @param presentHeight current height of the active chain tip, may lag the true value.
|
|
92
|
+
* @param before current bulk and live storage height ranges, either may be empty.
|
|
93
|
+
* @param priorLiveHeaders any headers accumulated by prior bulk ingestor(s) that are too recent for bulk storage.
|
|
94
|
+
* @returns updated priorLiveHeaders including any accumulated by this ingestor
|
|
95
|
+
*/
|
|
96
|
+
async synchronize(
|
|
97
|
+
presentHeight: number,
|
|
98
|
+
before: HeightRanges,
|
|
99
|
+
priorLiveHeaders: BlockHeader[]
|
|
100
|
+
): Promise<BulkSyncResult> {
|
|
101
|
+
const storage = this.storage()
|
|
102
|
+
|
|
103
|
+
const r: BulkSyncResult = {
|
|
104
|
+
liveHeaders: priorLiveHeaders,
|
|
105
|
+
liveRange: HeightRange.from(priorLiveHeaders),
|
|
106
|
+
done: false,
|
|
107
|
+
log: ''
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Decisions to be made:
|
|
111
|
+
// Q1. Are we already done?
|
|
112
|
+
// Q2. Are there live headers that should be migrated to bulk?
|
|
113
|
+
// Q3. What range of headers do we still need to retrieve?
|
|
114
|
+
|
|
115
|
+
// Q1: We are done if we have enough live headers and they include presentHeight.
|
|
116
|
+
const currentFullRange = before.bulk.union(before.live)
|
|
117
|
+
if (currentFullRange.maxHeight >= presentHeight) {
|
|
118
|
+
r.done = true
|
|
119
|
+
return r
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const targetBulkRange = new HeightRange(0, Math.max(0, presentHeight - storage.liveHeightThreshold))
|
|
123
|
+
let missingBulkRange = targetBulkRange.subtract(before.bulk)
|
|
124
|
+
|
|
125
|
+
const updateMissingBulkRange = async () => {
|
|
126
|
+
before = await storage.getAvailableHeightRanges()
|
|
127
|
+
missingBulkRange = targetBulkRange.subtract(before.bulk)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Q2: If missingBulkRange isn't empty and there are live headers in storage,
|
|
131
|
+
// migrate from existing live headers in excess of reorgHeightThreshold.
|
|
132
|
+
if (!missingBulkRange.isEmpty && !before.live.isEmpty) {
|
|
133
|
+
const countToMigrate = Math.min(
|
|
134
|
+
missingBulkRange.length,
|
|
135
|
+
Math.max(0, before.live.length - storage.reorgHeightThreshold)
|
|
136
|
+
)
|
|
137
|
+
r.log += `Migrating ${countToMigrate} live headers to bulk storage.\n`
|
|
138
|
+
await storage.migrateLiveToBulk(countToMigrate)
|
|
139
|
+
await updateMissingBulkRange()
|
|
140
|
+
if (!missingBulkRange.isEmpty) {
|
|
141
|
+
// If there are still missing bulk headers, MUST flush live storage.
|
|
142
|
+
const countToFlush = before.live.length
|
|
143
|
+
r.log += `Flushing ${countToFlush} live headers from live storage.\n`
|
|
144
|
+
await storage.deleteLiveBlockHeaders()
|
|
145
|
+
await updateMissingBulkRange()
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const targetFullRange = new HeightRange(0, presentHeight)
|
|
150
|
+
// Q3: What to fetch...
|
|
151
|
+
let rangeToFetch: HeightRange
|
|
152
|
+
if (missingBulkRange.isEmpty) {
|
|
153
|
+
// If there are no missing bulk headers, we don't need existing bulk range.
|
|
154
|
+
rangeToFetch = targetFullRange.subtract(before.bulk)
|
|
155
|
+
// And if there are live headers in excess of reorgHeightThreshold, they can be skipped as well.
|
|
156
|
+
if (before.live.length > storage.reorgHeightThreshold) {
|
|
157
|
+
rangeToFetch = rangeToFetch.subtract(
|
|
158
|
+
new HeightRange(before.live.minHeight, before.live.maxHeight - storage.reorgHeightThreshold)
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
// If there are missing bulk headers, ingest from start of missing through present height.
|
|
163
|
+
rangeToFetch = new HeightRange(missingBulkRange.minHeight, presentHeight)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const newLiveHeaders = await this.fetchHeaders(before, rangeToFetch, missingBulkRange, priorLiveHeaders)
|
|
167
|
+
|
|
168
|
+
await updateMissingBulkRange()
|
|
169
|
+
|
|
170
|
+
r.liveHeaders = newLiveHeaders
|
|
171
|
+
r.liveRange = HeightRange.from(r.liveHeaders)
|
|
172
|
+
r.done = missingBulkRange.isEmpty && r.liveRange.maxHeight >= presentHeight
|
|
173
|
+
|
|
174
|
+
return r
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Chain } from '../../../../sdk/types'
|
|
2
|
+
import { BlockHeader } from '../Api/BlockHeaderApi'
|
|
3
|
+
import { BulkIngestorBaseOptions } from '../Api/BulkIngestorApi'
|
|
4
|
+
import { BulkIngestorBase } from './BulkIngestorBase'
|
|
5
|
+
import { BulkHeaderFileInfo } from '../util/BulkHeaderFile'
|
|
6
|
+
import { BulkHeaderFilesInfo } from '../util/BulkHeaderFile'
|
|
7
|
+
import { HeightRange, HeightRanges } from '../util/HeightRange'
|
|
8
|
+
import { ChaintracksFetchApi } from '../Api/ChaintracksFetchApi'
|
|
9
|
+
import { WalletError, WERR_INVALID_PARAMETER } from '../../../../sdk'
|
|
10
|
+
import { validateBulkFileData } from '../util/blockHeaderUtilities'
|
|
11
|
+
import { selectBulkHeaderFiles } from '../util/BulkFileDataManager'
|
|
12
|
+
|
|
13
|
+
export interface BulkIngestorCDNOptions extends BulkIngestorBaseOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Required.
|
|
16
|
+
*
|
|
17
|
+
* The name of the JSON resource to request from CDN which describes currently
|
|
18
|
+
* available bulk block header resources.
|
|
19
|
+
*/
|
|
20
|
+
jsonResource: string | undefined
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Required.
|
|
24
|
+
*
|
|
25
|
+
* URL to CDN implementing the bulk ingestor CDN service protocol
|
|
26
|
+
*/
|
|
27
|
+
cdnUrl: string | undefined
|
|
28
|
+
|
|
29
|
+
maxPerFile: number | undefined
|
|
30
|
+
|
|
31
|
+
fetch: ChaintracksFetchApi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class BulkIngestorCDN extends BulkIngestorBase {
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param chain
|
|
38
|
+
* @param localCachePath defaults to './data/bulk_cdn_headers/'
|
|
39
|
+
* @returns
|
|
40
|
+
*/
|
|
41
|
+
static createBulkIngestorCDNOptions(
|
|
42
|
+
chain: Chain,
|
|
43
|
+
cdnUrl: string,
|
|
44
|
+
fetch: ChaintracksFetchApi,
|
|
45
|
+
maxPerFile?: number
|
|
46
|
+
): BulkIngestorCDNOptions {
|
|
47
|
+
const options: BulkIngestorCDNOptions = {
|
|
48
|
+
...BulkIngestorBase.createBulkIngestorBaseOptions(chain),
|
|
49
|
+
fetch,
|
|
50
|
+
jsonResource: `${chain}NetBlockHeaders.json`,
|
|
51
|
+
cdnUrl,
|
|
52
|
+
maxPerFile
|
|
53
|
+
}
|
|
54
|
+
return options
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fetch: ChaintracksFetchApi
|
|
58
|
+
jsonResource: string
|
|
59
|
+
cdnUrl: string
|
|
60
|
+
maxPerFile: number | undefined
|
|
61
|
+
|
|
62
|
+
availableBulkFiles: BulkHeaderFilesInfo | undefined
|
|
63
|
+
selectedFiles: BulkHeaderFileInfo[] | undefined
|
|
64
|
+
currentRange: HeightRange | undefined
|
|
65
|
+
|
|
66
|
+
constructor(options: BulkIngestorCDNOptions) {
|
|
67
|
+
super(options)
|
|
68
|
+
if (!options.jsonResource) throw new Error('The jsonResource options property is required.')
|
|
69
|
+
if (!options.cdnUrl) throw new Error('The cdnUrl options property is required.')
|
|
70
|
+
|
|
71
|
+
this.fetch = options.fetch
|
|
72
|
+
this.jsonResource = options.jsonResource
|
|
73
|
+
this.cdnUrl = options.cdnUrl
|
|
74
|
+
this.maxPerFile = options.maxPerFile
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override async getPresentHeight(): Promise<number | undefined> {
|
|
78
|
+
return undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getJsonHttpHeaders(): Record<string, string> {
|
|
82
|
+
const headers: Record<string, string> = {
|
|
83
|
+
Accept: 'application/json'
|
|
84
|
+
}
|
|
85
|
+
return headers
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A BulkFile CDN serves a JSON BulkHeaderFilesInfo resource which lists all the available binary bulk header files available and associated metadata.
|
|
90
|
+
*
|
|
91
|
+
* The term "CDN file" is used for a local bulk file that has a sourceUrl. (Not undefined)
|
|
92
|
+
* The term "incremental file" is used for the local bulk file that holds all the non-CDN bulk headers and must chain to the live headers if there are any.
|
|
93
|
+
*
|
|
94
|
+
* Bulk ingesting from a CDN happens in one of three contexts:
|
|
95
|
+
*
|
|
96
|
+
* 1. Cold Start: No local bulk or live headers.
|
|
97
|
+
* 2. Incremental: Available CDN files extend into an existing incremental file but not into the live headers.
|
|
98
|
+
* 3. Replace: Available CDN files extend into live headers.
|
|
99
|
+
*
|
|
100
|
+
* Context Cold Start:
|
|
101
|
+
* - The CDN files are selected in height order, starting at zero, always choosing the largest count less than the local maximum (maxPerFile).
|
|
102
|
+
*
|
|
103
|
+
* Context Incremental:
|
|
104
|
+
* - Last existing CDN file is updated if CDN now has a higher count.
|
|
105
|
+
* - Additional CDN files are added as in Cold Start.
|
|
106
|
+
* - The existing incremental file is truncated or deleted.
|
|
107
|
+
*
|
|
108
|
+
* Context Replace:
|
|
109
|
+
* - Existing live headers are truncated or deleted.
|
|
110
|
+
* - Proceed as context Incremental.
|
|
111
|
+
*
|
|
112
|
+
* @param before bulk and live range of headers before ingesting any new headers.
|
|
113
|
+
* @param fetchRange total range of header heights needed including live headers
|
|
114
|
+
* @param bulkRange range of missing bulk header heights required.
|
|
115
|
+
* @param priorLiveHeaders
|
|
116
|
+
* @returns
|
|
117
|
+
*/
|
|
118
|
+
async fetchHeaders(
|
|
119
|
+
before: HeightRanges,
|
|
120
|
+
fetchRange: HeightRange,
|
|
121
|
+
bulkRange: HeightRange,
|
|
122
|
+
priorLiveHeaders: BlockHeader[]
|
|
123
|
+
): Promise<BlockHeader[]> {
|
|
124
|
+
const storage = this.storage()
|
|
125
|
+
|
|
126
|
+
const toUrl = (file: string) => this.fetch.pathJoin(this.cdnUrl, file)
|
|
127
|
+
|
|
128
|
+
const url = toUrl(this.jsonResource)
|
|
129
|
+
this.availableBulkFiles = await this.fetch.fetchJson(url)
|
|
130
|
+
if (!this.availableBulkFiles) {
|
|
131
|
+
throw new WERR_INVALID_PARAMETER(
|
|
132
|
+
`${this.jsonResource}`,
|
|
133
|
+
`a valid BulkHeaderFilesInfo JSON resource available from ${url}`
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
this.selectedFiles = selectBulkHeaderFiles(
|
|
137
|
+
this.availableBulkFiles.files,
|
|
138
|
+
this.chain,
|
|
139
|
+
this.maxPerFile || this.availableBulkFiles.headersPerFile
|
|
140
|
+
)
|
|
141
|
+
for (const bf of this.selectedFiles) {
|
|
142
|
+
if (!bf.fileHash) {
|
|
143
|
+
throw new WERR_INVALID_PARAMETER(`fileHash`, `valid for alll files in ${this.jsonResource} from ${url}`)
|
|
144
|
+
}
|
|
145
|
+
if (!bf.chain || bf.chain !== this.chain) {
|
|
146
|
+
throw new WERR_INVALID_PARAMETER(`chain`, `"${this.chain}" for all files in ${this.jsonResource} from ${url}`)
|
|
147
|
+
}
|
|
148
|
+
if (!bf.sourceUrl || bf.sourceUrl !== this.cdnUrl) bf.sourceUrl = this.cdnUrl
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let log = 'BulkIngestorCDN fetchHeaders log:\n'
|
|
152
|
+
log += ` url: ${url}\n`
|
|
153
|
+
|
|
154
|
+
this.currentRange = await storage.bulkManager.getHeightRange()
|
|
155
|
+
log += ` bulk range before: ${this.currentRange}\n`
|
|
156
|
+
|
|
157
|
+
const r = await storage.bulkManager.merge(this.selectedFiles)
|
|
158
|
+
|
|
159
|
+
this.currentRange = await storage.bulkManager.getHeightRange()
|
|
160
|
+
log += ` bulk range after: ${this.currentRange}\n`
|
|
161
|
+
for (const u of r.unchanged) {
|
|
162
|
+
log += ` unchanged: ${u.fileName}, fileId=${u.fileId}\n`
|
|
163
|
+
}
|
|
164
|
+
for (const i of r.inserted) {
|
|
165
|
+
log += ` inserted: ${i.fileName}, fileId=${i.fileId}\n`
|
|
166
|
+
}
|
|
167
|
+
for (const u of r.updated) {
|
|
168
|
+
log += ` updated: ${u.fileName}, fileId=${u.fileId}\n`
|
|
169
|
+
}
|
|
170
|
+
this.log(log)
|
|
171
|
+
|
|
172
|
+
return priorLiveHeaders
|
|
173
|
+
}
|
|
174
|
+
}
|