@spirobel/monero-wallet-api 0.5.3 → 0.5.5
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/README.md +6 -0
- package/dist/api.d.ts +1 -1
- package/dist/api.js +1 -1
- package/dist/io/logging.d.ts +6 -6
- package/dist/io/logging.js +4 -3
- package/dist/io/sleep.d.ts +1 -1
- package/dist/io/sleep.js +16 -1
- package/dist/multisig/escrow.js +0 -1
- package/dist/scanning-syncing/backgroundWorker.d.ts +2 -1
- package/dist/scanning-syncing/backgroundWorker.js +104 -34
- package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +33 -2
- package/dist/scanning-syncing/connectionStatus.d.ts +6 -0
- package/dist/scanning-syncing/connectionStatus.js +15 -0
- package/dist/scanning-syncing/connectionStatusOpened.d.ts +3 -1
- package/dist/scanning-syncing/connectionStatusOpened.js +22 -5
- package/dist/scanning-syncing/openWallet.d.ts +4 -1
- package/dist/scanning-syncing/openWallet.js +3 -0
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +12 -4
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +188 -107
- package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +6 -25
- package/dist/scanning-syncing/scanresult/scanCoordination.js +89 -209
- package/dist/scanning-syncing/scanresult/scanLoop.d.ts +1 -2
- package/dist/scanning-syncing/scanresult/scanLoop.js +0 -13
- 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 -2
- package/dist/scanning-syncing/worker-mains/cpubound-main.d.ts +5 -2
- package/dist/scanning-syncing/worker-mains/cpubound-main.js +10 -8
- package/dist/scanning-syncing/worker-mains/worker.js +50 -2
- package/dist/scansettings/scanSettingsOpened.js +3 -6
- package/dist/tools/monero-tools.js +1 -1
- package/dist/viewpair/ViewPair.js +9 -4
- package/dist/wasm-processing/wasmFile.js +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { coordinatorMainMultithreaded } from "../scanresult/scanCoordination";
|
|
2
2
|
import { log } from "../../io/logging";
|
|
3
|
+
// one controller per coordinator worker; shutdown aborts fetch + race
|
|
4
|
+
let shutdownController;
|
|
5
|
+
export function requestCoordinatorShutdown() {
|
|
6
|
+
log("coordinatorMainWorker", ["shutdown requested"]);
|
|
7
|
+
shutdownController?.abort();
|
|
8
|
+
}
|
|
3
9
|
export async function coordinatorMainWorker(scan_settings_path, pathPrefix, cpuPorts) {
|
|
4
10
|
log("coordinatorMainWorker", "cpuPorts=" + cpuPorts?.length);
|
|
11
|
+
shutdownController = new AbortController();
|
|
5
12
|
try {
|
|
6
|
-
const gen = coordinatorMainMultithreaded(scan_settings_path, pathPrefix, cpuPorts);
|
|
13
|
+
const gen = coordinatorMainMultithreaded(scan_settings_path, pathPrefix, cpuPorts, shutdownController.signal);
|
|
7
14
|
for await (const event of gen) {
|
|
8
15
|
log("coordinatorMainWorker", ["event:", event]);
|
|
9
16
|
if (event.type === "scan_ready") {
|
|
@@ -15,10 +22,17 @@ export async function coordinatorMainWorker(scan_settings_path, pathPrefix, cpuP
|
|
|
15
22
|
},
|
|
16
23
|
});
|
|
17
24
|
}
|
|
25
|
+
else if (event.type === "shutdown_done") {
|
|
26
|
+
self.postMessage({ type: "SHUTDOWN_DONE" });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
18
29
|
}
|
|
30
|
+
// generator returned without shutdown_done (e.g. empty)
|
|
31
|
+
self.postMessage({ type: "SHUTDOWN_DONE" });
|
|
19
32
|
}
|
|
20
33
|
catch (error) {
|
|
21
|
-
|
|
34
|
+
// still tell main thread we stopped so terminate can proceed
|
|
35
|
+
self.postMessage({ type: "SHUTDOWN_DONE" });
|
|
22
36
|
self.postMessage({ type: "ERROR", payload: error });
|
|
23
37
|
}
|
|
24
38
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type ScanLoopInput, type ScanLoopYield } from "../scanresult/scanLoop";
|
|
2
|
-
export declare function handleCpuboundScan(msg: ScanLoopInput,
|
|
3
|
-
export declare function handleCpuboundScanTry(msg: ScanLoopInput,
|
|
2
|
+
export declare function handleCpuboundScan(msg: ScanLoopInput, cpu_worker_status: CpuWorkerStatus): Promise<ScanLoopYield>;
|
|
3
|
+
export declare function handleCpuboundScanTry(msg: ScanLoopInput, cpu_worker_status: CpuWorkerStatus): Promise<ScanLoopYield>;
|
|
4
|
+
export type CpuWorkerStatus = {
|
|
5
|
+
cancel: boolean;
|
|
6
|
+
};
|
|
4
7
|
export declare function sendFromCpuWorker(port: MessagePort, msg: ScanLoopYield): void;
|
|
5
8
|
export declare function sendToCpuWorker(port: MessagePort, msg: ScanLoopInput): void;
|
|
@@ -6,7 +6,7 @@ import {} from "../scanresult/scanLoop";
|
|
|
6
6
|
* logs progress every 10 blocks, then sends the final result.
|
|
7
7
|
*/
|
|
8
8
|
let callcounter = 0;
|
|
9
|
-
export async function handleCpuboundScan(msg,
|
|
9
|
+
export async function handleCpuboundScan(msg, cpu_worker_status) {
|
|
10
10
|
callcounter++;
|
|
11
11
|
log("handleCpuboundScan", "callcounter=" + callcounter);
|
|
12
12
|
if (!msg) {
|
|
@@ -14,17 +14,14 @@ export async function handleCpuboundScan(msg, port) {
|
|
|
14
14
|
throw new Error("[cpubound] no content msg! ( ScanLoopInput = undefined )");
|
|
15
15
|
}
|
|
16
16
|
if (msg === "cancel") {
|
|
17
|
+
// idle cancel: nothing in flight, just ack
|
|
17
18
|
log("handleCpuboundScan", "cancel msg! ( ScanLoopInput = cancel )");
|
|
18
|
-
|
|
19
|
+
return { type: "Canceled" };
|
|
19
20
|
}
|
|
20
21
|
log("handleCpuboundScan", "got scan msg, walletConfig=" +
|
|
21
22
|
msg.walletConfig?.primary_address?.slice(0, 6));
|
|
22
23
|
const walletConfig = msg.walletConfig;
|
|
23
24
|
const item = msg;
|
|
24
|
-
if (!port) {
|
|
25
|
-
log("handleCpuboundScan", "no port!");
|
|
26
|
-
throw new Error("[cpubound] no port");
|
|
27
|
-
}
|
|
28
25
|
log("handleCpuboundScan", "primed, feeding workItem uuid=" +
|
|
29
26
|
(item && typeof item === "object"
|
|
30
27
|
? item.work_uuid?.slice(0, 8)
|
|
@@ -90,6 +87,11 @@ export async function handleCpuboundScan(msg, port) {
|
|
|
90
87
|
heightto,
|
|
91
88
|
]);
|
|
92
89
|
}
|
|
90
|
+
if (cpu_worker_status.cancel) {
|
|
91
|
+
cpu_worker_status.cancel = false;
|
|
92
|
+
log("handleCpuboundScan", ["cpu work canceled"]);
|
|
93
|
+
return { type: "Canceled", work_uuid: item.work_uuid };
|
|
94
|
+
}
|
|
93
95
|
await sleep(10); // make sure the loop is not tight
|
|
94
96
|
}
|
|
95
97
|
log("handleCpuboundScan", [
|
|
@@ -105,9 +107,9 @@ export async function handleCpuboundScan(msg, port) {
|
|
|
105
107
|
result: scanResult,
|
|
106
108
|
};
|
|
107
109
|
}
|
|
108
|
-
export function handleCpuboundScanTry(msg,
|
|
110
|
+
export function handleCpuboundScanTry(msg, cpu_worker_status) {
|
|
109
111
|
try {
|
|
110
|
-
return handleCpuboundScan(msg,
|
|
112
|
+
return handleCpuboundScan(msg, cpu_worker_status);
|
|
111
113
|
}
|
|
112
114
|
catch (err) {
|
|
113
115
|
console.error("[cpubound] error", err);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { coordinatorMainWorker } from "./coordinator-main";
|
|
1
|
+
import { coordinatorMainWorker, requestCoordinatorShutdown, } from "./coordinator-main";
|
|
2
2
|
import { handleCpuboundScanTry, sendFromCpuWorker } from "./cpubound-main";
|
|
3
3
|
import { log, setupLoggingPath } from "../../io/logging";
|
|
4
4
|
import { multisigMainWorkerCall } from "./multisig-main";
|
|
@@ -8,17 +8,33 @@ self.addEventListener("unhandledrejection", (e) => self.postMessage({ type: "ERR
|
|
|
8
8
|
let SCAN_SETTINGS_PATH;
|
|
9
9
|
let PATH_PREFIX;
|
|
10
10
|
let cpuPort;
|
|
11
|
+
const cpu_worker_status = { cancel: false };
|
|
12
|
+
// true while a scan is running so idle cancel can ack immediately
|
|
13
|
+
let cpu_work_in_flight = false;
|
|
11
14
|
let multisig_dkg;
|
|
12
15
|
export function CPU_PORT_HANDLER(pe) {
|
|
16
|
+
if (pe.data === "cancel") {
|
|
17
|
+
if (cpu_work_in_flight) {
|
|
18
|
+
cpu_worker_status.cancel = true;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// idle cancel: ack right away so coordinator drain does not hang
|
|
22
|
+
if (cpuPort) {
|
|
23
|
+
sendFromCpuWorker(cpuPort, { type: "Canceled" });
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
13
27
|
if (!cpuPort)
|
|
14
28
|
throw new Error("[cpubound] cpuPort is undefined in port.onmessage");
|
|
15
29
|
log("CPU_PORT_HANDLER", "new workitem msg received");
|
|
30
|
+
cpu_work_in_flight = true;
|
|
16
31
|
cpuPort.postMessage({
|
|
17
32
|
type: "WORKSTART",
|
|
18
33
|
work_uuid: pe.data.work_uuid,
|
|
19
34
|
});
|
|
20
|
-
handleCpuboundScanTry(pe.data,
|
|
35
|
+
handleCpuboundScanTry(pe.data, cpu_worker_status).then((result) => {
|
|
21
36
|
log("CPU_PORT_HANDLER", "work finished, sending result");
|
|
37
|
+
cpu_work_in_flight = false;
|
|
22
38
|
if (!cpuPort)
|
|
23
39
|
throw new Error("[cpubound] cpuPort is undefined in port.onmessage");
|
|
24
40
|
cpuPort.onmessage = CPU_PORT_HANDLER;
|
|
@@ -28,6 +44,38 @@ export function CPU_PORT_HANDLER(pe) {
|
|
|
28
44
|
const handleMessage = async (e) => {
|
|
29
45
|
const msg = e.data;
|
|
30
46
|
log("handleMessage", ["msg received", msg]);
|
|
47
|
+
if (msg.type === "shutdown") {
|
|
48
|
+
// only coordinator listens for this; abort fetch + race loop
|
|
49
|
+
requestCoordinatorShutdown();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (msg.type === "heap_snapshot") {
|
|
53
|
+
try {
|
|
54
|
+
const path = typeof msg.path === "string" && msg.path.length > 0
|
|
55
|
+
? msg.path
|
|
56
|
+
: `worker-heap-${Date.now()}.heapsnapshot`;
|
|
57
|
+
// @ts-ignore bun has generateHeapSnapshot; node has v8.writeHeapSnapshot
|
|
58
|
+
let out;
|
|
59
|
+
if (typeof Bun !== "undefined" && typeof Bun.generateHeapSnapshot === "function") {
|
|
60
|
+
const snap = Bun.generateHeapSnapshot("v8");
|
|
61
|
+
await Bun.write(path, typeof snap === "string" ? snap : JSON.stringify(snap));
|
|
62
|
+
out = path;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
throw Error("in the browser use the inspector directly to observe worker memory growth");
|
|
66
|
+
// const v8 = await import("node:v8");
|
|
67
|
+
// out = v8.writeHeapSnapshot(path);
|
|
68
|
+
}
|
|
69
|
+
self.postMessage({ type: "HEAP_SNAPSHOT_DONE", path: out ?? path });
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
self.postMessage({
|
|
73
|
+
type: "HEAP_SNAPSHOT_ERROR",
|
|
74
|
+
error: err instanceof Error ? err.message : String(err),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
31
79
|
if (msg.type === "setup") {
|
|
32
80
|
const settingsPath = msg.scan_settings_path || SCAN_SETTINGS_STORE_NAME_DEFAULT;
|
|
33
81
|
SCAN_SETTINGS_PATH = settingsPath;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LOCAL_NODE_DEFAULT_URL } from "../node-interaction/nodeUrl";
|
|
2
2
|
import { LOGGING_FUNCTIONS, } from "../io/logging";
|
|
3
|
-
import { getPathPrefix, makeSpendKeyFromSeed, makeViewKey, openScanSettingsFile, SCAN_SETTINGS_STORE_NAME_DEFAULT, SUB_ADDRESS_INDEX_DEFAULT_VALUE, walletSettingsPlusKeys, writeEnvLineToDotEnvRefresh, writeScanSettings, writeViewKeyToDotEnv, } from "../api";
|
|
3
|
+
import { getPathPrefix, makeSpendKeyFromSeed, makeViewKey, openScanSettingsFile, parseAddress, SCAN_SETTINGS_STORE_NAME_DEFAULT, SUB_ADDRESS_INDEX_DEFAULT_VALUE, walletSettingsPlusKeys, writeEnvLineToDotEnvRefresh, writeScanSettings, writeViewKeyToDotEnv, } from "../api";
|
|
4
4
|
export class ScanSettingsOpened {
|
|
5
5
|
_scan_settings_path;
|
|
6
6
|
_pathPrefix;
|
|
@@ -309,7 +309,8 @@ export class ScanSettingsOpened {
|
|
|
309
309
|
}
|
|
310
310
|
}
|
|
311
311
|
static _validateAddress(address) {
|
|
312
|
-
|
|
312
|
+
const parsed = parseAddress(address);
|
|
313
|
+
if ("error" in parsed) {
|
|
313
314
|
throw new Error(`invalid monero address: ${address}`);
|
|
314
315
|
}
|
|
315
316
|
}
|
|
@@ -404,10 +405,6 @@ export class ScanSettingsOpened {
|
|
|
404
405
|
await writeScanSettings(this._settings, this._scan_settings_path);
|
|
405
406
|
}
|
|
406
407
|
}
|
|
407
|
-
function isValidMoneroAddress(address) {
|
|
408
|
-
// basic sanity check
|
|
409
|
-
return (address.length >= 95 && address.length <= 106 && isAlphaNumeric(address));
|
|
410
|
-
}
|
|
411
408
|
function isValidMoneroPrivateKey(key) {
|
|
412
409
|
return key.length === 64 && isAlphaNumeric(key);
|
|
413
410
|
}
|
|
@@ -122,7 +122,7 @@ export function parseDestination(destination) {
|
|
|
122
122
|
const url = new URL(destination);
|
|
123
123
|
return getDomainWithTLD(url.hostname);
|
|
124
124
|
}
|
|
125
|
-
export const OPEN_DOMAINS = ["monerochan.
|
|
125
|
+
export const OPEN_DOMAINS = ["monerochan.cash"];
|
|
126
126
|
// this validity check should happen in the contentscript when the link is clicked,
|
|
127
127
|
// not in the background script
|
|
128
128
|
// -> tor circuit is separated & compartmentalized
|
|
@@ -46,18 +46,23 @@ export class ViewPair extends WasmProcessor {
|
|
|
46
46
|
};
|
|
47
47
|
};
|
|
48
48
|
let init_viewpair_result = undefined;
|
|
49
|
+
let init_viewpair_error = undefined;
|
|
49
50
|
viewPair.readFromWasmMemory = (ptr, len) => {
|
|
50
51
|
init_viewpair_result = JSON.parse(viewPair.readString(ptr, len));
|
|
51
52
|
};
|
|
53
|
+
viewPair.readErrorFromWasmMemory = (ptr, len) => {
|
|
54
|
+
init_viewpair_error = JSON.parse(viewPair.readString(ptr, len));
|
|
55
|
+
};
|
|
52
56
|
//@ts-ignore
|
|
53
57
|
tinywasi.instance.exports.init_viewpair(primary_address.length, secret_view_key.length, subaddress_index);
|
|
58
|
+
if (init_viewpair_error && "error" in init_viewpair_error) {
|
|
59
|
+
throw new Error(`primary-address-not-valid (${primary_address.slice(0, 8)}...${primary_address.slice(-8)})`);
|
|
60
|
+
}
|
|
54
61
|
if (!init_viewpair_result) {
|
|
55
62
|
throw new Error("Failed to init viewpair");
|
|
56
63
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
viewPair._network = init_viewpair_result.network;
|
|
60
|
-
}
|
|
64
|
+
//@ts-ignore
|
|
65
|
+
viewPair._network = init_viewpair_result.network;
|
|
61
66
|
return viewPair;
|
|
62
67
|
}
|
|
63
68
|
/**
|