@spirobel/monero-wallet-api 0.5.3 → 0.5.4
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/multisig/escrow.js +0 -1
- package/dist/scanning-syncing/backgroundWorker.d.ts +0 -1
- package/dist/scanning-syncing/backgroundWorker.js +21 -33
- package/dist/scanning-syncing/connectionStatusOpened.d.ts +1 -1
- package/dist/scanning-syncing/connectionStatusOpened.js +2 -2
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +2 -0
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +81 -61
- package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +0 -22
- package/dist/scanning-syncing/scanresult/scanCoordination.js +3 -185
- package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
- package/dist/scanning-syncing/worker-mains/coordinator-main.js +0 -1
- package/dist/scanning-syncing/worker-mains/cpubound-main.d.ts +5 -2
- package/dist/scanning-syncing/worker-mains/cpubound-main.js +8 -7
- package/dist/scanning-syncing/worker-mains/worker.js +6 -1
- 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,6 +1,6 @@
|
|
|
1
|
-
import { setupBlocksBufferGenerator, ViewPair, handleConnectionStatusChanges, processScanResult, writeCacheToFile, sleep, findTipIndex, readWriteConnectionStatusFile,
|
|
1
|
+
import { setupBlocksBufferGenerator, ViewPair, handleConnectionStatusChanges, processScanResult, writeCacheToFile, sleep, findTipIndex, readWriteConnectionStatusFile, } from "../../api";
|
|
2
2
|
import { markWorkItemAsDone, } from "./scanLoop";
|
|
3
|
-
import { makeWorkItem
|
|
3
|
+
import { makeWorkItem } from "./scanLoop";
|
|
4
4
|
import { cullTooLargeScanHeight, getNonHaltedWallets, getPathPrefix, openScanSettingsFile, SCAN_SETTINGS_STORE_NAME_DEFAULT, walletSettingsPlusKeys, } from "../../api";
|
|
5
5
|
import { findRange, initScanCacheFile, lastRange, makeCacheRangeForHeight, mergeRanges, } from "./scanCache";
|
|
6
6
|
import { sendToCpuWorker } from "../worker-mains/cpubound-main";
|
|
@@ -166,19 +166,6 @@ export function reconcileWorkItemDone(blocksBuffer, workItemBuffer) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
-
/**
|
|
170
|
-
* one round of the select race. takes two primed promises (blocks, scan),
|
|
171
|
-
* races them, returns winner info. the loser stays in flight.
|
|
172
|
-
*/
|
|
173
|
-
export async function raceStep(blocksPromise, scanPromise) {
|
|
174
|
-
const winner = await Promise.race([
|
|
175
|
-
blocksPromise.then((v) => ({ src: "blocks", value: v })),
|
|
176
|
-
scanPromise.then((v) => ({ src: "scan", value: v })),
|
|
177
|
-
]);
|
|
178
|
-
const value = winner.value;
|
|
179
|
-
const done = !!winner.value.done;
|
|
180
|
-
return { src: winner.src, value, done };
|
|
181
|
-
}
|
|
182
169
|
/**
|
|
183
170
|
* handle a yield from the blocks buffer fetch loop.
|
|
184
171
|
*/
|
|
@@ -189,45 +176,6 @@ export async function handleBlocksYield(value, scanSettingsPath) {
|
|
|
189
176
|
await handleConnectionStatusChanges(value, scanSettingsPath);
|
|
190
177
|
return { isBlocksBufferChanged: false };
|
|
191
178
|
}
|
|
192
|
-
/**
|
|
193
|
-
* process a scan result for a completed work item.
|
|
194
|
-
* updates the cache, writes to disk, marks work item done, reconciles.
|
|
195
|
-
*/
|
|
196
|
-
export async function processScanResultForWorkItem(value, workBuffer, blocksBuffer, pathPrefix, secret_spend_key) {
|
|
197
|
-
if (value.type !== "Ready" || !value.result || !value.work_uuid)
|
|
198
|
-
return;
|
|
199
|
-
const item = await markWorkItemAsDone(value, workBuffer);
|
|
200
|
-
log("processScanResultForWorkItem", `item_work_uuid=${item?.work_uuid}`);
|
|
201
|
-
if (!item || item.status !== "scanwork_done")
|
|
202
|
-
throw new Error("[processScanResultForWorkItem] item not found or not scanwork not done. item_status=" +
|
|
203
|
-
item?.status);
|
|
204
|
-
const firstBlock = item.batch.get_blocks_result_meta.block_infos[item.from];
|
|
205
|
-
log("processScanResultForWorkItem", [
|
|
206
|
-
`block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} firstBlock=${firstBlock?.block_height} firstBlockHash=${firstBlock?.block_hash?.slice(0, 8)}`,
|
|
207
|
-
]);
|
|
208
|
-
log("processScanResultForWorkItem", [
|
|
209
|
-
`block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} from=${item.from} to=${item.to}`,
|
|
210
|
-
]);
|
|
211
|
-
const lastBlock = item.batch.get_blocks_result_meta.block_infos[item.to];
|
|
212
|
-
const cache = item.walletConfig.cache;
|
|
213
|
-
await processScanResult({
|
|
214
|
-
from_height: firstBlock.block_height,
|
|
215
|
-
to_height: lastBlock.block_height,
|
|
216
|
-
result: value.result,
|
|
217
|
-
scanCache: cache,
|
|
218
|
-
secret_spend_key,
|
|
219
|
-
});
|
|
220
|
-
await writeCacheToFile(cache, pathPrefix);
|
|
221
|
-
item.status = "process_result_done";
|
|
222
|
-
reconcileWorkItemDone(blocksBuffer, workBuffer);
|
|
223
|
-
//because cache is tied to the workitems by reference,
|
|
224
|
-
// the following workitems will have the most recent cache
|
|
225
|
-
//TODO: if we do cpu workers we need to be sure to wait with the processing at the top of this functon
|
|
226
|
-
// until all workitems for this wallet before it (to the left of it) are done
|
|
227
|
-
// the cpu worker loop generator will have to ensure the order of this
|
|
228
|
-
// currently as a sideeffect of the work scheduling function on fetch result aka blocks buffer changed,
|
|
229
|
-
// this is already implicitly handled
|
|
230
|
-
}
|
|
231
179
|
/**
|
|
232
180
|
* process a scan result for a completed work item.
|
|
233
181
|
* updates the cache, writes to disk, marks work item done, reconciles blockbuffer with this.
|
|
@@ -323,131 +271,6 @@ function computeETA(cache, totalBlocksScanned, scanStartTime) {
|
|
|
323
271
|
const etaMs = remaining / blocksPerMs;
|
|
324
272
|
return msToHHMM(etaMs);
|
|
325
273
|
}
|
|
326
|
-
/**
|
|
327
|
-
* coordinator main: async generator that drives the full scan cycle.
|
|
328
|
-
* takes only scanSettingsPath, derives everything else internally.
|
|
329
|
-
* yields events for some of the underlying generator events,
|
|
330
|
-
* look at CoordinatorEvent, processing not decoupled from cpu bound scan work,
|
|
331
|
-
* as this single threaded, there is no point
|
|
332
|
-
*/
|
|
333
|
-
export async function* coordinatorMain(scanSettingsPath, pathPrefix) {
|
|
334
|
-
const ctx = await setupCoordinator(scanSettingsPath, pathPrefix);
|
|
335
|
-
if (!ctx)
|
|
336
|
-
throw new Error("[coordinatorMain] findWorkToBeDone returned false");
|
|
337
|
-
const work_to_be_done = ctx.work_to_be_done;
|
|
338
|
-
const blocksBuffer = ctx.blocksBuffer;
|
|
339
|
-
const workBuffer = ctx.workBuffer;
|
|
340
|
-
const blocksGenerator = ctx.blocksGenerator;
|
|
341
|
-
let totalBlocksScanned = 0;
|
|
342
|
-
let scanStartTime = Date.now();
|
|
343
|
-
let blocksPromise = blocksGenerator.next();
|
|
344
|
-
const scanGens = new Map();
|
|
345
|
-
const scanPromises = new Map();
|
|
346
|
-
for (const wc of work_to_be_done.wallet_configs) {
|
|
347
|
-
scanGens.set(wc.primary_address, scanLoop({
|
|
348
|
-
primary_address: wc.primary_address,
|
|
349
|
-
secret_view_key: wc.secret_view_key,
|
|
350
|
-
secret_spend_key: wc.secret_spend_key,
|
|
351
|
-
subaddress_index: wc.subaddress_index,
|
|
352
|
-
}));
|
|
353
|
-
}
|
|
354
|
-
// prime all scan generators
|
|
355
|
-
// ( technically no need to do this as loop + scan generator + workbuffer structure mean it will happen automatically)
|
|
356
|
-
for (const [, gen] of scanGens) {
|
|
357
|
-
await gen.next();
|
|
358
|
-
}
|
|
359
|
-
while (true) {
|
|
360
|
-
const races = [blocksPromise.then((v) => ({ src: "blocks", value: v }))];
|
|
361
|
-
for (const [addr, p] of scanPromises) {
|
|
362
|
-
races.push(p.then((v) => ({ src: "scan", addr, value: v })));
|
|
363
|
-
}
|
|
364
|
-
const winner = await Promise.race(races);
|
|
365
|
-
log("coordinatorMain", [
|
|
366
|
-
"winner=" + winner.src + " addr=" + String(winner.addr ?? "").slice(0, 8),
|
|
367
|
-
]);
|
|
368
|
-
if (winner.src === "blocks") {
|
|
369
|
-
const result = winner.value.value;
|
|
370
|
-
const { isBlocksBufferChanged } = await handleBlocksYield(result, scanSettingsPath);
|
|
371
|
-
if (isBlocksBufferChanged) {
|
|
372
|
-
makeWorkItemsForAllWallets(work_to_be_done.wallet_configs, blocksBuffer, workBuffer);
|
|
373
|
-
// start scan promises for wallets that now have work
|
|
374
|
-
for (const wc of work_to_be_done.wallet_configs) {
|
|
375
|
-
const addr = wc.primary_address;
|
|
376
|
-
if (scanPromises.has(addr))
|
|
377
|
-
continue;
|
|
378
|
-
const gen = scanGens.get(addr);
|
|
379
|
-
log("coordinatorMain", ["scanGens.get(addr)", gen]);
|
|
380
|
-
if (!gen)
|
|
381
|
-
continue;
|
|
382
|
-
const item = workBuffer.find((x) => x.status === "fresh" && x.walletConfig.primary_address === addr);
|
|
383
|
-
if (item) {
|
|
384
|
-
item.status = "scanwork_in_progress";
|
|
385
|
-
scanPromises.set(addr, gen.next(item));
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
logBufStatus(blocksBuffer, workBuffer, scanPromises, "after_blocks");
|
|
389
|
-
yield { type: "blocks_buffer_changed" };
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
logBufStatus(blocksBuffer, workBuffer, scanPromises, "fetch_conn");
|
|
393
|
-
yield { type: "connection_status", status: result };
|
|
394
|
-
}
|
|
395
|
-
blocksPromise = blocksGenerator.next();
|
|
396
|
-
}
|
|
397
|
-
else {
|
|
398
|
-
const addr = winner.addr;
|
|
399
|
-
const gen = scanGens.get(addr);
|
|
400
|
-
const value = winner.value.value;
|
|
401
|
-
if (value.type === "InProgress") {
|
|
402
|
-
scanPromises.set(addr, gen.next());
|
|
403
|
-
}
|
|
404
|
-
else if (value.type === "Ready") {
|
|
405
|
-
const wc = work_to_be_done.wallet_configs.find((x) => x.primary_address === addr);
|
|
406
|
-
if (!wc)
|
|
407
|
-
throw new Error("[coordinatorMain] wallet config not found on process result");
|
|
408
|
-
// count blocks scanned in this work item for ETA
|
|
409
|
-
const doneItem = workBuffer.find((x) => x.work_uuid === value.work_uuid);
|
|
410
|
-
const blockCount = doneItem ? doneItem.to - doneItem.from + 1 : 0;
|
|
411
|
-
await processScanResultForWorkItem(value, workBuffer, blocksBuffer, getPathPrefix(scanSettingsPath, pathPrefix), wc?.secret_spend_key);
|
|
412
|
-
totalBlocksScanned += blockCount;
|
|
413
|
-
const eta = computeETA(wc.cache, totalBlocksScanned, scanStartTime);
|
|
414
|
-
// write connection status sync (includes eta)
|
|
415
|
-
if (eta) {
|
|
416
|
-
await readWriteConnectionStatusFile((cs) => {
|
|
417
|
-
cs.sync = {
|
|
418
|
-
...cs.sync,
|
|
419
|
-
scanned_ranges: wc.cache.scanned_ranges,
|
|
420
|
-
daemon_height: wc.cache.daemon_height,
|
|
421
|
-
current_scan_height: lastRange(wc.cache.scanned_ranges)?.end || 0,
|
|
422
|
-
eta,
|
|
423
|
-
timestamp: new Date().toISOString(),
|
|
424
|
-
};
|
|
425
|
-
}, scanSettingsPath);
|
|
426
|
-
}
|
|
427
|
-
const nextItem = workBuffer.find((x) => x.status !== "process_result_done" &&
|
|
428
|
-
x.walletConfig.primary_address === addr);
|
|
429
|
-
if (nextItem) {
|
|
430
|
-
scanPromises.set(addr, gen.next(nextItem));
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
scanPromises.delete(addr);
|
|
434
|
-
}
|
|
435
|
-
logBufStatus(blocksBuffer, workBuffer, scanPromises, "after_scan");
|
|
436
|
-
yield {
|
|
437
|
-
type: "scan_ready",
|
|
438
|
-
address: addr,
|
|
439
|
-
result: value,
|
|
440
|
-
newCache: wc.cache,
|
|
441
|
-
changed_outputs: [],
|
|
442
|
-
};
|
|
443
|
-
// signal idle when no scans active and nothing buffered
|
|
444
|
-
if (scanPromises.size === 0 && blocksBuffer.length === 0) {
|
|
445
|
-
yield { type: "all_idle" };
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
274
|
export async function setupCoordinator(scanSettingsPath, pathPrefix) {
|
|
452
275
|
const work_to_be_done = await findWorkToBeDone(scanSettingsPath, pathPrefix);
|
|
453
276
|
if (!work_to_be_done)
|
|
@@ -482,12 +305,7 @@ export async function setupCoordinator(scanSettingsPath, pathPrefix) {
|
|
|
482
305
|
*/
|
|
483
306
|
export async function* coordinatorMainMultithreaded(scanSettingsPath, pathPrefix, cpuPorts) {
|
|
484
307
|
if (!cpuPorts || cpuPorts.length === 0) {
|
|
485
|
-
|
|
486
|
-
// "[coordinatorMain Multithreaded] cpuPorts empty, there must be at least cpu worker",
|
|
487
|
-
// );
|
|
488
|
-
log("coordinatorMainMultithreaded", "fallback to single-threaded");
|
|
489
|
-
yield* coordinatorMain(scanSettingsPath, pathPrefix);
|
|
490
|
-
return;
|
|
308
|
+
throw new Error("[coordinatorMain Multithreaded] cpuPorts empty, there must be at least cpu worker");
|
|
491
309
|
}
|
|
492
310
|
const ctx = await setupCoordinator(scanSettingsPath, pathPrefix);
|
|
493
311
|
if (!ctx)
|