UNPKG

wallet-storage-client

Version:
253 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Monitor = void 0; const index_client_1 = require("../index.client"); 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 TaskSendWaiting_1 = require("./tasks/TaskSendWaiting"); const TaskClock_1 = require("./tasks/TaskClock"); const TaskNewHeader_1 = require("./tasks/TaskNewHeader"); /** * 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 index_client_1.Services(chain)); if (!services.options.chaintracks) throw new index_client_1.sdk.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; } addAllTasksToOther() { this._otherTasks.push(new TaskClock_1.TaskClock(this)); this._otherTasks.push(new TaskNewHeader_1.TaskNewHeader(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 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 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 TaskFailAbandoned_1.TaskFailAbandoned(this, 8 * this.oneMinute)); this._tasks.push(new TaskPurge_1.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 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 TaskFailAbandoned_1.TaskFailAbandoned(this, 8 * this.oneMinute)); this._tasks.push(new TaskPurge_1.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 index_client_1.sdk.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 = index_client_1.sdk.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 = index_client_1.sdk.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}`); await this.logEvent(ttr.name, log); } } } catch (eu) { const e = index_client_1.sdk.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 index_client_1.sdk.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, index_client_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; } /** * 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; function sum(a, getNum) { let s = 0; for (const v of a) s += getNum(v); return s; } function filter(a, pred) { const ts = []; const fs = []; for (const v of a) if (pred(v)) ts.push(v); else fs.push(v); return { ts, fs }; } //# sourceMappingURL=Monitor.js.map