@spirobel/monero-wallet-api 0.5.4 → 0.6.0

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.
Files changed (91) hide show
  1. package/dist/actionlog/actionLogOpened.d.ts +96 -1
  2. package/dist/actionlog/actionLogOpened.js +386 -1
  3. package/dist/actionlog/actionLogTypes.d.ts +114 -0
  4. package/dist/actionlog/actionLogTypes.js +198 -0
  5. package/dist/actionlog/backend/idbBackend.d.ts +13 -0
  6. package/dist/actionlog/backend/idbBackend.js +109 -0
  7. package/dist/actionlog/backend/jsonlBackend.d.ts +19 -0
  8. package/dist/actionlog/backend/jsonlBackend.js +110 -0
  9. package/dist/actionlog/backend/memoryBackend.d.ts +11 -0
  10. package/dist/actionlog/backend/memoryBackend.js +31 -0
  11. package/dist/actionlog/backend/sqliteBackend.d.ts +14 -0
  12. package/dist/actionlog/backend/sqliteBackend.js +96 -0
  13. package/dist/actionlog/backend.d.ts +12 -0
  14. package/dist/actionlog/backend.js +1 -0
  15. package/dist/actionlog/idbBackend.d.ts +19 -0
  16. package/dist/actionlog/idbBackend.js +98 -0
  17. package/dist/actionlog/jsonlBackend.d.ts +19 -0
  18. package/dist/actionlog/jsonlBackend.js +75 -0
  19. package/dist/actionlog/memoryBackend.d.ts +14 -0
  20. package/dist/actionlog/memoryBackend.js +38 -0
  21. package/dist/actionlog/openMaps.d.ts +11 -0
  22. package/dist/actionlog/openMaps.js +32 -0
  23. package/dist/actionlog/sqliteBackend.d.ts +18 -0
  24. package/dist/actionlog/sqliteBackend.js +105 -0
  25. package/dist/actionlog/toolContext.d.ts +36 -0
  26. package/dist/actionlog/toolContext.js +1 -0
  27. package/dist/actionlog/types.d.ts +62 -0
  28. package/dist/actionlog/types.js +62 -0
  29. package/dist/api.d.ts +4 -0
  30. package/dist/api.js +3 -0
  31. package/dist/io/sleep.d.ts +1 -1
  32. package/dist/io/sleep.js +16 -1
  33. package/dist/scanning-syncing/backgroundWorker.d.ts +2 -0
  34. package/dist/scanning-syncing/backgroundWorker.js +92 -10
  35. package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +33 -2
  36. package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +2 -2
  37. package/dist/scanning-syncing/connectionStatus.d.ts +8 -0
  38. package/dist/scanning-syncing/connectionStatus.js +50 -0
  39. package/dist/scanning-syncing/connectionStatusOpened.d.ts +3 -1
  40. package/dist/scanning-syncing/connectionStatusOpened.js +22 -14
  41. package/dist/scanning-syncing/openWallet.d.ts +4 -1
  42. package/dist/scanning-syncing/openWallet.js +3 -0
  43. package/dist/scanning-syncing/scanresult/scanCache.d.ts +5 -1
  44. package/dist/scanning-syncing/scanresult/scanCache.js +11 -2
  45. package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +56 -5
  46. package/dist/scanning-syncing/scanresult/scanCacheOpened.js +422 -83
  47. package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +7 -3
  48. package/dist/scanning-syncing/scanresult/scanCoordination.js +126 -28
  49. package/dist/scanning-syncing/scanresult/scanLoop.d.ts +1 -2
  50. package/dist/scanning-syncing/scanresult/scanLoop.js +0 -13
  51. package/dist/scanning-syncing/scanresult/scanResult.js +17 -12
  52. package/dist/scanning-syncing/scanresult/scanStats.js +8 -5
  53. package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
  54. package/dist/scanning-syncing/worker-mains/coordinator-main.d.ts +1 -0
  55. package/dist/scanning-syncing/worker-mains/coordinator-main.js +16 -1
  56. package/dist/scanning-syncing/worker-mains/cpubound-main.js +3 -2
  57. package/dist/scanning-syncing/worker-mains/worker.js +45 -2
  58. package/dist/tools/calls/001.d.ts +20 -0
  59. package/dist/tools/calls/001.js +236 -0
  60. package/dist/tools/calls/002-content.d.ts +46 -0
  61. package/dist/tools/calls/002-content.js +135 -0
  62. package/dist/tools/calls/002-fail-cleanup.d.ts +27 -0
  63. package/dist/tools/calls/002-fail-cleanup.js +60 -0
  64. package/dist/tools/calls/002.d.ts +64 -0
  65. package/dist/tools/calls/002.js +353 -0
  66. package/dist/tools/content.d.ts +22 -0
  67. package/dist/tools/content.js +41 -0
  68. package/dist/tools/counterparty.d.ts +3 -0
  69. package/dist/tools/counterparty.js +1 -0
  70. package/dist/tools/download-public-suffix.d.ts +1 -0
  71. package/dist/tools/download-public-suffix.js +20 -0
  72. package/dist/tools/globals.d.ts +196 -0
  73. package/dist/tools/globals.js +14 -0
  74. package/dist/tools/monero-tools-content.d.ts +29 -0
  75. package/dist/tools/monero-tools-content.js +80 -0
  76. package/dist/tools/monero-tools.d.ts +27 -52
  77. package/dist/tools/monero-tools.js +127 -222
  78. package/dist/tools/public-suffix-list.d.ts +1 -0
  79. package/dist/tools/public-suffix-list.js +10336 -0
  80. package/dist/tools/runtimeMessages.d.ts +4 -0
  81. package/dist/tools/runtimeMessages.js +18 -0
  82. package/dist/tools/toolActionLogContext.d.ts +33 -0
  83. package/dist/tools/toolActionLogContext.js +1 -0
  84. package/dist/tools/toolArm.d.ts +47 -0
  85. package/dist/tools/toolArm.js +1 -0
  86. package/dist/tools/ui.d.ts +2 -0
  87. package/dist/tools/ui.js +1 -0
  88. package/dist/tools/worker.d.ts +1 -0
  89. package/dist/tools/worker.js +1 -0
  90. package/dist/wasm-processing/wasmFile.js +2 -2
  91. package/package.json +8 -1
