UNPKG

@bsv/wallet-toolbox-client

Version:
278 lines 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Monitor = void 0; const utilityHelpers_1 = require("../utility/utilityHelpers"); const TaskPurge_1 = require("./tasks/TaskPurge"); const TaskReviewStatus_1 = require("./tasks/TaskReviewStatus"); const TaskSyncWhenIdle_1 = require("./tasks/TaskSyncWhenIdle"); const TaskFailAbandoned_1 = require("./tasks/TaskFailAbandoned"); const TaskCheckForProofs_1 = require("./tasks/TaskCheckForProofs"); const TaskClock_1 = require("./tasks/TaskClock"); const TaskNewHeader_1 = require("./tasks/TaskNewHeader"); const TaskMonitorCallHistory_1 = require("./tasks/TaskMonitorCallHistory"); const TaskSendWaiting_1 = require("./tasks/TaskSendWaiting"); const TaskCheckNoSends_1 = require("./tasks/TaskCheckNoSends"); const TaskUnFail_1 = require("./tasks/TaskUnFail"); const WERR_errors_1 = require("../sdk/WERR_errors"); const WalletError_1 = require("../sdk/WalletError"); const Services_1 = require("../services/Services"); /** * Background task to make sure transactions are processed, transaction proofs are received and propagated, * and potentially that reorgs update proofs that were already received. */ class Monitor { static createDefaultWalletMonitorOptions(chain, storage, services) { services || (services = new Services_1.Services(chain)); if (!services.options.chaintracks) throw new WERR_errors_1.WERR_INVALID_PARAMETER('services.options.chaintracks', 'valid'); const o = { chain, services, storage, msecsWaitPerMerkleProofServiceReq: 500, taskRunWaitMsecs: 5000, abandonedMsecs: 1000 * 60 * 5, unprovenAttemptsLimitTest: 10, unprovenAttemptsLimitMain: 144, chaintracks: services.options.chaintracks }; return o; } constructor(options) { this.oneSecond = 1000; this.oneMinute = 60 * this.oneSecond; this.oneHour = 60 * this.oneMinute; this.oneDay = 24 * this.oneHour; this.oneWeek = 7 * this.oneDay; /** * _tasks are typically run by the scheduler but may also be run by runTask. */ this._tasks = []; /** * _otherTasks can be run by runTask but not by scheduler. */ this._otherTasks = []; this._tasksRunning = false; this.defaultPurgeParams = { purgeSpent: false, purgeCompleted: false, purgeFailed: true, purgeSpentAge: 2 * this.oneWeek, purgeCompletedAge: 2 * this.oneWeek, purgeFailedAge: 5 * this.oneDay }; this._runAsyncSetup = true; this.options = { ...options }; this.services = options.services; this.chain = this.services.chain; this.storage = options.storage; this.chaintracks = options.chaintracks; this.onTransactionProven = options.onTransactionProven; this.onTransactionBroadcasted = options.onTransactionBroadcasted; } addAllTasksToOther() { this._otherTasks.push(new TaskClock_1.TaskClock(this)); this._otherTasks.push(new TaskNewHeader_1.TaskNewHeader(this)); this._otherTasks.push(new TaskMonitorCallHistory_1.TaskMonitorCallHistory(this)); this._otherTasks.push(new TaskPurge_1.TaskPurge(this, this.defaultPurgeParams)); this._otherTasks.push(new TaskReviewStatus_1.TaskReviewStatus(this)); this._otherTasks.push(new TaskSendWaiting_1.TaskSendWaiting(this)); this._otherTasks.push(new TaskCheckForProofs_1.TaskCheckForProofs(this)); this._otherTasks.push(new TaskCheckNoSends_1.TaskCheckNoSends(this)); this._otherTasks.push(new TaskUnFail_1.TaskUnFail(this)); this._otherTasks.push(new TaskFailAbandoned_1.TaskFailAbandoned(this)); this._otherTasks.push(new TaskSyncWhenIdle_1.TaskSyncWhenIdle(this)); } /** * Default tasks with settings appropriate for a single user storage * possibly with sync'ing enabled */ addDefaultTasks() { this._tasks.push(new TaskClock_1.TaskClock(this)); this._tasks.push(new TaskNewHeader_1.TaskNewHeader(this)); this._tasks.push(new TaskMonitorCallHistory_1.TaskMonitorCallHistory(this)); this._tasks.push(new TaskSendWaiting_1.TaskSendWaiting(this, 8 * this.oneSecond, 7 * this.oneSecond)); // Check every 8 seconds but must be 7 seconds old this._tasks.push(new TaskCheckForProofs_1.TaskCheckForProofs(this, 2 * this.oneHour)); // Every two hours if no block found this._tasks.push(new TaskCheckNoSends_1.TaskCheckNoSends(this)); this._tasks.push(new TaskFailAbandoned_1.TaskFailAbandoned(this, 8 * this.oneMinute)); this._tasks.push(new TaskUnFail_1.TaskUnFail(this)); //this._tasks.push(new TaskPurge(this, this.defaultPurgeParams, 6 * this.oneHour)) this._tasks.push(new TaskReviewStatus_1.TaskReviewStatus(this)); } /** * Tasks appropriate for multi-user storage * without sync'ing enabled. */ addMultiUserTasks() { this._tasks.push(new TaskClock_1.TaskClock(this)); this._tasks.push(new TaskNewHeader_1.TaskNewHeader(this)); this._tasks.push(new TaskMonitorCallHistory_1.TaskMonitorCallHistory(this)); this._tasks.push(new TaskSendWaiting_1.TaskSendWaiting(this, 8 * this.oneSecond, 7 * this.oneSecond)); // Check every 8 seconds but must be 7 seconds old this._tasks.push(new TaskCheckForProofs_1.TaskCheckForProofs(this, 2 * this.oneHour)); // Every two hours if no block found this._tasks.push(new TaskCheckNoSends_1.TaskCheckNoSends(this)); this._tasks.push(new TaskFailAbandoned_1.TaskFailAbandoned(this, 8 * this.oneMinute)); this._tasks.push(new TaskUnFail_1.TaskUnFail(this)); //this._tasks.push(new TaskPurge(this, this.defaultPurgeParams, 6 * this.oneHour)) this._tasks.push(new TaskReviewStatus_1.TaskReviewStatus(this)); } addTask(task) { if (this._tasks.some(t => t.name === task.name)) throw new WERR_errors_1.WERR_BAD_REQUEST(`task ${task.name} has already been added.`); this._tasks.push(task); } removeTask(name) { this._tasks = this._tasks.filter(t => t.name !== name); } async setupChaintracksListeners() { try { // TODO: Use a task monitoring the newest block headere to trigger processNewHeader and reorg handling. } catch (err) { /* this chaintracks doesn't support event subscriptions */ } } async runTask(name) { let task = this._tasks.find(t => t.name === name); let log = ''; if (!task) task = this._otherTasks.find(t => t.name === name); if (task) { await task.asyncSetup(); log = await task.runTask(); } return log; } async runOnce() { if (this._runAsyncSetup) { for (const t of this._tasks) { try { await t.asyncSetup(); } catch (eu) { const e = WalletError_1.WalletError.fromUnknown(eu); const details = `monitor task ${t.name} asyncSetup error ${e.code} ${e.description}`; console.log(details); await this.logEvent('error0', details); } if (!this._tasksRunning) break; } this._runAsyncSetup = false; } if (this.storage.getActive().isStorageProvider()) { const tasksToRun = []; const now = new Date().getTime(); for (const t of this._tasks) { try { if (t.trigger(now).run) tasksToRun.push(t); } catch (eu) { const e = WalletError_1.WalletError.fromUnknown(eu); const details = `monitor task ${t.name} trigger error ${e.code} ${e.description}`; console.log(details); await this.logEvent('error0', details); } } for (const ttr of tasksToRun) { try { if (this.storage.getActive().isStorageProvider()) { const log = await ttr.runTask(); if (log && log.length > 0) { console.log(`Task${ttr.name} ${log.slice(0, 256)}`); await this.logEvent(ttr.name, log); } } } catch (eu) { const e = WalletError_1.WalletError.fromUnknown(eu); const details = `monitor task ${ttr.name} runTask error ${e.code} ${e.description}\n${e.stack}`; console.log(details); await this.logEvent('error1', details); } finally { ttr.lastRunMsecsSinceEpoch = new Date().getTime(); } } } } async startTasks() { if (this._tasksRunning) throw new WERR_errors_1.WERR_BAD_REQUEST('monitor tasks are already runnining.'); this._tasksRunning = true; for (; this._tasksRunning;) { await this.runOnce(); // console.log(`${new Date().toISOString()} tasks run, waiting...`) await (0, utilityHelpers_1.wait)(this.options.taskRunWaitMsecs); } } async logEvent(event, details) { await this.storage.runAsStorageProvider(async (sp) => { await sp.insertMonitorEvent({ created_at: new Date(), updated_at: new Date(), id: 0, event, details }); }); } stopTasks() { this._tasksRunning = false; } /** * Process new chain header event received from Chaintracks * * Kicks processing 'unconfirmed' and 'unmined' request processing. * * @param reqs */ processNewBlockHeader(header) { const h = header; this.lastNewHeader = h; this.lastNewHeaderWhen = new Date(); // console.log(`WalletMonitor notified of new block header ${h.height}`) // Nudge the proof checker to try again. TaskCheckForProofs_1.TaskCheckForProofs.checkNow = true; } /** * This is a function run from a TaskSendWaiting Monitor task. * * This allows the user of wallet-toolbox to 'subscribe' for transaction broadcast updates. * * @param broadcastResult */ callOnBroadcastedTransaction(broadcastResult) { if (this.onTransactionBroadcasted) { this.onTransactionBroadcasted(broadcastResult); } } /** * This is a function run from a TaskCheckForProofs Monitor task. * * This allows the user of wallet-toolbox to 'subscribe' for transaction updates. * * @param txStatus */ callOnProvenTransaction(txStatus) { if (this.onTransactionProven) { this.onTransactionProven(txStatus); } } /** * Process reorg event received from Chaintracks * * Reorgs can move recent transactions to new blocks at new index positions. * Affected transaction proofs become invalid and must be updated. * * It is possible for a transaction to become invalid. * * Coinbase transactions always become invalid. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars processReorg(depth, oldTip, newTip) { /* */ } } exports.Monitor = Monitor; //# sourceMappingURL=Monitor.js.map