wallet-storage-client
Version:
Client only Wallet Storage
253 lines • 10.2 kB
JavaScript
"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