@@ -46,6 +46,36 @@ export async function createWebworker(handle_result, scan_settings_path, pathPre
46
46
  "coordinator worker started, node_url=" + node_url,
47
47
  ", cpuPorts=" + cpuPorts.length,
48
48
  ]);
49
+ let shutdownResolve;
50
+ let shuttingDown = false;
51
+ const hardTerminate = () => {
52
+ for (const p of cpuPorts) {
53
+ try {
54
+ p.close();
55
+ }
56
+ catch {
57
+ // port may already be closed
58
+ }
59
+ }
60
+ coordinationWorker.onmessage = null;
61
+ coordinationWorker.onerror = null;
62
+ for (const w of cpuWorkers) {
63
+ w.onerror = null;
64
+ w.onmessage = null;
65
+ try {
66
+ w.terminate();
67
+ }
68
+ catch {
69
+ // already dead
70
+ }
71
+ }
72
+ try {
73
+ coordinationWorker.terminate();
74
+ }
75
+ catch {
76
+ // already dead
77
+ }
78
+ };
49
79
  coordinationWorker.onmessage = (event) => {
50
80
  if (event.data.type === "ERROR") {
51
81
  if (handle_error)
@@ -55,22 +85,74 @@ export async function createWebworker(handle_result, scan_settings_path, pathPre
55
85
  if (handle_result)
56
86
  handle_result(event.data.payload);
57
87
  }
88
+ else if (event.data.type === "SHUTDOWN_DONE") {
89
+ log("createWebworker", ["SHUTDOWN_DONE received"]);
90
+ shutdownResolve?.();
91
+ shutdownResolve = undefined;
92
+ }
58
93
  };
94
+ // wait for one HEAP_SNAPSHOT_DONE / ERROR from a worker
95
+ const waitHeap = (w, path, timeoutMs = 15000) => new Promise((resolve, reject) => {
96
+ const t = setTimeout(() => {
97
+ w.removeEventListener("message", onMsg);
98
+ reject(new Error("heap snapshot timeout: " + path));
99
+ }, timeoutMs);
100
+ const onMsg = (event) => {
101
+ if (event.data?.type === "HEAP_SNAPSHOT_DONE") {
102
+ clearTimeout(t);
103
+ w.removeEventListener("message", onMsg);
104
+ resolve(event.data.path || path);
105
+ }
106
+ else if (event.data?.type === "HEAP_SNAPSHOT_ERROR") {
107
+ clearTimeout(t);
108
+ w.removeEventListener("message", onMsg);
109
+ reject(new Error(String(event.data.error || "heap snapshot failed")));
110
+ }
111
+ };
112
+ w.addEventListener("message", onMsg);
113
+ try {
114
+ w.postMessage({ type: "heap_snapshot", path });
115
+ }
116
+ catch (err) {
117
+ clearTimeout(t);
118
+ w.removeEventListener("message", onMsg);
119
+ reject(err);
120
+ }
121
+ });
59
122
  return {
60
123
  fetchWorker: coordinationWorker,
61
124
  cpuWorkers,
62
- terminate: () => {
63
- for (const p of cpuPorts) {
64
- p.close();
125
+ // graceful only: abort fetch, cancel cpus, wait. does not terminate.
126
+ shutdown: async (timeoutMs = 5000) => {
127
+ if (shuttingDown)
128
+ return;
129
+ shuttingDown = true;
130
+ log("createWebworker", ["shutdown start, timeoutMs=", timeoutMs]);
131
+ const done = new Promise((resolve) => {
132
+ shutdownResolve = resolve;
133
+ });
134
+ try {
135
+ coordinationWorker.postMessage({ type: "shutdown" });
65
136
  }
66
- coordinationWorker.onmessage = null;
67
- coordinationWorker.onerror = null;
68
- for (const w of cpuWorkers) {
69
- w.onerror = null;
70
- w.onmessage = null;
71
- w.terminate();
137
+ catch (err) {
138
+ log("createWebworker", ["post shutdown failed", String(err)]);
139
+ return;
72
140
  }
73
- coordinationWorker.terminate();
141
+ await Promise.race([done, sleep(timeoutMs)]);
142
+ },
143
+ terminate: () => {
144
+ hardTerminate();
145
+ },
146
+ dumpHeaps: async (dir = ".") => {
147
+ const stamp = Date.now();
148
+ const paths = [];
149
+ const coordPath = `${dir}/coordinator-${stamp}.heapsnapshot`;
150
+ paths.push(await waitHeap(coordinationWorker, coordPath));
151
+ for (let i = 0; i < cpuWorkers.length; i++) {
152
+ const p = `${dir}/cpubound-${i}-${stamp}.heapsnapshot`;
153
+ paths.push(await waitHeap(cpuWorkers[i], p));
154
+ }
155
+ return paths;
74
156
  },
75
157
  };
76
158
  }
@@ -27,6 +27,10 @@ max_blocks_buffer_size = MAX_BLOCKS_BUFFER_SIZE, anchor_range, stopSync) {
27
27
  };
28
28
  yield connection_status.last_packet;
29
29
  while (true) {
30
+ if (stopSync?.aborted) {
31
+ log("blocksBufferFetchLoop", ["aborted, exiting fetch loop"]);
32
+ return;
33
+ }
30
34
  // avoid overwriting last_packet in case of catastrophic reorg
31
35
  // this generator will block on this. coordinator should rethrow the CatastrophicReorgError
32
36
  if (connection_status.last_packet.status === "catastrophic_reorg") {
@@ -39,9 +43,17 @@ max_blocks_buffer_size = MAX_BLOCKS_BUFFER_SIZE, anchor_range, stopSync) {
39
43
  bytes_read: 0,
40
44
  node_url: node_url,
41
45
  timestamp: new Date().toISOString(),
46
+ daemon_height: connection_status.sync.daemon_height ||
47
+ connection_status.last_packet.daemon_height,
42
48
  };
43
49
  yield connection_status.last_packet;
44
- await sleep(5000);
50
+ try {
51
+ await sleep(1000, stopSync);
52
+ }
53
+ catch {
54
+ log("blocksBufferFetchLoop", ["aborted during buffer-full sleep"]);
55
+ return;
56
+ }
45
57
  continue;
46
58
  }
47
59
  log("blocksBufferFetchLoop", [
@@ -56,11 +68,18 @@ max_blocks_buffer_size = MAX_BLOCKS_BUFFER_SIZE, anchor_range, stopSync) {
56
68
  get_blocks_bin = await doRPCrequest(nodeUrl, current_range, stopSync);
57
69
  }
58
70
  catch (error) {
71
+ // abort is intentional shutdown, not a connection failure
72
+ if (stopSync?.aborted || isAbortError(error)) {
73
+ log("blocksBufferFetchLoop", ["aborted during rpc request"]);
74
+ return;
75
+ }
59
76
  connection_status.last_packet = {
60
77
  status: "connection_failed",
61
78
  bytes_read: 0,
62
79
  node_url,
63
80
  timestamp: new Date().toISOString(),
81
+ daemon_height: connection_status.sync.daemon_height ||
82
+ connection_status.last_packet.daemon_height,
64
83
  };
65
84
  yield connection_status.last_packet;
66
85
  throw error;
@@ -78,7 +97,13 @@ max_blocks_buffer_size = MAX_BLOCKS_BUFFER_SIZE, anchor_range, stopSync) {
78
97
  yield connection_status.last_packet;
79
98
  // no new blocks: at tip, sleep and retry
80
99
  if (!result_meta.block_infos.length) {
81
- await sleep(1000);
100
+ try {
101
+ await sleep(1000, stopSync);
102
+ }
103
+ catch {
104
+ log("blocksBufferFetchLoop", ["aborted during tip sleep"]);
105
+ return;
106
+ }
82
107
  continue;
83
108
  }
84
109
  const bufferItem = makeBlocksBufferItem(result_meta, get_blocks_bin);
@@ -379,3 +404,9 @@ export async function reduceStartHeightToTip(start_height, node_url) {
379
404
  export class CatastrophicReorgError extends Error {
380
405
  name = "CatastrophicReorgError";
381
406
  }
407
+ function isAbortError(error) {
408
+ if (!error || typeof error !== "object")
409
+ return false;
410
+ const name = error.name;
411
+ return name === "AbortError" || name === "DOMException";
412
+ }
@@ -1,4 +1,4 @@
1
- import { blocksBufferFetchLoop, readWriteConnectionStatusFile, CatastrophicReorgError, readOrInitConnectionStatus, } from "../../api";
1
+ import { blocksBufferFetchLoop, readWriteConnectionStatusFile, applyLastPacket, CatastrophicReorgError, readOrInitConnectionStatus, } from "../../api";
2
2
  import { log } from "../../io/logging";
3
3
  export async function setupBlocksBufferGenerator(params) {
4
4
  const blocksBuffer = [];
@@ -11,7 +11,7 @@ export async function handleConnectionStatusChanges(event, scanSettingsPath) {
11
11
  return;
12
12
  if ("status" in event) {
13
13
  await readWriteConnectionStatusFile((cs2) => {
14
- cs2.last_packet = event;
14
+ applyLastPacket(cs2, event);
15
15
  }, scanSettingsPath);
16
16
  if (event.status === "catastrophic_reorg") {
17
17
  log("handleConnectionStatusChanges", "catastrophic reorg, stopping");
@@ -19,6 +19,14 @@ export type ConnectionStatus = {
19
19
  last_packet: ConnectionSatusLastPacket;
20
20
  sync: ConnectionStatusSync;
21
21
  };
22
+ export declare function applyWalletScanProgress(cs: ConnectionStatus, progress: {
23
+ current_scan_height: number;
24
+ scanned_ranges?: CacheRange[];
25
+ daemon_height?: number;
26
+ eta?: string;
27
+ }): void;
28
+ export declare function applyLastPacket(cs: ConnectionStatus, packet: ConnectionSatusLastPacket): void;
29
+ export declare function isConnectedFromStatus(cs: ConnectionStatus | null | undefined): boolean;
22
30
  export declare const DEFAULT_CONNECTION_STATUS_PREFIX = "ConnectionStatus-";
23
31
  export declare function msToHHMM(ms: number): string;
24
32
  export declare function emptyConnectionStatus(overrides?: Partial<ConnectionStatus>): ConnectionStatus;
@@ -1,5 +1,55 @@
1
1
  import { atomicWrite, } from "../api";
2
2
  import { SCAN_SETTINGS_STORE_NAME_DEFAULT } from "../api";
3
+ // wallet progress write: always set scan fields; only replace eta when a new one is provided
4
+ // so a missing eta does not wipe / flicker the previous value.
5
+ export function applyWalletScanProgress(cs, progress) {
6
+ cs.sync.current_scan_height = progress.current_scan_height;
7
+ if (progress.scanned_ranges) {
8
+ cs.sync.scanned_ranges = progress.scanned_ranges;
9
+ }
10
+ if (typeof progress.daemon_height === "number") {
11
+ cs.sync.daemon_height = progress.daemon_height;
12
+ }
13
+ if (progress.eta !== undefined) {
14
+ cs.sync.eta = progress.eta;
15
+ }
16
+ const now = new Date().toISOString();
17
+ cs.sync.timestamp = now;
18
+ // finished batch is a heartbeat so buffer-full catch-up does not look dead
19
+ if (cs.last_packet) {
20
+ cs.last_packet.timestamp = now;
21
+ }
22
+ }
23
+ // idle tip: ok packet has daemon height but no work item wrote sync yet
24
+ export function applyLastPacket(cs, packet) {
25
+ cs.last_packet = packet;
26
+ if (packet.status === "OK" && typeof packet.daemon_height === "number") {
27
+ cs.sync.daemon_height = packet.daemon_height;
28
+ if (!cs.sync.current_scan_height) {
29
+ cs.sync.current_scan_height = packet.daemon_height;
30
+ }
31
+ }
32
+ }
33
+ export function isConnectedFromStatus(cs) {
34
+ if (!cs?.last_packet)
35
+ return false;
36
+ const { status, timestamp } = cs.last_packet;
37
+ // fetch paused on purpose while cpu drains the buffer. not a drop.
38
+ if (status === "blocks_buffer_full")
39
+ return true;
40
+ if (status !== "OK")
41
+ return false;
42
+ // catching up: slow get_blocks_bin is not a drop. 10s rule is for tip idle only.
43
+ const behind = cs.sync.daemon_height > 0 &&
44
+ cs.sync.current_scan_height > 0 &&
45
+ cs.sync.daemon_height - cs.sync.current_scan_height > 100;
46
+ if (behind)
47
+ return true;
48
+ if (!timestamp)
49
+ return false;
50
+ const age = Date.now() - new Date(timestamp).getTime();
51
+ return age >= 0 && age <= 10_000;
52
+ }
3
53
  export const DEFAULT_CONNECTION_STATUS_PREFIX = "ConnectionStatus-";
4
54
  export function msToHHMM(ms) {
5
55
  const totalSeconds = Math.floor(ms / 1000);
@@ -1,9 +1,10 @@
1
- import { type ConnectionStatus } from "../api";
1
+ import { type ConnectionStatus } from "./connectionStatus";
2
2
  export declare class ConnectionStatusOpened {
3
3
  private _onChange?;
4
4
  private _timer;
5
5
  private _cached;
6
6
  private _prevConnected;
7
+ private _prevSyncKey;
7
8
  private _path;
8
9
  constructor(scan_settings_path: string, _onChange?: ((status: ConnectionStatus | null) => void) | null | undefined);
9
10
  watch(intervalMs?: number): Promise<void>;
@@ -11,5 +12,6 @@ export declare class ConnectionStatusOpened {
11
12
  get connectionStatus(): ConnectionStatus | null;
12
13
  get isConnected(): boolean;
13
14
  get daemonHeight(): number | undefined;
15
+ private _syncKey;
14
16
  private _poll;
15
17
  }
@@ -1,9 +1,11 @@
1
- import { readConnectionStatusFile, connectionStatusFilePath, } from "../api";
1
+ import { readConnectionStatusFile, connectionStatusFilePath, isConnectedFromStatus, } from "./connectionStatus";
2
2
  export class ConnectionStatusOpened {
3
3
  _onChange;
4
4
  _timer;
5
5
  _cached = null;
6
6
  _prevConnected = false;
7
+ // notify when sync progress fields change, not only connect flip
8
+ _prevSyncKey = "";
7
9
  _path;
8
10
  constructor(scan_settings_path, _onChange) {
9
11
  this._onChange = _onChange;
@@ -25,26 +27,32 @@ export class ConnectionStatusOpened {
25
27
  return this._cached;
26
28
  }
27
29
  get isConnected() {
28
- const cs = this._cached;
29
- if (!cs?.last_packet)
30
- return false;
31
- const { status, timestamp } = cs.last_packet;
32
- if (status !== "OK" && status !== "blocks_buffer_full")
33
- return false;
34
- if (!timestamp)
35
- return false;
36
- const age = Date.now() - new Date(timestamp).getTime();
37
- return age >= 0 && age <= 10_000;
30
+ return isConnectedFromStatus(this._cached);
38
31
  }
39
32
  get daemonHeight() {
40
- return this._cached?.last_packet?.daemon_height;
33
+ return this._cached?.sync?.daemon_height;
34
+ }
35
+ _syncKey(cs) {
36
+ const s = cs?.sync;
37
+ if (!s)
38
+ return "";
39
+ return [
40
+ s.daemon_height ?? "",
41
+ s.current_scan_height ?? "",
42
+ s.eta ?? "",
43
+ s.timestamp ?? "",
44
+ ].join("|");
41
45
  }
42
46
  async _poll() {
43
47
  this._cached =
44
48
  (await readConnectionStatusFile(this._path).catch(() => null)) || null;
45
49
  const now = this.isConnected;
46
- if (now !== this._prevConnected) {
47
- this._prevConnected = now;
50
+ const syncKey = this._syncKey(this._cached);
51
+ const connectedChanged = now !== this._prevConnected;
52
+ const syncChanged = syncKey !== this._prevSyncKey;
53
+ this._prevConnected = now;
54
+ this._prevSyncKey = syncKey;
55
+ if (connectedChanged || syncChanged) {
48
56
  this._onChange?.(this._cached);
49
57
  }
50
58
  }
@@ -7,9 +7,12 @@ import { ManyScanCachesOpened, ScanCacheOpened, type ManyScanCachesOpenedCreateO
7
7
  * @param options.no_worker to feed the ManyScanCachesOpened manually with .feed(params) from CacheChangedCallbackParams
8
8
  * @param options.no_stats to disable creation of stats file with aggregated information e.g. amount per wallet / subaddress
9
9
  * @param options.notifyMasterChanged pass the output of this to another (no_worker) instance to feed
10
+ * @param options.logs "console" | "file" | "console-and-file" | "off"
11
+ * @param options.logs_include function names to include (e.g. handleCpuboundScan, blocksBufferFetchLoop)
12
+ * @param options.logs_exclude function names to exclude
10
13
  * @returns Promise<ManyScanCachesOpened>
11
14
  */
12
- export declare function openWallets(options?: ManyScanCachesOpenedCreateOptions): Promise<ManyScanCachesOpened | undefined>;
15
+ export declare function openWallets(options?: ManyScanCachesOpenedCreateOptions): Promise<ManyScanCachesOpened>;
13
16
  /**
14
17
  * Opens a **single wallet** for scanning.
15
18
  * **Touches ScanSettings.json**,
@@ -8,6 +8,9 @@ import { openScanSettingsFile, writeWalletToScanSettings } from "../api";
8
8
  * @param options.no_worker to feed the ManyScanCachesOpened manually with .feed(params) from CacheChangedCallbackParams
9
9
  * @param options.no_stats to disable creation of stats file with aggregated information e.g. amount per wallet / subaddress
10
10
  * @param options.notifyMasterChanged pass the output of this to another (no_worker) instance to feed
11
+ * @param options.logs "console" | "file" | "console-and-file" | "off"
12
+ * @param options.logs_include function names to include (e.g. handleCpuboundScan, blocksBufferFetchLoop)
13
+ * @param options.logs_exclude function names to exclude
11
14
  * @returns Promise<ManyScanCachesOpened>
12
15
  */
13
16
  export async function openWallets(options) {
@@ -20,6 +20,7 @@ export declare function lastRangeThrows(ranges: CacheRange[]): CacheRange;
20
20
  export declare function mergeRanges(ranges: CacheRange[]): CacheRange[];
21
21
  export declare const findRange: (ranges: CacheRange[], value: number) => CacheRange | null;
22
22
  export declare function findRangeThrows(ranges: CacheRange[], value: number): CacheRange;
23
+ export declare function currentScanHeightFromRanges(ranges: CacheRange[], start_height: number): number;
23
24
  export type CacheRange = {
24
25
  start: number;
25
26
  end: number;
@@ -67,6 +68,9 @@ export type TxLog = {
67
68
  feeEstimate?: FeeEstimateResponse;
68
69
  sendResult?: SendRawTransactionResult;
69
70
  error?: string;
71
+ invocationId?: string;
72
+ signed_tx?: string;
73
+ hidden_on_send_plate?: boolean;
70
74
  };
71
75
  export type ChangeReason = "spent" | "added" | "ownspend" | "reorged" | "reorged_spent" | "burned";
72
76
  export type ChangedOutput = {
@@ -95,4 +99,4 @@ export interface HasPrimaryAddress {
95
99
  primary_address: string;
96
100
  }
97
101
  export declare function handleScanError(error: unknown): void;
98
- export declare function isConnectionError(error: unknown): true | undefined;
102
+ export declare function isConnectionError(error: unknown): boolean;
@@ -158,6 +158,10 @@ export function findRangeThrows(ranges, value) {
158
158
  throw new Error(`range not found for value: ${value}`);
159
159
  return range;
160
160
  }
161
+ // end of the range that contains start_height. miss means we scan from start
162
+ export function currentScanHeightFromRanges(ranges, start_height) {
163
+ return findRange(ranges, start_height)?.end ?? start_height ?? 0;
164
+ }
161
165
  export function handleScanError(error) {
162
166
  // treat errno 0 code "ConnectionRefused" as non fatal outcome, and rethrow,
163
167
  // so that UI can be informed after catching it higher up
@@ -185,10 +189,15 @@ export function isConnectionError(error) {
185
189
  if (error &&
186
190
  typeof error === "object" &&
187
191
  (("code" in error && error.code === "ConnectionRefused") ||
188
- ("errno" in error && error.errno === 0))) {
192
+ ("errno" in error && error.errno === 0) ||
193
+ ("message" in error &&
194
+ typeof error.message === "string" &&
195
+ (error.message.includes("Failed to fetch") ||
196
+ error.message.includes("ERR_INTERNET_DISCONNECTED") ||
197
+ error.message.includes("NetworkError"))))) {
189
198
  return true;
190
199
  }
191
200
  else {
192
- false;
201
+ return false;
193
202
  }
194
203
  }
@@ -1,6 +1,7 @@
1
1
  import { ViewPair, type FeeEstimateResponse, type Output, type SendRawTransactionResult } from "../../api";
2
2
  import { type Payment } from "../../send-functionality/inputSelection";
3
3
  import { ConnectionStatusOpened } from "../connectionStatusOpened";
4
+ import { ActionLogOpened, type ActionLogBackendKind, type ExtensionMessageBus } from "../../actionlog/actionLogOpened";
4
5
  import type { LogSetting, PossibleLogs } from "../../io/logging";
5
6
  import { type CacheChangedCallback, type CacheChangedCallbackParameters, type ScanCache, type Subaddress, type TxLog } from "./scanCache";
6
7
  import { type FoundTransaction, type PrePendingTx } from "./scanStats";
@@ -19,6 +20,7 @@ export type CreateTransactionParams = {
19
20
  payments: Payment[];
20
21
  inputs?: Output[];
21
22
  no_fee_circuit_breaker?: boolean;
23
+ invocationId?: string;
22
24
  };
23
25
  export declare class ScanCacheOpened {
24
26
  private _scanSettings;
@@ -101,6 +103,23 @@ export declare class ScanCacheOpened {
101
103
  get subaddresses(): Subaddress[];
102
104
  get tx_logs(): TxLog[];
103
105
  makeSignSendTransaction(params: CreateTransactionParams): Promise<SendRawTransactionResult>;
106
+ private outputsForIndexes;
107
+ makeSignSend(params: {
108
+ payments: Payment[];
109
+ input_indexes: string[];
110
+ }): Promise<SendRawTransactionResult>;
111
+ sweepSignSend(params: {
112
+ address: string;
113
+ input_indexes: string[];
114
+ }): Promise<SendRawTransactionResult>;
115
+ rebroadcastTx(txLogIndex: number): Promise<SendRawTransactionResult>;
116
+ get failed_txs(): {
117
+ index: number;
118
+ txlog: TxLog;
119
+ inputs: Output[];
120
+ }[];
121
+ private recordSend;
122
+ dismissSendPlate(): Promise<void>;
104
123
  /**
105
124
  * makeStandardTransaction
106
125
  */
@@ -129,7 +148,8 @@ export declare class ScanCacheOpened {
129
148
  remove: () => null;
130
149
  };
131
150
  pause(): Promise<void>;
132
- stopWorker(): void;
151
+ dumpWorkerHeaps(dir?: string): Promise<string[]>;
152
+ stopWorker(): Promise<void>;
133
153
  unpause(): Promise<void>;
134
154
  private _onWorkerError;
135
155
  /**
@@ -178,18 +198,48 @@ export type ManyScanCachesOpenedCreateOptions = {
178
198
  retryDelayMs?: number;
179
199
  connectionStatusIntervalMs?: number;
180
200
  onConnectionStatusChange?: ((status: ConnectionStatus | null) => void) | null;
201
+ logs?: LogSetting;
202
+ logs_include?: PossibleLogs[];
203
+ logs_exclude?: PossibleLogs[];
204
+ actionLogPath?: string;
205
+ actionLogBackend?: ActionLogBackendKind;
206
+ onActionLogChange?: (() => void) | null;
207
+ extensionMessageBus?: ExtensionMessageBus;
181
208
  };
182
209
  export declare class ManyScanCachesOpened {
183
210
  readonly connectionStatusOpened: ConnectionStatusOpened;
211
+ readonly actionLogOpened: ActionLogOpened;
184
212
  private _scanSettings;
185
213
  private _options;
186
214
  get start_height(): number | null;
187
215
  get current_height(): number | null;
188
216
  get node_url(): string;
189
217
  changeNodeUrlAndStartHeight(node_url?: string, start_height?: number | null): Promise<void>;
190
- retry(): Promise<boolean>;
191
- stopWorker(): void;
218
+ retry(): Promise<boolean | undefined>;
219
+ stopWorker(): Promise<void>;
220
+ dumpWorkerHeaps(dir?: string): Promise<string[]>;
192
221
  changeNodeUrl(node_url: string): Promise<void>;
222
+ private walletByAddress;
223
+ makeSignSend(params: {
224
+ primary_address: string;
225
+ payments: {
226
+ address: string;
227
+ amount: string;
228
+ }[];
229
+ input_indexes: string[];
230
+ }): Promise<SendRawTransactionResult | undefined>;
231
+ sweepSignSend(params: {
232
+ primary_address: string;
233
+ address: string;
234
+ input_indexes: string[];
235
+ }): Promise<SendRawTransactionResult | undefined>;
236
+ rebroadcastTx(params: {
237
+ primary_address: string;
238
+ tx_log_index: number;
239
+ }): Promise<SendRawTransactionResult | undefined>;
240
+ dismissSendPlate(params: {
241
+ primary_address: string;
242
+ }): Promise<void>;
193
243
  get merchant_confirmations(): number | null | undefined;
194
244
  get cpu_worker_count(): number | undefined;
195
245
  get logs(): LogSetting | undefined;
@@ -201,13 +251,14 @@ export declare class ManyScanCachesOpened {
201
251
  unwatchConnectionStatus(): void;
202
252
  setMerchantConfirmations(merchant_confirmations: number | null): Promise<void>;
203
253
  setCpuWorkerCount(cpu_worker_count: number | undefined): Promise<void>;
204
- setLogSettings(logs?: LogSetting | null, logs_include?: PossibleLogs[] | null, logs_exclude?: PossibleLogs[] | null): Promise<void>;
254
+ setLogSettings(logs?: LogSetting | null, logs_include?: string[] | null, logs_exclude?: string[] | null): Promise<void>;
205
255
  setWalletName(primary_address: string, name?: string): Promise<void>;
206
256
  setWalletSlot(primary_address: string, slot?: number): Promise<void>;
207
257
  changeStartHeight(start_height: number | null): Promise<void>;
208
258
  private static _buildWallets;
209
- static create(options: ManyScanCachesOpenedCreateOptions): Promise<ManyScanCachesOpened | undefined>;
259
+ static create(options: ManyScanCachesOpenedCreateOptions): Promise<ManyScanCachesOpened>;
210
260
  buildWallets(): Promise<void>;
261
+ private reloadWalletsAfterStop;
211
262
  addViewWallet(primary_address: string, view_key: string, fields?: {
212
263
  wallet_name?: string;
213
264
  wallet_slot?: number;