import { Monitor } from '../Monitor'; import { WalletMonitorTask } from './WalletMonitorTask'; /** * `TaskCheckNoSends` is a WalletMonitor task that retrieves merkle proofs for * 'nosend' transactions that MAY have been shared externally. * * Unlike intentionally processed transactions, 'nosend' transactions are fully valid * transactions which have not been processed by the wallet. * * By default, this task runs once a day to check if any 'nosend' transaction has * managed to get mined by some external process. * * If a proof is obtained and validated, a new ProvenTx record is created and * the original ProvenTxReq status is advanced to 'notifying'. * * # Aging schedule on the checkNow path * * When this task is triggered by a new block header (`checkNow = true`, wired in * `Monitor.processNewBlockHeader`), it does NOT scan every `nosend` row on every * block. The set of `nosend` rows can grow large over a wallet's lifetime * (txs sitting in escrow, un-aborted tests, abandoned batches), and a fast, * unfiltered scan on every block would do an unbounded number of external * `getMerklePath` lookups per block. * * Instead, the row's age (now - `created_at`) determines how often it is * eligible for a checkNow-triggered chain check. The schedule starts at "skip * entirely" for very fresh rows (to protect in-flight batched-tx workflows * where chained `createAction({ noSend: true, sendWith: [...] })` builds * deliberately keep rows in `nosend` until a single terminator broadcasts the * whole BEEF), then progresses to "every block", "hourly", "daily", and * "weekly" as rows age: * * age < 5 min → skip (in-flight batch protection) * 5 min ≤ age < 1 hr → check on every checkNow trigger * 1 hr ≤ age < 24 hr → check on ~hourly cadence (block-height % 6) * 24 hr ≤ age < 7 days → check on ~daily cadence (block-height % 144) * age ≥ 7 days → check on ~weekly cadence (block-height % 1008) * * Block-height modulo gives a deterministic, stateless way to schedule * checks for older rows; no per-row "last checked" persistence is required. * Each row's modulo offset is keyed by its `provenTxReqId` so that rows in * the same age tier are staggered across the modulo cycle rather than all * firing on the same block — `(blockHeight + provenTxReqId) % tierInterval`. * For a wallet with N rows in tier T and tier interval K, this gives * roughly N/K rows fired per block instead of N rows fired every K blocks. * * The scheduled daily cadence (no `checkNow`) is unaffected — it still scans * every row regardless of age. That path is the once-per-day fallback that * guarantees externally-broadcast `nosend` txs are eventually recognized * even if the aging schedule on the checkNow path defers them. */ export declare class TaskCheckNoSends extends WalletMonitorTask { triggerMsecs: number; static readonly taskName = "CheckNoSends"; /** * An external service such as the chaintracks new block header * listener can set this true to cause */ static checkNow: boolean; /** * Aging-schedule constants for the `checkNow` path. Rows below `tier0FreshSkipMsecs` * are never checked via checkNow (batched-tx protection). Rows from tier 0 up * to `tier1EveryBlockMsecs` are checked on every checkNow trigger. Beyond that, * checks happen on `block-height % tierNBlockInterval === 0` cadences with * growing intervals. The scheduled daily cadence (no checkNow) is unaffected. */ static readonly tier0FreshSkipMsecs: number; static readonly tier1EveryBlockMsecs: number; static readonly tier2HourlyMsecs: number; static readonly tier3DailyMsecs: number; static readonly tier2BlockInterval = 6; static readonly tier3BlockInterval = 144; static readonly tier4BlockInterval = 1008; /** * Decide whether a single `nosend` row should be chain-checked on the * current `checkNow` trigger, based on its age, the current block * height, and its `provenTxReqId` (used to stagger same-tier rows * across the modulo cycle). See class docstring for the full schedule * and staggering rationale. */ static shouldCheckOnCheckNow(createdAt: Date, nowMs: number, currentBlockHeight: number, provenTxReqId: number): boolean; constructor(monitor: Monitor, triggerMsecs?: number); /** * Normally triggered by checkNow getting set by new block header found event from chaintracks */ trigger(nowMsecsSinceEpoch: number): { run: boolean; }; runTask(): Promise; } //# sourceMappingURL=TaskCheckNoSends.d.ts.map