@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.
- package/dist/actionlog/actionLogOpened.d.ts +96 -1
- package/dist/actionlog/actionLogOpened.js +386 -1
- package/dist/actionlog/actionLogTypes.d.ts +114 -0
- package/dist/actionlog/actionLogTypes.js +198 -0
- package/dist/actionlog/backend/idbBackend.d.ts +13 -0
- package/dist/actionlog/backend/idbBackend.js +109 -0
- package/dist/actionlog/backend/jsonlBackend.d.ts +19 -0
- package/dist/actionlog/backend/jsonlBackend.js +110 -0
- package/dist/actionlog/backend/memoryBackend.d.ts +11 -0
- package/dist/actionlog/backend/memoryBackend.js +31 -0
- package/dist/actionlog/backend/sqliteBackend.d.ts +14 -0
- package/dist/actionlog/backend/sqliteBackend.js +96 -0
- package/dist/actionlog/backend.d.ts +12 -0
- package/dist/actionlog/backend.js +1 -0
- package/dist/actionlog/idbBackend.d.ts +19 -0
- package/dist/actionlog/idbBackend.js +98 -0
- package/dist/actionlog/jsonlBackend.d.ts +19 -0
- package/dist/actionlog/jsonlBackend.js +75 -0
- package/dist/actionlog/memoryBackend.d.ts +14 -0
- package/dist/actionlog/memoryBackend.js +38 -0
- package/dist/actionlog/openMaps.d.ts +11 -0
- package/dist/actionlog/openMaps.js +32 -0
- package/dist/actionlog/sqliteBackend.d.ts +18 -0
- package/dist/actionlog/sqliteBackend.js +105 -0
- package/dist/actionlog/toolContext.d.ts +36 -0
- package/dist/actionlog/toolContext.js +1 -0
- package/dist/actionlog/types.d.ts +62 -0
- package/dist/actionlog/types.js +62 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.js +3 -0
- package/dist/io/sleep.d.ts +1 -1
- package/dist/io/sleep.js +16 -1
- package/dist/scanning-syncing/backgroundWorker.d.ts +2 -0
- package/dist/scanning-syncing/backgroundWorker.js +92 -10
- package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +33 -2
- package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +2 -2
- package/dist/scanning-syncing/connectionStatus.d.ts +8 -0
- package/dist/scanning-syncing/connectionStatus.js +50 -0
- package/dist/scanning-syncing/connectionStatusOpened.d.ts +3 -1
- package/dist/scanning-syncing/connectionStatusOpened.js +22 -14
- package/dist/scanning-syncing/openWallet.d.ts +4 -1
- package/dist/scanning-syncing/openWallet.js +3 -0
- package/dist/scanning-syncing/scanresult/scanCache.d.ts +5 -1
- package/dist/scanning-syncing/scanresult/scanCache.js +11 -2
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +56 -5
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +422 -83
- package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +7 -3
- package/dist/scanning-syncing/scanresult/scanCoordination.js +126 -28
- package/dist/scanning-syncing/scanresult/scanLoop.d.ts +1 -2
- package/dist/scanning-syncing/scanresult/scanLoop.js +0 -13
- package/dist/scanning-syncing/scanresult/scanResult.js +17 -12
- package/dist/scanning-syncing/scanresult/scanStats.js +8 -5
- package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
- package/dist/scanning-syncing/worker-mains/coordinator-main.d.ts +1 -0
- package/dist/scanning-syncing/worker-mains/coordinator-main.js +16 -1
- package/dist/scanning-syncing/worker-mains/cpubound-main.js +3 -2
- package/dist/scanning-syncing/worker-mains/worker.js +45 -2
- package/dist/tools/calls/001.d.ts +20 -0
- package/dist/tools/calls/001.js +236 -0
- package/dist/tools/calls/002-content.d.ts +46 -0
- package/dist/tools/calls/002-content.js +135 -0
- package/dist/tools/calls/002-fail-cleanup.d.ts +27 -0
- package/dist/tools/calls/002-fail-cleanup.js +60 -0
- package/dist/tools/calls/002.d.ts +64 -0
- package/dist/tools/calls/002.js +353 -0
- package/dist/tools/content.d.ts +22 -0
- package/dist/tools/content.js +41 -0
- package/dist/tools/counterparty.d.ts +3 -0
- package/dist/tools/counterparty.js +1 -0
- package/dist/tools/download-public-suffix.d.ts +1 -0
- package/dist/tools/download-public-suffix.js +20 -0
- package/dist/tools/globals.d.ts +196 -0
- package/dist/tools/globals.js +14 -0
- package/dist/tools/monero-tools-content.d.ts +29 -0
- package/dist/tools/monero-tools-content.js +80 -0
- package/dist/tools/monero-tools.d.ts +27 -52
- package/dist/tools/monero-tools.js +127 -222
- package/dist/tools/public-suffix-list.d.ts +1 -0
- package/dist/tools/public-suffix-list.js +10336 -0
- package/dist/tools/runtimeMessages.d.ts +4 -0
- package/dist/tools/runtimeMessages.js +18 -0
- package/dist/tools/toolActionLogContext.d.ts +33 -0
- package/dist/tools/toolActionLogContext.js +1 -0
- package/dist/tools/toolArm.d.ts +47 -0
- package/dist/tools/toolArm.js +1 -0
- package/dist/tools/ui.d.ts +2 -0
- package/dist/tools/ui.js +1 -0
- package/dist/tools/worker.d.ts +1 -0
- package/dist/tools/worker.js +1 -0
- package/dist/wasm-processing/wasmFile.js +2 -2
- 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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 "
|
|
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 "
|
|
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
|
-
|
|
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?.
|
|
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
|
-
|
|
47
|
-
|
|
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
|
|
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):
|
|
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
|
-
|
|
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?:
|
|
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
|
|
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;
|