@spirobel/monero-wallet-api 0.4.0 → 0.4.1

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.
@@ -1,9 +1,10 @@
1
- import { setupBlocksBufferGenerator, ViewPair, handleConnectionStatusChanges, processScanResultWITHOUT_SIDE_EFFECTS, writeCacheToFile, sleep, findTipIndex, } from "../../api";
1
+ import { setupBlocksBufferGenerator, ViewPair, handleConnectionStatusChanges, processScanResult, writeCacheToFile, sleep, findTipIndex, } from "../../api";
2
2
  import { markWorkItemAsDone, } from "./scanLoop";
3
3
  import { makeWorkItem, scanLoop } from "./scanLoop";
4
4
  import { cullTooLargeScanHeight, getNonHaltedWallets, getPathPrefix, openScanSettingsFile, SCAN_SETTINGS_STORE_NAME_DEFAULT, walletSettingsPlusKeys, } from "../scanSettings";
5
5
  import { findRange, initScanCacheFile, lastRange, makeCacheRangeForHeight, mergeRanges, } from "./scanCache";
6
6
  import { sendToCpuWorker } from "../worker-mains/cpubound-main";
7
+ import { log } from "../../io/logging";
7
8
  /**
8
9
  * this depends only on ScanSettings.json start_height and wallet caches scanned_ranges
9
10
  * side effect: will init wallet cache file if it does not exist
@@ -59,7 +60,7 @@ export async function findWorkToBeDone(scan_settings_path = SCAN_SETTINGS_STORE_
59
60
  for (const wallet_cache of wallet_caches) {
60
61
  // only add the range to wallets that don't already have one
61
62
  if (!findRange(wallet_cache.scanned_ranges, total_start_height)) {
62
- // console.log("inserting range_at_start", range_at_start);
63
+ log("findWorkToBeDone", "inserting range_at_start");
63
64
  wallet_cache.scanned_ranges.push(range_at_start);
64
65
  wallet_cache.scanned_ranges = mergeRanges(wallet_cache.scanned_ranges);
65
66
  }
@@ -81,9 +82,9 @@ export async function findWorkToBeDone(scan_settings_path = SCAN_SETTINGS_STORE_
81
82
  export function workToBeDoneForBatch(cache, batch_meta_infos) {
82
83
  const begin_height = batch_meta_infos[0].block_height;
83
84
  const end_height = batch_meta_infos[batch_meta_infos.length - 1].block_height;
84
- console.log("[workToBeDoneForBatch]", begin_height, end_height);
85
+ log("workToBeDoneForBatch", [begin_height, end_height]);
85
86
  const foundRange = findRange(cache.scanned_ranges, begin_height);
86
- console.log("[workToBeDoneForBatch] foundRange", foundRange);
87
+ log("workToBeDoneForBatch", ["foundRange", foundRange]);
87
88
  if (foundRange) {
88
89
  const fullycovered = cache.scanned_ranges.find((r) => r.start <= begin_height && r.end > end_height);
89
90
  if (fullycovered) {
@@ -96,13 +97,13 @@ export function workToBeDoneForBatch(cache, batch_meta_infos) {
96
97
  throw new Error("[workToBeDoneForBatch] tip not found, malformed range that covers the work to be done for this batch");
97
98
  const tipindex = findTipIndex(batch_meta_infos, tip);
98
99
  if (tipindex === "reorg_found") {
99
- console.log("[workToBeDoneForBatch] reorg found");
100
+ log("workToBeDoneForBatch", "reorg found");
100
101
  return { from: 0 };
101
102
  }
102
103
  else if (tipindex === "empty_blocks_array") {
103
104
  return "skip";
104
105
  }
105
- console.log("[workToBeDoneForBatch] tipindex", tipindex);
106
+ log("workToBeDoneForBatch", ["tipindex", tipindex]);
106
107
  return { from: tipindex }; // NORMAL case of scheduling directly in front of the tip
107
108
  }
108
109
  }
@@ -123,36 +124,14 @@ export function makeWorkItemsFromBlocksBuffer(blocksBuffer, workItemBuffer, wall
123
124
  const alreadyReferenced = workItemBuffer.some((w) => w.batch.local_uuid === batch.local_uuid &&
124
125
  w.walletConfig.primary_address === walletConfig.primary_address);
125
126
  if (!alreadyReferenced) {
126
- // const begin_height =
127
- // batch.get_blocks_result_meta.block_infos[0].block_height;
128
- // const end_height =
129
- // batch.get_blocks_result_meta.block_infos[
130
- // batch.get_blocks_result_meta.block_infos.length - 1
131
- // ].block_height;
132
- // console.log(walletConfig.cache.scanned_ranges);
133
- // console.log(begin_height);
134
- // // skip batches fully covered by an existing scanned range
135
- // if (
136
- // walletConfig.cache.scanned_ranges.some(
137
- // (r) => r.start <= begin_height && r.end >= end_height,
138
- // )
139
- // ) {
140
- // throw new Error(
141
- // "skip" +
142
- // begin_height +
143
- // " end_height" +
144
- // end_height +
145
- // " " +
146
- // walletConfig.primary_address.slice(0, 6),
147
- // );
148
- // continue;
149
- // }
150
127
  const workToBeDone = workToBeDoneForBatch(walletConfig.cache, batch.get_blocks_result_meta.block_infos);
151
128
  if (workToBeDone === "skip") {
152
129
  continue;
153
130
  }
154
131
  const workItem = makeWorkItem(walletConfig, batch, from, to);
155
- console.log(`[reconcileBlocksBufferChanged] workItem: uuid=${workItem.work_uuid.slice()} to=${workItem.to} from=${workItem.from} batchbegin_height=${batch.get_blocks_result_meta.block_infos[0].block_height} batchend_height=${batch.get_blocks_result_meta.block_infos[batch.get_blocks_result_meta.block_infos.length - 1].block_height}`);
132
+ log("makeWorkItemsFromBlocksBuffer", [
133
+ `uuid=${workItem.work_uuid.slice()} to=${workItem.to} from=${workItem.from} batchbegin_height=${batch.get_blocks_result_meta.block_infos[0].block_height} batchend_height=${batch.get_blocks_result_meta.block_infos[batch.get_blocks_result_meta.block_infos.length - 1].block_height}`,
134
+ ]);
156
135
  workItemBuffer.push(workItem);
157
136
  }
158
137
  }
@@ -168,16 +147,16 @@ export function makeWorkItemsForAllWallets(wallet_configs, blocksBuffer, workBuf
168
147
  * blocks buffer if no remaining work items reference it.
169
148
  */
