@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
|
@@ -5,6 +5,8 @@ import { spendable } from "./scanResult";
|
|
|
5
5
|
import { getPathPrefix, readPrivateSpendKeyFromEnv, SCAN_SETTINGS_STORE_NAME_DEFAULT, SUB_ADDRESS_INDEX_DEFAULT_VALUE, walletSettingsPlusKeys, } from "../../api";
|
|
6
6
|
import { ScanSettingsOpened } from "../../scansettings/scanSettingsOpened";
|
|
7
7
|
import { ConnectionStatusOpened } from "../connectionStatusOpened";
|
|
8
|
+
import { ActionLogOpened, } from "../../actionlog/actionLogOpened";
|
|
9
|
+
import { sendToBackground } from "../../tools/globals";
|
|
8
10
|
import { findRange, lastRange, readCacheFileDefaultLocation, writeCacheFileDefaultLocationThrows, } from "./scanCache";
|
|
9
11
|
import { alignScanStatsWithCache, isSelfSpent, processTxlogInputs, processTxlogPayments, writeStatsFileDefaultLocation, } from "./scanStats";
|
|
10
12
|
import { readWriteConnectionStatusFile, } from "../connectionStatus";
|
|
@@ -37,11 +39,13 @@ export class ScanCacheOpened {
|
|
|
37
39
|
/** sample sizes to try when decoyRetry is enabled, in order */
|
|
38
40
|
decoyRetrySizes = [20, 50, 100, 200, 500];
|
|
39
41
|
static async create(params) {
|
|
40
|
-
|
|
42
|
+
// same dir as workers / connection status: next to ScanSettings.json
|
|
43
|
+
const pathPrefix = getPathPrefix(params.scan_settings_path, params.pathPrefix);
|
|
44
|
+
const theCatchToBeOpened = await readCacheFileDefaultLocation(params.primary_address, pathPrefix);
|
|
41
45
|
if (!params.primary_address)
|
|
42
46
|
throw new Error(`primary_address is required, potentially half filled out wallet setting in: ${params.scan_settings_path || SCAN_SETTINGS_STORE_NAME_DEFAULT}`);
|
|
43
47
|
// use ScanSettingsOpened instead of direct scanSettings calls
|
|
44
|
-
const scanSettings = await ScanSettingsOpened.create(params.scan_settings_path,
|
|
48
|
+
const scanSettings = await ScanSettingsOpened.create(params.scan_settings_path, pathPrefix);
|
|
45
49
|
const walletSettings = scanSettings.getWallet(params.primary_address);
|
|
46
50
|
if (!walletSettings)
|
|
47
51
|
throw new Error(`wallet not found in settings. did you call openwallet with the right params?
|
|
@@ -55,7 +59,7 @@ export class ScanCacheOpened {
|
|
|
55
59
|
// read secret_view_key and secret_spend_key from env
|
|
56
60
|
const walletSettingsWithKeys = await walletSettingsPlusKeys(walletWithSettings);
|
|
57
61
|
// create viewpair + ScanCacheOpened instance
|
|
58
|
-
const scanCacheOpen = new ScanCacheOpened(scanSettings, await ViewPair.create(params.primary_address, walletSettingsWithKeys.secret_view_key, walletSettings.subaddress_index, scanSettings.node_url), walletSettings.wallet_route, walletSettings.wallet_name, walletSettings.wallet_slot, params.no_worker || false, params.masterCacheChanged || null, scanSettings.start_height, params.scan_settings_path,
|
|
62
|
+
const scanCacheOpen = new ScanCacheOpened(scanSettings, await ViewPair.create(params.primary_address, walletSettingsWithKeys.secret_view_key, walletSettings.subaddress_index, scanSettings.node_url), walletSettings.wallet_route, walletSettings.wallet_name, walletSettings.wallet_slot, params.no_worker || false, params.masterCacheChanged || null, scanSettings.start_height, params.scan_settings_path, pathPrefix, params.workerError);
|
|
59
63
|
if (theCatchToBeOpened)
|
|
60
64
|
scanCacheOpen._cache = theCatchToBeOpened;
|
|
61
65
|
if (!walletSettings.halted) {
|
|
@@ -66,7 +70,7 @@ export class ScanCacheOpened {
|
|
|
66
70
|
scanCacheOpen._highest_subaddress_index =
|
|
67
71
|
walletSettings.subaddress_index || SUB_ADDRESS_INDEX_DEFAULT_VALUE;
|
|
68
72
|
if (!params.no_stats) {
|
|
69
|
-
scanCacheOpen._stats = await alignScanStatsWithCache(scanCacheOpen._cache, scanCacheOpen.view_pair, params.primary_address,
|
|
73
|
+
scanCacheOpen._stats = await alignScanStatsWithCache(scanCacheOpen._cache, scanCacheOpen.view_pair, params.primary_address, pathPrefix, walletSettings.subaddress_index, lastRange(scanCacheOpen._cache.scanned_ranges)?.end);
|
|
70
74
|
}
|
|
71
75
|
else {
|
|
72
76
|
scanCacheOpen._no_stats = params.no_stats; // true
|
|
@@ -83,8 +87,10 @@ export class ScanCacheOpened {
|
|
|
83
87
|
return this._highest_subaddress_index;
|
|
84
88
|
}
|
|
85
89
|
get current_height() {
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
// progress on the range that covers start_height (start can jump;
|
|
91
|
+
// lastRange would pick a different segment and be wrong)
|
|
92
|
+
const current_range = findRange(this._cache.scanned_ranges, this._start_height || 0);
|
|
93
|
+
return current_range == null ? null : current_range.end;
|
|
88
94
|
}
|
|
89
95
|
get current_top_range_height() {
|
|
90
96
|
if (typeof this._stats === "undefined" || this._stats === null)
|
|
@@ -92,7 +98,7 @@ export class ScanCacheOpened {
|
|
|
92
98
|
return this._stats.height;
|
|
93
99
|
}
|
|
94
100
|
async changeStartHeight(start_height) {
|
|
95
|
-
this.stopWorker();
|
|
101
|
+
await this.stopWorker();
|
|
96
102
|
await this._scanSettings.setStartHeight(start_height);
|
|
97
103
|
this._start_height = start_height;
|
|
98
104
|
await this.unpause();
|
|
@@ -167,7 +173,7 @@ export class ScanCacheOpened {
|
|
|
167
173
|
return this._scanSettings.logs_exclude;
|
|
168
174
|
}
|
|
169
175
|
async changeNodeUrlAndStartHeight(node_url, start_height) {
|
|
170
|
-
this.stopWorker();
|
|
176
|
+
await this.stopWorker();
|
|
171
177
|
if (node_url !== undefined) {
|
|
172
178
|
await this._scanSettings.setNodeUrl(node_url);
|
|
173
179
|
this.node_url = node_url;
|
|
@@ -179,7 +185,7 @@ export class ScanCacheOpened {
|
|
|
179
185
|
await this.unpause();
|
|
180
186
|
}
|
|
181
187
|
async changeNodeUrl(node_url) {
|
|
182
|
-
this.stopWorker();
|
|
188
|
+
await this.stopWorker();
|
|
183
189
|
await this._scanSettings.setNodeUrl(node_url);
|
|
184
190
|
this.node_url = node_url;
|
|
185
191
|
await this.unpause();
|
|
@@ -188,7 +194,7 @@ export class ScanCacheOpened {
|
|
|
188
194
|
await this._scanSettings.setMerchantConfirmations(merchant_confirmations);
|
|
189
195
|
}
|
|
190
196
|
async setCpuWorkerCount(cpu_worker_count) {
|
|
191
|
-
this.stopWorker();
|
|
197
|
+
await this.stopWorker();
|
|
192
198
|
await this._scanSettings.setCpuWorkerCount(cpu_worker_count);
|
|
193
199
|
await this.unpause();
|
|
194
200
|
}
|
|
@@ -201,7 +207,7 @@ export class ScanCacheOpened {
|
|
|
201
207
|
await this._scanSettings.setWalletName(this.view_pair.primary_address, name);
|
|
202
208
|
}
|
|
203
209
|
async retry() {
|
|
204
|
-
this.stopWorker();
|
|
210
|
+
await this.stopWorker();
|
|
205
211
|
// scansettings so external changes (e.g. from a sidebar frontend instance) are picked up
|
|
206
212
|
await this._scanSettings.reload();
|
|
207
213
|
const walletStillExists = this._scanSettings.walletExists(this.primary_address);
|
|
@@ -385,18 +391,20 @@ export class ScanCacheOpened {
|
|
|
385
391
|
let maybeInputs = [];
|
|
386
392
|
let maybeFeeEstimate;
|
|
387
393
|
let maybeSendResult;
|
|
394
|
+
let maybeSignedTx;
|
|
388
395
|
try {
|
|
389
396
|
const { selectedInputs, feeEstimate } = await this.calculateFeeAndSelectInputs(params);
|
|
390
397
|
maybeInputs = selectedInputs;
|
|
391
398
|
maybeFeeEstimate = feeEstimate;
|
|
392
399
|
const unsignedTx = await this.makeTransactionFromSelectedInputs(params.payments, selectedInputs, feeEstimate);
|
|
393
400
|
const signedTx = await this.signTransaction(unsignedTx);
|
|
401
|
+
maybeSignedTx = signedTx;
|
|
394
402
|
const sendResult = await this.sendTransaction(signedTx);
|
|
395
403
|
maybeSendResult = sendResult;
|
|
396
404
|
if (sendResult.status !== "OK")
|
|
397
405
|
throw new Error("send raw transaction rpc returned error");
|
|
398
406
|
// before writing the scan cache, we stop the worker to avoid a race
|
|
399
|
-
this.stopWorker();
|
|
407
|
+
await this.stopWorker();
|
|
400
408
|
// write txlog to cache + update pending_spent_utxos (affects stats + spendability)
|
|
401
409
|
await writeCacheFileDefaultLocationThrows({
|
|
402
410
|
primary_address: this.primary_address,
|
|
@@ -415,6 +423,8 @@ export class ScanCacheOpened {
|
|
|
415
423
|
inputs_index,
|
|
416
424
|
height: this.current_height,
|
|
417
425
|
timestamp: Date.now(),
|
|
426
|
+
invocationId: params.invocationId,
|
|
427
|
+
signed_tx: signedTx,
|
|
418
428
|
};
|
|
419
429
|
const newLen = cache.tx_logs.push(txLog);
|
|
420
430
|
const txLogIndex = newLen - 1;
|
|
@@ -440,7 +450,7 @@ export class ScanCacheOpened {
|
|
|
440
450
|
}
|
|
441
451
|
catch (e) {
|
|
442
452
|
// before writing the scan cache, we stop the worker to avoid a race
|
|
443
|
-
this.stopWorker();
|
|
453
|
+
await this.stopWorker();
|
|
444
454
|
// write txlog error to cache
|
|
445
455
|
await writeCacheFileDefaultLocationThrows({
|
|
446
456
|
primary_address: this.primary_address,
|
|
@@ -460,17 +470,16 @@ export class ScanCacheOpened {
|
|
|
460
470
|
inputs_index,
|
|
461
471
|
height: this.current_height,
|
|
462
472
|
timestamp: Date.now(),
|
|
473
|
+
invocationId: params.invocationId,
|
|
474
|
+
signed_tx: maybeSignedTx,
|
|
463
475
|
};
|
|
464
|
-
|
|
476
|
+
cache.tx_logs.push(txLog);
|
|
465
477
|
},
|
|
466
478
|
});
|
|
467
479
|
const newCache = await readCacheFileDefaultLocation(this.primary_address, this.pathPrefix);
|
|
468
480
|
if (!newCache)
|
|
469
481
|
throw new Error(`cache not found for primary address: ${this.primary_address}, and path prefix: ${this.pathPrefix}`);
|
|
470
|
-
const changed_outputs =
|
|
471
|
-
change_reason: "spent",
|
|
472
|
-
output: input,
|
|
473
|
-
}));
|
|
482
|
+
const changed_outputs = [];
|
|
474
483
|
await this.feed({
|
|
475
484
|
newCache,
|
|
476
485
|
changed_outputs,
|
|
@@ -480,6 +489,179 @@ export class ScanCacheOpened {
|
|
|
480
489
|
throw e;
|
|
481
490
|
}
|
|
482
491
|
}
|
|
492
|
+
outputsForIndexes(indexes) {
|
|
493
|
+
const byId = new Map(this.spendableInputs().map((output) => [
|
|
494
|
+
String(output.index_on_blockchain),
|
|
495
|
+
output,
|
|
496
|
+
]));
|
|
497
|
+
const selected = [];
|
|
498
|
+
for (const id of indexes) {
|
|
499
|
+
const output = byId.get(id);
|
|
500
|
+
if (!output)
|
|
501
|
+
throw new Error(`input not spendable: ${id}`);
|
|
502
|
+
selected.push(output);
|
|
503
|
+
}
|
|
504
|
+
if (!selected.length)
|
|
505
|
+
throw new Error("no inputs selected");
|
|
506
|
+
return selected;
|
|
507
|
+
}
|
|
508
|
+
async makeSignSend(params) {
|
|
509
|
+
// an empty list means the wallet selects the inputs
|
|
510
|
+
if (!params.input_indexes.length) {
|
|
511
|
+
return this.makeSignSendTransaction({
|
|
512
|
+
payments: params.payments,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
return this.makeSignSendTransaction({
|
|
516
|
+
payments: params.payments,
|
|
517
|
+
inputs: this.outputsForIndexes(params.input_indexes),
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
async sweepSignSend(params) {
|
|
521
|
+
const inputs = params.input_indexes.length
|
|
522
|
+
? this.outputsForIndexes(params.input_indexes)
|
|
523
|
+
: this.spendableInputs();
|
|
524
|
+
if (!inputs.length)
|
|
525
|
+
throw new Error("no inputs selected");
|
|
526
|
+
const feeEstimate = await this.getFeeEstimate();
|
|
527
|
+
const unsignedTx = await this.makeSweepTransactionFromSelectedInputs(params.address, inputs, feeEstimate);
|
|
528
|
+
const signedTx = await this.signTransaction(unsignedTx);
|
|
529
|
+
const payments = [{ address: params.address, amount: "0" }];
|
|
530
|
+
const sendResult = await this.sendTransaction(signedTx);
|
|
531
|
+
if (sendResult.status !== "OK") {
|
|
532
|
+
await this.recordSend({
|
|
533
|
+
payments,
|
|
534
|
+
inputs,
|
|
535
|
+
signedTx,
|
|
536
|
+
feeEstimate,
|
|
537
|
+
sendResult,
|
|
538
|
+
error: "send raw transaction rpc returned error",
|
|
539
|
+
lockInputs: false,
|
|
540
|
+
});
|
|
541
|
+
throw new Error("send raw transaction rpc returned error");
|
|
542
|
+
}
|
|
543
|
+
await this.recordSend({
|
|
544
|
+
payments,
|
|
545
|
+
inputs,
|
|
546
|
+
signedTx,
|
|
547
|
+
feeEstimate,
|
|
548
|
+
sendResult,
|
|
549
|
+
lockInputs: true,
|
|
550
|
+
});
|
|
551
|
+
return sendResult;
|
|
552
|
+
}
|
|
553
|
+
async rebroadcastTx(txLogIndex) {
|
|
554
|
+
const txlog = this._cache.tx_logs?.[txLogIndex];
|
|
555
|
+
if (!txlog?.signed_tx)
|
|
556
|
+
throw new Error("no signed tx to rebroadcast");
|
|
557
|
+
const inputs = txlog.inputs_index
|
|
558
|
+
.map((id) => this._cache.outputs[id])
|
|
559
|
+
.filter((output) => output);
|
|
560
|
+
if (inputs.length &&
|
|
561
|
+
inputs.every((output) => typeof output.spent_in_tx_hash === "string"))
|
|
562
|
+
throw new Error("inputs already spent on chain");
|
|
563
|
+
const sendResult = await this.sendTransaction(txlog.signed_tx);
|
|
564
|
+
await this.stopWorker();
|
|
565
|
+
await writeCacheFileDefaultLocationThrows({
|
|
566
|
+
primary_address: this.primary_address,
|
|
567
|
+
pathPrefix: this.pathPrefix,
|
|
568
|
+
writeCallback: async (cache) => {
|
|
569
|
+
const row = cache.tx_logs?.[txLogIndex];
|
|
570
|
+
if (!row)
|
|
571
|
+
throw new Error("tx log missing");
|
|
572
|
+
row.sendResult = sendResult;
|
|
573
|
+
if (sendResult.status === "OK") {
|
|
574
|
+
row.error = undefined;
|
|
575
|
+
if (!cache.pending_spent_utxos)
|
|
576
|
+
cache.pending_spent_utxos = {};
|
|
577
|
+
for (const inputId of row.inputs_index)
|
|
578
|
+
cache.pending_spent_utxos[inputId] = txLogIndex;
|
|
579
|
+
}
|
|
580
|
+
else {
|
|
581
|
+
row.error = sendResult.reason || "rebroadcast failed";
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
const newCache = await readCacheFileDefaultLocation(this.primary_address, this.pathPrefix);
|
|
586
|
+
if (!newCache)
|
|
587
|
+
throw new Error("cache not found after rebroadcast");
|
|
588
|
+
await this.feed({ newCache, changed_outputs: [] });
|
|
589
|
+
await this.unpause();
|
|
590
|
+
return sendResult;
|
|
591
|
+
}
|
|
592
|
+
get failed_txs() {
|
|
593
|
+
const logs = this._cache.tx_logs || [];
|
|
594
|
+
const rows = [];
|
|
595
|
+
for (let index = 0; index < logs.length; index++) {
|
|
596
|
+
const txlog = logs[index];
|
|
597
|
+
if (!txlog || txlog.hidden_on_send_plate)
|
|
598
|
+
continue;
|
|
599
|
+
if (txlog.sendResult?.status === "OK")
|
|
600
|
+
continue;
|
|
601
|
+
const inputs = txlog.inputs_index
|
|
602
|
+
.map((id) => this._cache.outputs[id])
|
|
603
|
+
.filter((output) => output);
|
|
604
|
+
if (inputs.length &&
|
|
605
|
+
inputs.every((output) => typeof output.spent_in_tx_hash === "string"))
|
|
606
|
+
continue;
|
|
607
|
+
rows.push({ index, txlog, inputs });
|
|
608
|
+
}
|
|
609
|
+
return rows;
|
|
610
|
+
}
|
|
611
|
+
async recordSend(args) {
|
|
612
|
+
await this.stopWorker();
|
|
613
|
+
await writeCacheFileDefaultLocationThrows({
|
|
614
|
+
primary_address: this.primary_address,
|
|
615
|
+
pathPrefix: this.pathPrefix,
|
|
616
|
+
writeCallback: async (cache) => {
|
|
617
|
+
if (!cache.tx_logs)
|
|
618
|
+
cache.tx_logs = [];
|
|
619
|
+
if (!cache.pending_spent_utxos)
|
|
620
|
+
cache.pending_spent_utxos = {};
|
|
621
|
+
const inputs_index = args.inputs.map((input) => String(input.index_on_blockchain));
|
|
622
|
+
const txLog = {
|
|
623
|
+
sendResult: args.sendResult,
|
|
624
|
+
error: args.error,
|
|
625
|
+
feeEstimate: args.feeEstimate,
|
|
626
|
+
payments: args.payments,
|
|
627
|
+
node_url: this.node_url,
|
|
628
|
+
inputs_index,
|
|
629
|
+
height: this.current_height,
|
|
630
|
+
timestamp: Date.now(),
|
|
631
|
+
signed_tx: args.signedTx,
|
|
632
|
+
};
|
|
633
|
+
const txLogIndex = cache.tx_logs.push(txLog) - 1;
|
|
634
|
+
if (!args.lockInputs)
|
|
635
|
+
return;
|
|
636
|
+
for (const inputId of inputs_index)
|
|
637
|
+
cache.pending_spent_utxos[inputId] = txLogIndex;
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
const newCache = await readCacheFileDefaultLocation(this.primary_address, this.pathPrefix);
|
|
641
|
+
if (!newCache)
|
|
642
|
+
throw new Error("cache not found after send");
|
|
643
|
+
const changed_outputs = args.lockInputs
|
|
644
|
+
? args.inputs.map((output) => ({ change_reason: "spent", output }))
|
|
645
|
+
: [];
|
|
646
|
+
await this.feed({ newCache, changed_outputs });
|
|
647
|
+
await this.unpause();
|
|
648
|
+
}
|
|
649
|
+
async dismissSendPlate() {
|
|
650
|
+
await this.stopWorker();
|
|
651
|
+
await writeCacheFileDefaultLocationThrows({
|
|
652
|
+
primary_address: this.primary_address,
|
|
653
|
+
pathPrefix: this.pathPrefix,
|
|
654
|
+
writeCallback: async (cache) => {
|
|
655
|
+
for (const row of cache.tx_logs || [])
|
|
656
|
+
row.hidden_on_send_plate = true;
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
const newCache = await readCacheFileDefaultLocation(this.primary_address, this.pathPrefix);
|
|
660
|
+
if (!newCache)
|
|
661
|
+
throw new Error("cache not found after dismiss");
|
|
662
|
+
await this.feed({ newCache, changed_outputs: [] });
|
|
663
|
+
await this.unpause();
|
|
664
|
+
}
|
|
483
665
|
/**
|
|
484
666
|
* makeStandardTransaction
|
|
485
667
|
*/
|
|
@@ -550,15 +732,35 @@ export class ScanCacheOpened {
|
|
|
550
732
|
};
|
|
551
733
|
}
|
|
552
734
|
async pause() {
|
|
553
|
-
this.stopWorker();
|
|
735
|
+
await this.stopWorker();
|
|
554
736
|
return await this._scanSettings.haltWallet(this.view_pair.primary_address);
|
|
555
737
|
}
|
|
556
|
-
|
|
738
|
+
// debug: dump coordinator + cpu worker heaps while they are live
|
|
739
|
+
// in browser: use inspector directly, this is only useful for bun
|
|
740
|
+
async dumpWorkerHeaps(dir) {
|
|
741
|
+
if (!this.worker?.dumpHeaps)
|
|
742
|
+
return [];
|
|
743
|
+
return await this.worker.dumpHeaps(dir);
|
|
744
|
+
}
|
|
745
|
+
async stopWorker() {
|
|
557
746
|
if (this.worker) {
|
|
558
|
-
this.worker
|
|
747
|
+
const worker = this.worker;
|
|
748
|
+
// clear ref first so concurrent stop/retry cannot double shutdown
|
|
559
749
|
this.worker.cpuWorkers = [];
|
|
560
750
|
this.worker.fetchWorker = undefined;
|
|
561
751
|
delete this.worker;
|
|
752
|
+
// drain coordinator first, then hard kill workers
|
|
753
|
+
try {
|
|
754
|
+
await worker.shutdown();
|
|
755
|
+
}
|
|
756
|
+
catch {
|
|
757
|
+
// still terminate below
|
|
758
|
+
}
|
|
759
|
+
try {
|
|
760
|
+
worker.terminate();
|
|
761
|
+
}
|
|
762
|
+
catch {
|
|
763
|
+
}
|
|
562
764
|
}
|
|
563
765
|
}
|
|
564
766
|
async unpause() {
|
|
@@ -688,48 +890,120 @@ export class ScanCacheOpened {
|
|
|
688
890
|
_stats = null;
|
|
689
891
|
notifyListeners = [];
|
|
690
892
|
}
|
|
893
|
+
function defaultNotifyMasterChanged(params) {
|
|
894
|
+
void sendToBackground("walletCacheChanged", JSON.stringify(params, (_k, v) => typeof v === "bigint" ? v.toString() : v)).catch(() => { });
|
|
895
|
+
}
|
|
691
896
|
export class ManyScanCachesOpened {
|
|
692
897
|
connectionStatusOpened;
|
|
898
|
+
actionLogOpened;
|
|
693
899
|
_scanSettings;
|
|
694
900
|
_options;
|
|
695
901
|
get start_height() {
|
|
696
902
|
if (this.wallets.length === 0)
|
|
697
|
-
return
|
|
903
|
+
return this._scanSettings.start_height;
|
|
698
904
|
return this.wallets[0]?.start_height;
|
|
699
905
|
}
|
|
906
|
+
// overall scan tip = lagging non-halted wallet (this.wallets is already non-halted)
|
|
700
907
|
get current_height() {
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
908
|
+
let min = null;
|
|
909
|
+
for (const w of this.wallets) {
|
|
910
|
+
const h = w.current_height;
|
|
911
|
+
if (h == null)
|
|
912
|
+
continue;
|
|
913
|
+
if (min == null || h < min)
|
|
914
|
+
min = h;
|
|
915
|
+
}
|
|
916
|
+
return min;
|
|
704
917
|
}
|
|
705
918
|
get node_url() {
|
|
706
919
|
if (this.wallets.length === 0)
|
|
707
|
-
return "";
|
|
920
|
+
return this._scanSettings.node_url ?? "";
|
|
708
921
|
return this.wallets[0]?.node_url;
|
|
709
922
|
}
|
|
710
923
|
async changeNodeUrlAndStartHeight(node_url, start_height) {
|
|
711
|
-
if (this.
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
924
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
925
|
+
await sendToBackground("worker.changeNodeUrlAndStartHeight", {
|
|
926
|
+
node_url,
|
|
927
|
+
start_height,
|
|
928
|
+
}).catch(() => { });
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (this.wallets.length === 0) {
|
|
932
|
+
if (node_url !== undefined)
|
|
933
|
+
await this._scanSettings.setNodeUrl(node_url);
|
|
934
|
+
if (start_height !== undefined)
|
|
935
|
+
await this._scanSettings.setStartHeight(start_height);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
await this.wallets[0].changeNodeUrlAndStartHeight(node_url, start_height);
|
|
715
939
|
}
|
|
716
940
|
async retry() {
|
|
717
941
|
if (this.wallets.length === 0)
|
|
718
|
-
|
|
942
|
+
return;
|
|
719
943
|
const masterWallet = this.wallets[0];
|
|
720
944
|
return await masterWallet.retry();
|
|
721
945
|
}
|
|
722
|
-
stopWorker() {
|
|
946
|
+
async stopWorker() {
|
|
947
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
948
|
+
await sendToBackground("worker.stopWorker", null).catch(() => { });
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
723
951
|
if (this.wallets.length === 0)
|
|
724
|
-
|
|
952
|
+
return;
|
|
725
953
|
const masterWallet = this.wallets[0];
|
|
726
|
-
return masterWallet.stopWorker();
|
|
954
|
+
return await masterWallet.stopWorker();
|
|
727
955
|
}
|
|
728
|
-
|
|
956
|
+
// debug: dump live coordinator + cpu worker heaps via master wallet workers
|
|
957
|
+
// in browser: use inspector directly, this is only useful for bun
|
|
958
|
+
async dumpWorkerHeaps(dir) {
|
|
729
959
|
if (this.wallets.length === 0)
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
960
|
+
return [];
|
|
961
|
+
return await this.wallets[0].dumpWorkerHeaps(dir);
|
|
962
|
+
}
|
|
963
|
+
async changeNodeUrl(node_url) {
|
|
964
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
965
|
+
await sendToBackground("worker.setNodeUrl", { node_url }).catch(() => { });
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
if (this.wallets.length === 0) {
|
|
969
|
+
await this._scanSettings.setNodeUrl(node_url);
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
await this.wallets[0].changeNodeUrl(node_url);
|
|
973
|
+
}
|
|
974
|
+
walletByAddress(primary_address) {
|
|
975
|
+
const wallet = this.wallets.find((row) => row.primary_address === primary_address);
|
|
976
|
+
if (!wallet)
|
|
977
|
+
throw new Error("wallet not found");
|
|
978
|
+
return wallet;
|
|
979
|
+
}
|
|
980
|
+
async makeSignSend(params) {
|
|
981
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
982
|
+
await sendToBackground("worker.makeSignSend", params).catch(() => { });
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
return this.walletByAddress(params.primary_address).makeSignSend(params);
|
|
986
|
+
}
|
|
987
|
+
async sweepSignSend(params) {
|
|
988
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
989
|
+
await sendToBackground("worker.sweepSignSend", params).catch(() => { });
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
return this.walletByAddress(params.primary_address).sweepSignSend(params);
|
|
993
|
+
}
|
|
994
|
+
async rebroadcastTx(params) {
|
|
995
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
996
|
+
await sendToBackground("worker.rebroadcastTx", params).catch(() => { });
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
return this.walletByAddress(params.primary_address).rebroadcastTx(params.tx_log_index);
|
|
1000
|
+
}
|
|
1001
|
+
async dismissSendPlate(params) {
|
|
1002
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
1003
|
+
await sendToBackground("worker.dismissSendPlate", params).catch(() => { });
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
return this.walletByAddress(params.primary_address).dismissSendPlate();
|
|
733
1007
|
}
|
|
734
1008
|
get merchant_confirmations() {
|
|
735
1009
|
if (this.wallets.length === 0)
|
|
@@ -742,19 +1016,13 @@ export class ManyScanCachesOpened {
|
|
|
742
1016
|
return this.wallets[0]?.cpu_worker_count;
|
|
743
1017
|
}
|
|
744
1018
|
get logs() {
|
|
745
|
-
|
|
746
|
-
return undefined;
|
|
747
|
-
return this.wallets[0]?.logs;
|
|
1019
|
+
return this._scanSettings.logs;
|
|
748
1020
|
}
|
|
749
1021
|
get logs_include() {
|
|
750
|
-
|
|
751
|
-
return undefined;
|
|
752
|
-
return this.wallets[0]?.logs_include;
|
|
1022
|
+
return this._scanSettings.logs_include;
|
|
753
1023
|
}
|
|
754
1024
|
get logs_exclude() {
|
|
755
|
-
|
|
756
|
-
return undefined;
|
|
757
|
-
return this.wallets[0]?.logs_exclude;
|
|
1025
|
+
return this._scanSettings.logs_exclude;
|
|
758
1026
|
}
|
|
759
1027
|
get connectionStatus() {
|
|
760
1028
|
return this.connectionStatusOpened.connectionStatus;
|
|
@@ -770,18 +1038,29 @@ export class ManyScanCachesOpened {
|
|
|
770
1038
|
}
|
|
771
1039
|
async setMerchantConfirmations(merchant_confirmations) {
|
|
772
1040
|
if (this.wallets.length === 0)
|
|
773
|
-
|
|
1041
|
+
return;
|
|
774
1042
|
return await this.wallets[0].setMerchantConfirmations(merchant_confirmations);
|
|
775
1043
|
}
|
|
776
1044
|
async setCpuWorkerCount(cpu_worker_count) {
|
|
777
1045
|
if (this.wallets.length === 0)
|
|
778
|
-
|
|
1046
|
+
return;
|
|
779
1047
|
return await this.wallets[0].setCpuWorkerCount(cpu_worker_count);
|
|
780
1048
|
}
|
|
781
1049
|
async setLogSettings(logs, logs_include, logs_exclude) {
|
|
782
|
-
if (this.
|
|
783
|
-
|
|
784
|
-
|
|
1050
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
1051
|
+
await sendToBackground("worker.setLogSettings", {
|
|
1052
|
+
logs,
|
|
1053
|
+
logs_include,
|
|
1054
|
+
logs_exclude,
|
|
1055
|
+
}).catch(() => { });
|
|
1056
|
+
}
|
|
1057
|
+
await this._scanSettings.setLogSettings(logs, logs_include, logs_exclude);
|
|
1058
|
+
if (this.actionLogOpened.extensionMessageBus === "ui")
|
|
1059
|
+
return;
|
|
1060
|
+
for (const wallet of this.wallets)
|
|
1061
|
+
await wallet.stopWorker();
|
|
1062
|
+
for (const wallet of this.wallets)
|
|
1063
|
+
await wallet.unpause();
|
|
785
1064
|
}
|
|
786
1065
|
async setWalletName(primary_address, name) {
|
|
787
1066
|
await this._scanSettings.setWalletName(primary_address, name);
|
|
@@ -790,10 +1069,17 @@ export class ManyScanCachesOpened {
|
|
|
790
1069
|
await this._scanSettings.setWalletSlot(primary_address, slot);
|
|
791
1070
|
}
|
|
792
1071
|
async changeStartHeight(start_height) {
|
|
793
|
-
if (this.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1072
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
1073
|
+
await sendToBackground("worker.setStartHeight", {
|
|
1074
|
+
start_height,
|
|
1075
|
+
}).catch(() => { });
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (this.wallets.length === 0) {
|
|
1079
|
+
await this._scanSettings.setStartHeight(start_height);
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
await this.wallets[0].changeStartHeight(start_height);
|
|
797
1083
|
}
|
|
798
1084
|
static async _buildWallets(scanSettingsOpened, options) {
|
|
799
1085
|
const { scan_settings_path, pathPrefix, no_worker, no_stats, workerError, notifyMasterChanged, } = options;
|
|
@@ -851,28 +1137,49 @@ export class ManyScanCachesOpened {
|
|
|
851
1137
|
static async create(options) {
|
|
852
1138
|
const { scan_settings_path, pathPrefix, onConnectionStatusChange, connectionStatusIntervalMs, autoRetry, retryDelayMs, } = options;
|
|
853
1139
|
const scanSettingsOpened = await ScanSettingsOpened.create(scan_settings_path, pathPrefix);
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1140
|
+
// persist log options into ScanSettings.json before workers start
|
|
1141
|
+
if (options.logs !== undefined ||
|
|
1142
|
+
options.logs_include !== undefined ||
|
|
1143
|
+
options.logs_exclude !== undefined) {
|
|
1144
|
+
await scanSettingsOpened.setLogSettings(options.logs !== undefined ? options.logs : scanSettingsOpened.logs, options.logs_include !== undefined
|
|
1145
|
+
? options.logs_include
|
|
1146
|
+
: scanSettingsOpened.logs_include, options.logs_exclude !== undefined
|
|
1147
|
+
? options.logs_exclude
|
|
1148
|
+
: scanSettingsOpened.logs_exclude);
|
|
1149
|
+
}
|
|
857
1150
|
// wrap workerError with auto-retry
|
|
858
1151
|
let retryTimer;
|
|
859
1152
|
let instance = null;
|
|
1153
|
+
const clearRetryTimer = () => {
|
|
1154
|
+
clearTimeout(retryTimer);
|
|
1155
|
+
retryTimer = undefined;
|
|
1156
|
+
};
|
|
860
1157
|
const retryFn = async () => {
|
|
861
|
-
|
|
862
|
-
|
|
1158
|
+
clearRetryTimer();
|
|
1159
|
+
// no wallets means idle, do not rebuild or restart workers
|
|
1160
|
+
if (!instance || instance.wallets.length === 0)
|
|
1161
|
+
return;
|
|
1162
|
+
const node_url = instance.node_url;
|
|
863
1163
|
if (!node_url)
|
|
864
|
-
|
|
1164
|
+
return;
|
|
1165
|
+
// probe first; only restart workers when node is reachable
|
|
865
1166
|
const getinfo_result = await get_info(node_url)
|
|
866
|
-
.then((r) =>
|
|
867
|
-
r?.status === "OK";
|
|
868
|
-
})
|
|
1167
|
+
.then((r) => r?.status === "OK")
|
|
869
1168
|
.catch(() => false);
|
|
870
|
-
if (getinfo_result)
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
1169
|
+
if (!getinfo_result) {
|
|
1170
|
+
// still down: try again later without buildWallets thrash
|
|
1171
|
+
if (!retryTimer && instance.wallets.length > 0) {
|
|
1172
|
+
retryTimer = setTimeout(retryFn, retryDelayMs ?? 5000);
|
|
1173
|
+
}
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
await instance.buildWallets();
|
|
874
1177
|
};
|
|
875
1178
|
const newOptions = { ...options };
|
|
1179
|
+
// browser worker (background.ts): broadcast cache to sidebar
|
|
1180
|
+
if (!newOptions.notifyMasterChanged && !newOptions.no_worker) {
|
|
1181
|
+
newOptions.notifyMasterChanged = defaultNotifyMasterChanged;
|
|
1182
|
+
}
|
|
876
1183
|
let connectionFailedShown = false;
|
|
877
1184
|
if (autoRetry) {
|
|
878
1185
|
const originalError = options.workerError;
|
|
@@ -881,7 +1188,7 @@ export class ManyScanCachesOpened {
|
|
|
881
1188
|
const msg = err instanceof Error ? err.message : String(err);
|
|
882
1189
|
if (csOpened.connectionStatus?.last_packet?.status ===
|
|
883
1190
|
"catastrophic_reorg") {
|
|
884
|
-
|
|
1191
|
+
clearRetryTimer();
|
|
885
1192
|
instance?.stopWorker();
|
|
886
1193
|
throw new Error("catastrophic reorg, aborting ...");
|
|
887
1194
|
}
|
|
@@ -893,14 +1200,13 @@ export class ManyScanCachesOpened {
|
|
|
893
1200
|
console.error("unable to connect to node ... retrying ...");
|
|
894
1201
|
}
|
|
895
1202
|
}
|
|
896
|
-
|
|
1203
|
+
// only schedule retry when we still have wallets to scan
|
|
1204
|
+
if (!retryTimer && instance && instance.wallets.length > 0) {
|
|
897
1205
|
retryTimer = setTimeout(retryFn, retryDelayMs ?? 5000);
|
|
898
1206
|
}
|
|
899
1207
|
};
|
|
900
1208
|
}
|
|
901
|
-
const wallets = await this._buildWallets(scanSettingsOpened, newOptions);
|
|
902
|
-
if (!wallets)
|
|
903
|
-
return undefined;
|
|
1209
|
+
const wallets = (await this._buildWallets(scanSettingsOpened, newOptions)) ?? [];
|
|
904
1210
|
const csOpened = new ConnectionStatusOpened(scan_settings_path || SCAN_SETTINGS_STORE_NAME_DEFAULT, autoRetry
|
|
905
1211
|
? (status) => {
|
|
906
1212
|
if (status?.last_packet?.status === "OK" && connectionFailedShown) {
|
|
@@ -912,40 +1218,73 @@ export class ManyScanCachesOpened {
|
|
|
912
1218
|
}
|
|
913
1219
|
: (onConnectionStatusChange ?? null));
|
|
914
1220
|
csOpened.watch(connectionStatusIntervalMs);
|
|
915
|
-
|
|
1221
|
+
const actionLogPath = newOptions.actionLogPath ??
|
|
1222
|
+
actionLogPathFromScanSettings(scan_settings_path || SCAN_SETTINGS_STORE_NAME_DEFAULT);
|
|
1223
|
+
const actionLogOpened = await ActionLogOpened.create({
|
|
1224
|
+
path: actionLogPath,
|
|
1225
|
+
backend: newOptions.actionLogBackend,
|
|
1226
|
+
onChange: newOptions.onActionLogChange ?? null,
|
|
1227
|
+
extensionMessageBus: newOptions.extensionMessageBus,
|
|
1228
|
+
});
|
|
1229
|
+
instance = new ManyScanCachesOpened(wallets, csOpened, actionLogOpened, scanSettingsOpened, newOptions);
|
|
916
1230
|
return instance;
|
|
917
1231
|
}
|
|
918
1232
|
async buildWallets() {
|
|
919
|
-
this.
|
|
1233
|
+
if (this.actionLogOpened.extensionMessageBus === "ui") {
|
|
1234
|
+
await sendToBackground("worker.buildWallets", null).catch(() => { });
|
|
1235
|
+
return;
|
|
1236
|
+
}
|
|
1237
|
+
await this.stopWorker();
|
|
1238
|
+
await this.reloadWalletsAfterStop();
|
|
1239
|
+
}
|
|
1240
|
+
// assumes stopWorker already ran (graceful shutdown + terminate)
|
|
1241
|
+
async reloadWalletsAfterStop() {
|
|
920
1242
|
await this._scanSettings.reload();
|
|
1243
|
+
// empty after reload: stay idle, do not open ScanCacheOpened or start workers
|
|
1244
|
+
const remaining = this._scanSettings.wallets.filter((w) => !w?.halted);
|
|
1245
|
+
if (remaining.length === 0) {
|
|
1246
|
+
this._wallets = [];
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
921
1249
|
const newWallets = await ManyScanCachesOpened._buildWallets(this._scanSettings, this._options);
|
|
922
|
-
|
|
923
|
-
throw new Error("no non-halted wallets left after rebuild, use addViewWallet or addSpendWallet first");
|
|
924
|
-
this._wallets = newWallets;
|
|
1250
|
+
this._wallets = newWallets ?? [];
|
|
925
1251
|
}
|
|
926
1252
|
async addViewWallet(primary_address, view_key, fields) {
|
|
1253
|
+
await this.stopWorker();
|
|
927
1254
|
await this._scanSettings.addViewWallet(primary_address, view_key, fields);
|
|
928
|
-
await this.
|
|
1255
|
+
await this.reloadWalletsAfterStop();
|
|
929
1256
|
}
|
|
930
1257
|
async addSpendWallet(wallet_secret, fields) {
|
|
1258
|
+
await this.stopWorker();
|
|
931
1259
|
await this._scanSettings.addSpendWallet(wallet_secret, fields);
|
|
932
|
-
await this.
|
|
1260
|
+
await this.reloadWalletsAfterStop();
|
|
933
1261
|
}
|
|
934
1262
|
async removeWallet(primary_address) {
|
|
1263
|
+
await this.stopWorker();
|
|
935
1264
|
await this._scanSettings.removeWallet(primary_address);
|
|
936
|
-
await this.
|
|
1265
|
+
await this.reloadWalletsAfterStop();
|
|
937
1266
|
}
|
|
938
1267
|
async feed(params) {
|
|
1268
|
+
if (this.wallets.length === 0)
|
|
1269
|
+
return;
|
|
939
1270
|
await this.wallets[0].feed(params);
|
|
940
1271
|
}
|
|
941
1272
|
_wallets = [];
|
|
942
1273
|
get wallets() {
|
|
943
1274
|
return this._wallets;
|
|
944
1275
|
}
|
|
945
|
-
constructor(wallets, connectionStatusOpened, _scanSettings, _options) {
|
|
1276
|
+
constructor(wallets, connectionStatusOpened, actionLogOpened, _scanSettings, _options) {
|
|
946
1277
|
this.connectionStatusOpened = connectionStatusOpened;
|
|
1278
|
+
this.actionLogOpened = actionLogOpened;
|
|
947
1279
|
this._scanSettings = _scanSettings;
|
|
948
1280
|
this._options = _options;
|
|
949
1281
|
this._wallets = wallets;
|
|
1282
|
+
this.actionLogOpened.bindMco(this);
|
|
950
1283
|
}
|
|
951
1284
|
}
|
|
1285
|
+
function actionLogPathFromScanSettings(scan_settings_path) {
|
|
1286
|
+
const base = scan_settings_path.replace(/\.json$/i, "");
|
|
1287
|
+
if (globalThis.areWeInTheBrowser === true)
|
|
1288
|
+
return "actionlog";
|
|
1289
|
+
return `${base}-actionlog.sqlite`;
|
|
1290
|
+
}
|