170
149
  export function reconcileWorkItemDone(blocksBuffer, workItemBuffer) {
171
- // console.log(
172
- // `[reconcileWorkItemDone] workItemBuffer.length=${workItemBuffer.length} , blocksBuffer.length=${blocksBuffer.length}`,
173
- // );
150
+ log("reconcileWorkItemDone", [
151
+ `workItemBuffer.length=${workItemBuffer.length} , blocksBuffer.length=${blocksBuffer.length}`,
152
+ ]);
174
153
  while (workItemBuffer.length > 0 &&
175
154
  workItemBuffer[0].status === "process_result_done") {
176
155
  const removed = workItemBuffer.shift();
177
156
  const stillReferenced = workItemBuffer.some((w) => w.batch.local_uuid === removed.batch.local_uuid);
178
- // console.log(
179
- // `[reconcileWorkItemDone] workItem: ${removed.work_uuid.slice()} removed. stillReferenced=${stillReferenced}`,
180
- // );
157
+ log("reconcileWorkItemDone", [
158
+ `workItem: ${removed.work_uuid.slice()} removed. stillReferenced=${stillReferenced}`,
159
+ ]);
181
160
  if (!stillReferenced) {
182
161
  const idx = blocksBuffer.findIndex((b) => b.local_uuid === removed.batch.local_uuid);
183
162
  // this really means we have to save work items scanCache to file
@@ -218,17 +197,20 @@ export async function processScanResultForWorkItem(value, workBuffer, blocksBuff
218
197
  if (value.type !== "Ready" || !value.result || !value.work_uuid)
219
198
  return;
220
199
  const item = await markWorkItemAsDone(value, workBuffer);
221
- //console.log(`[processScanResultForWorkItem] item=${JSON.stringify(item)}`);
200
+ log("processScanResultForWorkItem", `item_work_uuid=${item?.work_uuid}`);
222
201
  if (!item || item.status !== "scanwork_done")
223
202
  throw new Error("[processScanResultForWorkItem] item not found or not scanwork not done. item_status=" +
224
203
  item?.status);
225
204
  const firstBlock = item.batch.get_blocks_result_meta.block_infos[item.from];
226
- //console.log(`
227
- // [processScanResultForWorkItem] block_infos=${JSON.stringify(item.batch.get_blocks_result_meta.block_infos)} block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} firstBlock=${firstBlock} `);
228
- console.log(`[processScanResultForWorkItem] block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} from=${item.from} to=${item.to}`);
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
+ ]);
229
211
  const lastBlock = item.batch.get_blocks_result_meta.block_infos[item.to];
230
212
  const cache = item.walletConfig.cache;
231
- await processScanResultWITHOUT_SIDE_EFFECTS({
213
+ await processScanResult({
232
214
  from_height: firstBlock.block_height,
233
215
  to_height: lastBlock.block_height,
234
216
  result: value.result,
@@ -263,28 +245,27 @@ export async function processWorkItem(item, workBuffer, blocksBuffer, pathPrefix
263
245
  }
264
246
  }
265
247
  const firstBlock = item.batch.get_blocks_result_meta.block_infos[item.from];
266
- // console.log(
267
- // `[processWorkItem] block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} from=${item.from} to=${item.to}`,
268
- // );
248
+ log("processWorkItem", [
249
+ `block_infos.length=${item.batch.get_blocks_result_meta.block_infos.length} from=${item.from} to=${item.to}`,
250
+ ]);
269
251
  const lastBlock = item.batch.get_blocks_result_meta.block_infos[item.to];
270
- // console.log(
271
- // "[processWorkItem] ",
272
- // item.walletConfig.primary_address.slice(0, 6),
273
- // "@",
274
- // firstBlock.block_height,
275
- // "-",
276
- // lastBlock.block_height,
277
- // );
252
+ log("processWorkItem", [
253
+ item.walletConfig.primary_address.slice(0, 6),
254
+ "@",
255
+ firstBlock.block_height,
256
+ "-",
257
+ lastBlock.block_height,
258
+ ]);
278
259
  let res;
279
260
  try {
280
- res = await processScanResultWITHOUT_SIDE_EFFECTS({
261
+ res = await processScanResult({
281
262
  from_height: firstBlock.block_height,
282
263
  to_height: lastBlock.block_height,
283
264
  result: item.result,
284
265
  scanCache: cache,
285
266
  secret_spend_key,
286
267
  });
287
- // console.log("[processWorkItem] res=", res);
268
+ log("processWorkItem", ["res=", res]);
288
269
  }
289
270
  catch (error) {
290
271
  console.error("[processWorkItem] error=", error);
@@ -292,7 +273,7 @@ export async function processWorkItem(item, workBuffer, blocksBuffer, pathPrefix
292
273
  }
293
274
  await writeCacheToFile(cache, pathPrefix);
294
275
  item.status = "process_result_done";
295
- //console.log(`[processWorkItem] process_result_done item=${JSON.stringify(item)}`);
276
+ log("processWorkItem", `process_result_done work_uuid=${item?.work_uuid}`);
296
277
  // remove from blocksbuffer if no more work items reference it
297
278
  reconcileWorkItemDone(blocksBuffer, workBuffer);
298
279
  //because cache is tied to the workitems by reference,
@@ -314,7 +295,9 @@ function logBufStatus(blocksBuffer, workBuffer, ports, label) {
314
295
  w.batch.get_blocks_result_meta.block_infos[w.from]?.block_height +
315
296
  "-" +
316
297
  w.batch.get_blocks_result_meta.block_infos[w.to]?.block_height);
317
- console.log(`[buf] ${label} blocks=[${bb.join(",")}] work=[${wb.join(",")}] `);
298
+ log("logBufStatus", [
299
+ `[buf] ${label} blocks=[${bb.join(",")}] work=[${wb.join(",")}]`,
300
+ ]);
318
301
  }
319
302
  /**
320
303
  * coordinator main: async generator that drives the full scan cycle.
@@ -353,10 +336,9 @@ export async function* coordinatorMain(scanSettingsPath, pathPrefix) {
353
336
  races.push(p.then((v) => ({ src: "scan", addr, value: v })));
354
337
  }
355
338
  const winner = await Promise.race(races);
356
- console.log("[coordinatorMain] winner=" +
357
- winner.src +
358
- " addr=" +
359
- String(winner.addr ?? "").slice(0, 8));
339
+ log("coordinatorMain", [
340
+ "winner=" + winner.src + " addr=" + String(winner.addr ?? "").slice(0, 8),
341
+ ]);
360
342
  if (winner.src === "blocks") {
361
343
  const result = winner.value.value;
362
344
  const { isBlocksBufferChanged } = await handleBlocksYield(result, scanSettingsPath);
@@ -368,7 +350,7 @@ export async function* coordinatorMain(scanSettingsPath, pathPrefix) {
368
350
  if (scanPromises.has(addr))
369
351
  continue;
370
352
  const gen = scanGens.get(addr);
371
- console.log("scanGens.get(addr)", gen);
353
+ log("coordinatorMain", ["scanGens.get(addr)", gen]);
372
354
  if (!gen)
373
355
  continue;
374
356
  const item = workBuffer.find((x) => x.status === "fresh" && x.walletConfig.primary_address === addr);
@@ -459,7 +441,7 @@ export async function* coordinatorMainMultithreaded(scanSettingsPath, pathPrefix
459
441
  // throw new Error(
460
442
  // "[coordinatorMain Multithreaded] cpuPorts empty, there must be at least cpu worker",
461
443
  // );
462
- console.log("[coordinatorMainMultithreaded] fallback to single-threaded");
444
+ log("coordinatorMainMultithreaded", "fallback to single-threaded");
463
445
  yield* coordinatorMain(scanSettingsPath, pathPrefix);
464
446
  return;
465
447
  }
@@ -482,7 +464,7 @@ export async function* coordinatorMainMultithreaded(scanSettingsPath, pathPrefix
482
464
  let race_count = 0;
483
465
  while (true) {
484
466
  race_count++;
485
- console.log("[coordinatorMainMultithreaded] race_count", race_count);
467
+ log("coordinatorMainMultithreaded", ["race_count", race_count]);
486
468
  await scheduleWorkOnCpuPorts(freePorts, workBuffer);
487
469
  const scan_promises = freePorts
488
470
  .filter((ps) => ps.promise)
@@ -494,9 +476,9 @@ export async function* coordinatorMainMultithreaded(scanSettingsPath, pathPrefix
494
476
  value: v,
495
477
  })),
496
478
  ];
497
- console.log("[coordinatorMainMultithreaded] races", races);
479
+ log("coordinatorMainMultithreaded", ["races", races]);
498
480
  const winner = await Promise.race(races);
499
- //console.log("[coordinatorMainMultithreaded] cpu ports status", freePorts);
481
+ log("coordinatorMainMultithreaded", "cpu ports status");
500
482
  if ("src" in winner && winner.src === "blocks") {
501
483
  const result = winner.value.value;
502
484
  const { isBlocksBufferChanged } = await handleBlocksYield(result, scanSettingsPath);
@@ -529,7 +511,7 @@ export async function* coordinatorMainMultithreaded(scanSettingsPath, pathPrefix
529
511
  if (!to_be_processed)
530
512
  continue;
531
513
  const res = await processWorkItem(to_be_processed, workBuffer, blocksBuffer, getPathPrefix(scanSettingsPath, pathPrefix), wallet.secret_spend_key);
532
- // console.log("[coordinatorMainMultithreaded] processWorkItem result", res);
514
+ // log("coordinatorMainMultithreaded", "processWorkItem result");
533
515
  yield {
534
516
  type: "scan_ready",
535
517
  address: wallet.primary_address,
@@ -561,9 +543,14 @@ export async function scheduleWorkOnCpuPorts(ports, work_buffer) {
561
543
  const onmessage = (event) => {
562
544
  const msg = event.data;
563
545
  // handle the result here:
564
- console.log("[scheduleWorkOnCpuPorts] onmessage result", msg);
546
+ log("scheduleWorkOnCpuPorts", ["onmessage result", msg]);
565
547
  if (msg.work_uuid !== item.work_uuid) {
566
- console.log("[scheduleWorkOnCpuPorts] wrong work_uuid in msg msg.work_uuid=", msg.work_uuid, "item.work_uuid=", item.work_uuid);
548
+ log("scheduleWorkOnCpuPorts", [
549
+ "wrong work_uuid in msg msg.work_uuid=",
550
+ msg.work_uuid,
551
+ "item.work_uuid=",
552
+ item.work_uuid,
553
+ ]);
567
554
  throw new Error("[scheduleWorkOnCpuPorts] wrong work_uuid in msg");
568
555
  }
569
556
  if (msg.type === "WORKSTART") {
@@ -571,7 +558,7 @@ export async function scheduleWorkOnCpuPorts(ports, work_buffer) {
571
558
  return;
572
559
  }
573
560
  if (item.status !== "scanwork_in_progress") {
574
- console.log("[scheduleWorkOnCpuPorts] wrong status in msg");
561
+ log("scheduleWorkOnCpuPorts", "wrong status in msg");
575
562
  throw new Error("[scheduleWorkOnCpuPorts] wrong status in msg");
576
563
  }
577
564
  item.result = msg.result;
@@ -581,7 +568,7 @@ export async function scheduleWorkOnCpuPorts(ports, work_buffer) {
581
568
  };
582
569
  port_status.port.onmessage = onmessage;
583
570
  item.status = "scanwork_in_progress";
584
- console.log("[scheduleWorkOnCpuPorts] scheduling work item", item.work_uuid);
571
+ log("scheduleWorkOnCpuPorts", ["scheduling work item", item.work_uuid]);
585
572
  port_status.promise = new Promise((resolve) => {
586
573
  resolve_port = resolve;
587
574
  });
@@ -604,7 +591,11 @@ export async function scheduleWorkOnCpuPorts(ports, work_buffer) {
604
591
  break;
605
592
  }
606
593
  catch {
607
- console.log("[scheduleWorkOnCpuPorts] resend attempt", attempt, item.work_uuid);
594
+ log("scheduleWorkOnCpuPorts", [
595
+ "resend attempt",
596
+ attempt,
597
+ item.work_uuid,
598
+ ]);
608
599
  }
609
600
  }
610
601
  }
@@ -22,7 +22,7 @@ export type ProcessScanResult = {
22
22
  current_range: CacheRange;
23
23
  changed_outputs: ChangedOutput[];
24
24
  };
25
- export declare function processScanResultWITHOUT_SIDE_EFFECTS(params: ProcessScanResultParamsWithoutSideEffects): Promise<ProcessScanResult>;
25
+ export declare function processScanResult(params: ProcessScanResultParamsWithoutSideEffects): Promise<ProcessScanResult>;
26
26
  export declare function processResultReturnValue(cache: ScanCache, params: ProcessScanResultParamsWithoutSideEffects, changed_outputs: ChangedOutput[]): {
27
27
  current_range: CacheRange;
28
28
  changed_outputs: ChangedOutput[];
@@ -1,8 +1,9 @@
1
1
  import { CatastrophicReorgError, } from "../../api";
2
2
  import { computeKeyImage } from "./computeKeyImage";
3
+ import { log } from "../../io/logging";
3
4
  import { mergeRanges, findRange, readCacheFileDefaultLocation, lastRange, writeCacheToFile, findRangeThrows, } from "./scanCache";
4
5
  import {} from "../../node-interaction/binaryEndpoints";
5
- export async function processScanResultWITHOUT_SIDE_EFFECTS(params) {
6
+ export async function processScanResult(params) {
6
7
  const { result, scanCache: cache, secret_spend_key } = params;
7
8
  let changed_outputs = [];
8
9
  if (!(result && "primary_address" in result))
@@ -25,7 +26,10 @@ export async function processScanResultWITHOUT_SIDE_EFFECTS(params) {
25
26
  throw new CatastrophicReorgError("could not locate anchors, should not try to processResult on stale workItems");
26
27
  const split_height = result.block_infos[split_height_index];
27
28
  // we found the split height & do the reorg
28
- console.log("[handleReorg] SPLIT FOUND at height", split_height.block_height);
29
+ log("processScanResult", [
30
+ "SPLIT FOUND at height",
31
+ split_height.block_height,
32
+ ]);
29
33
  if (!cache.reorg_info) {
30
34
  cache.reorg_info = {
31
35
  split_heights: [split_height],
@@ -41,7 +45,12 @@ export async function processScanResultWITHOUT_SIDE_EFFECTS(params) {
41
45
  const reverted_outputs = Object.entries(cache.outputs).filter(([id, output]) => output.spent_block_height !== undefined &&
42
46
  output.spent_block_height >= split_height.block_height);
43
47
  const removed_outputs = Object.entries(cache.outputs).filter(([id, output]) => output.block_height >= split_height.block_height);
44
- console.log("[handleReorg] removed_outputs count:", removed_outputs.length, "reverted count:", reverted_outputs.length);
48
+ log("processScanResult", [
49
+ "removed_outputs count:",
50
+ removed_outputs.length,
51
+ "reverted count:",
52
+ reverted_outputs.length,
53
+ ]);
45
54
  for (const [id, old_output_state] of removed_outputs) {
46
55
  // 1. find key_image of output to be removed (as it was reorged)
47
56
  const [key_image] = Object.entries(cache.own_key_images).find(([own_key_image, globalid]) => globalid === id) || [""]; // if this is viewonly the key_image will be empty