@spirobel/monero-wallet-api 0.3.18 → 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.
Files changed (51) hide show
  1. package/dist/api.d.ts +5 -2
  2. package/dist/api.js +5 -1
  3. package/dist/io/atomicWrite.js +3 -1
  4. package/dist/io/indexedDB.js +7 -2
  5. package/dist/io/logging.d.ts +40 -0
  6. package/dist/io/logging.js +172 -0
  7. package/dist/keypairs-seeds/keypairs.d.ts +21 -0
  8. package/dist/keypairs-seeds/keypairs.js +21 -0
  9. package/dist/node-interaction/binaryEndpoints.d.ts +31 -2
  10. package/dist/node-interaction/binaryEndpoints.js +61 -43
  11. package/dist/node-interaction/jsonEndpoints.d.ts +257 -130
  12. package/dist/node-interaction/jsonEndpoints.js +159 -119
  13. package/dist/node-interaction/nodeUrl.d.ts +21 -6
  14. package/dist/node-interaction/nodeUrl.js +50 -8
  15. package/dist/scanning-syncing/backgroundWorker.d.ts +10 -6
  16. package/dist/scanning-syncing/backgroundWorker.js +88 -39
  17. package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.d.ts +44 -0
  18. package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +355 -0
  19. package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.d.ts +15 -0
  20. package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +28 -0
  21. package/dist/scanning-syncing/{scanresult → blocksbuffer}/getBlocksbinBuffer.d.ts +5 -5
  22. package/dist/scanning-syncing/{scanresult → blocksbuffer}/getBlocksbinBuffer.js +7 -5
  23. package/dist/scanning-syncing/connectionStatus.d.ts +33 -9
  24. package/dist/scanning-syncing/connectionStatus.js +89 -19
  25. package/dist/scanning-syncing/scanSettings.d.ts +21 -0
  26. package/dist/scanning-syncing/scanSettings.js +29 -0
  27. package/dist/scanning-syncing/scanresult/reorg.d.ts +0 -11
  28. package/dist/scanning-syncing/scanresult/reorg.js +39 -16
  29. package/dist/scanning-syncing/scanresult/scanCache.d.ts +16 -2
  30. package/dist/scanning-syncing/scanresult/scanCache.js +65 -5
  31. package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +18 -0
  32. package/dist/scanning-syncing/scanresult/scanCacheOpened.js +56 -11
  33. package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +127 -0
  34. package/dist/scanning-syncing/scanresult/scanCoordination.js +602 -0
  35. package/dist/scanning-syncing/scanresult/scanLoop.d.ts +20 -0
  36. package/dist/scanning-syncing/scanresult/scanLoop.js +116 -0
  37. package/dist/scanning-syncing/scanresult/scanResult.d.ts +24 -3
  38. package/dist/scanning-syncing/scanresult/scanResult.js +159 -97
  39. package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
  40. package/dist/scanning-syncing/worker-mains/coordinator-main.d.ts +1 -0
  41. package/dist/scanning-syncing/worker-mains/coordinator-main.js +24 -0
  42. package/dist/scanning-syncing/worker-mains/cpubound-main.d.ts +5 -0
  43. package/dist/scanning-syncing/worker-mains/cpubound-main.js +122 -0
  44. package/dist/scanning-syncing/worker-mains/worker.d.ts +1 -1
  45. package/dist/scanning-syncing/worker-mains/worker.js +62 -6
  46. package/dist/viewpair/ViewPair.d.ts +30 -7
  47. package/dist/viewpair/ViewPair.js +56 -118
  48. package/dist/wasm-processing/wasmFile.js +1 -1
  49. package/package.json +22 -24
  50. package/dist/io/extension.d.ts +0 -18
  51. package/dist/io/extension.js +0 -11
@@ -1,51 +1,95 @@
1
- import { z } from "zod";
2
- export const GetInfoResponseSchema = z.object({
3
- id: z.string(),
4
- jsonrpc: z.literal("2.0"),
5
- result: z.object({
6
- adjusted_time: z.number(),
7
- alt_blocks_count: z.number(),
8
- block_size_limit: z.number(),
9
- block_size_median: z.number(),
10
- block_weight_limit: z.number(),
11
- block_weight_median: z.number(),
12
- bootstrap_daemon_address: z.string(),
13
- busy_syncing: z.boolean(),
14
- credits: z.number(),
15
- cumulative_difficulty: z.number(),
16
- cumulative_difficulty_top64: z.number(),
17
- database_size: z.number(),
18
- difficulty: z.number(),
19
- difficulty_top64: z.number(),
20
- free_space: z.number(),
21
- grey_peerlist_size: z.number(),
22
- height: z.number(),
23
- height_without_bootstrap: z.number(),
24
- incoming_connections_count: z.number(),
25
- mainnet: z.boolean(),
26
- nettype: z.string(),
27
- offline: z.boolean(),
28
- outgoing_connections_count: z.number(),
29
- restricted: z.boolean(),
30
- rpc_connections_count: z.number(),
31
- stagenet: z.boolean(),
32
- start_time: z.number(),
33
- status: z.string(),
34
- synchronized: z.boolean(),
35
- target: z.number(),
36
- target_height: z.number(),
37
- testnet: z.boolean(),
38
- top_block_hash: z.string(),
39
- top_hash: z.string(),
40
- tx_count: z.number(),
41
- tx_pool_size: z.number(),
42
- untrusted: z.boolean(),
43
- update_available: z.boolean(),
44
- version: z.string(),
45
- was_bootstrap_ever_used: z.boolean(),
46
- white_peerlist_size: z.number(),
47
- wide_cumulative_difficulty: z.string(),
48
- wide_difficulty: z.string(),
1
+ function obj(s) {
2
+ function p(d) {
3
+ if (typeof d !== "object" || d === null)
4
+ throw 0;
5
+ const r = {};
6
+ for (const k in s)
7
+ r[k] = s[k](d[k]);
8
+ return r;
9
+ }
10
+ p.safeParse = (d) => {
11
+ try {
12
+ return { success: true, data: p(d) };
13
+ }
14
+ catch {
15
+ return { success: false, error: new Error() };
16
+ }
17
+ };
18
+ return p;
19
+ }
20
+ const str = (x) => {
21
+ if (typeof x !== "string")
22
+ throw 0;
23
+ return x;
24
+ };
25
+ const num = (x) => {
26
+ if (typeof x !== "number")
27
+ throw 0;
28
+ return x;
29
+ };
30
+ const bool = (x) => {
31
+ if (typeof x !== "boolean")
32
+ throw 0;
33
+ return x;
34
+ };
35
+ const lit = (v) => (x) => {
36
+ if (x !== v)
37
+ throw 0;
38
+ return v;
39
+ };
40
+ const arr = (f) => (x) => {
41
+ if (!Array.isArray(x))
42
+ throw 0;
43
+ return x.map(f);
44
+ };
45
+ const opt = (f) => (x) => x === undefined ? undefined : f(x);
46
+ export const GetInfoResponseSchema = obj({
47
+ id: str,
48
+ jsonrpc: lit("2.0"),
49
+ result: obj({
50
+ adjusted_time: num,
51
+ alt_blocks_count: num,
52
+ block_size_limit: num,
53
+ block_size_median: num,
54
+ block_weight_limit: num,
55
+ block_weight_median: num,
56
+ bootstrap_daemon_address: str,
57
+ busy_syncing: bool,
58
+ credits: num,
59
+ cumulative_difficulty: num,
60
+ cumulative_difficulty_top64: num,
61
+ database_size: num,
62
+ difficulty: num,
63
+ difficulty_top64: num,
64
+ free_space: num,
65
+ grey_peerlist_size: num,
66
+ height: num,
67
+ height_without_bootstrap: num,
68
+ incoming_connections_count: num,
69
+ mainnet: bool,
70
+ nettype: str,
71
+ offline: bool,
72
+ outgoing_connections_count: num,
73
+ restricted: bool,
74
+ rpc_connections_count: num,
75
+ stagenet: bool,
76
+ start_time: num,
77
+ status: str,
78
+ synchronized: bool,
79
+ target: num,
80
+ target_height: num,
81
+ testnet: bool,
82
+ top_block_hash: str,
83
+ top_hash: str,
84
+ tx_count: num,
85
+ tx_pool_size: num,
86
+ untrusted: bool,
87
+ update_available: bool,
88
+ version: str,
89
+ was_bootstrap_ever_used: bool,
90
+ white_peerlist_size: num,
91
+ wide_cumulative_difficulty: str,
92
+ wide_difficulty: str,
49
93
  }),
50
94
  });
51
95
  /**
@@ -61,17 +105,17 @@ export const GetInfoResponseSchema = z.object({
61
105
  * - start_height: unsigned int; Note that this is not necessarily equal to from_height, especially for amount=0 where start_height will be no less than the height of the v4 hardfork.
62
106
  * - status: string; General RPC error code. "OK" means everything looks good.
63
107
  */
64
- export const GetOutputDistributionResponseSchema = z.object({
65
- id: z.string(),
66
- jsonrpc: z.literal("2.0"),
67
- result: z.object({
68
- distributions: z.array(z.object({
69
- amount: z.number(),
70
- base: z.number(),
71
- distribution: z.array(z.number()),
72
- start_height: z.number(),
108
+ export const GetOutputDistributionResponseSchema = obj({
109
+ id: str,
110
+ jsonrpc: lit("2.0"),
111
+ result: obj({
112
+ distributions: arr(obj({
113
+ amount: num,
114
+ base: num,
115
+ distribution: arr(num),
116
+ start_height: num,
73
117
  })),
74
- status: z.string(),
118
+ status: str,
75
119
  }),
76
120
  });
77
121
  export function parseGetOutputDistributionResponse(data) {
@@ -154,14 +198,14 @@ export async function get_output_distribution(NODE_URL, params = {
154
198
  * - fees: (Optional) Array of unsigned int; Fee estimates for priorities 1–4.
155
199
  * - quantization_mask: unsigned int; Mask used for fee rounding.
156
200
  */
157
- export const GetFeeEstimateResponseSchema = z.object({
158
- id: z.string(),
159
- jsonrpc: z.literal("2.0"),
160
- result: z.object({
161
- status: z.string(),
162
- fee: z.number(),
163
- fees: z.array(z.number()).optional(),
164
- quantization_mask: z.number(),
201
+ export const GetFeeEstimateResponseSchema = obj({
202
+ id: str,
203
+ jsonrpc: lit("2.0"),
204
+ result: obj({
205
+ status: str,
206
+ fee: num,
207
+ fees: opt(arr(num)),
208
+ quantization_mask: num,
165
209
  }),
166
210
  });
167
211
  export function parseGetFeeEstimateResponse(data) {
@@ -199,19 +243,19 @@ export async function get_fee_estimate(NODE_URL) {
199
243
  }
200
244
  return parsedResult.result;
201
245
  }
202
- export const SendRawTransactionResponseSchema = z.object({
203
- double_spend: z.boolean(), // double_spend - boolean; Transaction is a double spend (true) or not (false).
204
- fee_too_low: z.boolean(), // fee_too_low - boolean; Fee is too low (true) or OK (false).
205
- invalid_input: z.boolean(), // invalid_input - boolean; Input is invalid (true) or valid (false).
206
- invalid_output: z.boolean(), // invalid_output - boolean; Output is invalid (true) or valid (false).
207
- low_mixin: z.boolean(), // low_mixin - boolean; Mixin count is too low (true) or OK (false).
208
- not_rct: z.boolean().optional(), // not_rct - boolean; Transaction is a standard ring transaction (true) or a ring confidential transaction (false).
209
- not_relayed: z.boolean(), // not_relayed - boolean; Transaction was not relayed (true) or relayed (false).
210
- overspend: z.boolean(), // overspend - boolean; Transaction uses more money than available (true) or not (false).
211
- reason: z.string(), // reason - string; Additional information. Currently empty or "Not relayed" if transaction was accepted but not relayed.
212
- status: z.string(), // status - string; General RPC error code. "OK" means everything looks good. Any other value means that something went wrong.
213
- too_big: z.boolean(), // too_big - boolean; Transaction size is too big (true) or OK (false).
214
- untrusted: z.boolean(), // untrusted - boolean; States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced and thus handles the RPC locally (false)
246
+ export const SendRawTransactionResponseSchema = obj({
247
+ double_spend: bool, // double_spend - boolean; Transaction is a double spend (true) or not (false).
248
+ fee_too_low: bool, // fee_too_low - boolean; Fee is too low (true) or OK (false).
249
+ invalid_input: bool, // invalid_input - boolean; Input is invalid (true) or valid (false).
250
+ invalid_output: bool, // invalid_output - boolean; Output is invalid (true) or valid (false).
251
+ low_mixin: bool, // low_mixin - boolean; Mixin count is too low (true) or OK (false).
252
+ not_rct: opt(bool), // not_rct - boolean; Transaction is a standard ring transaction (true) or a ring confidential transaction (false).
253
+ not_relayed: bool, // not_relayed - boolean; Transaction was not relayed (true) or relayed (false).
254
+ overspend: bool, // overspend - boolean; Transaction uses more money than available (true) or not (false).
255
+ reason: str, // reason - string; Additional information. Currently empty or "Not relayed" if transaction was accepted but not relayed.
256
+ status: str, // status - string; General RPC error code. "OK" means everything looks good. Any other value means that something went wrong.
257
+ too_big: bool, // too_big - boolean; Transaction size is too big (true) or OK (false).
258
+ untrusted: bool, // untrusted - boolean; States if the result is obtained using the bootstrap mode, and is therefore not trusted (true), or when the daemon is fully synced and thus handles the RPC locally (false)
215
259
  });
216
260
  export function parseSendRawTransactionResponse(data) {
217
261
  const result = SendRawTransactionResponseSchema.safeParse(data);
@@ -279,47 +323,43 @@ do_not_relay = false) {
279
323
  * - code: int; Error code.
280
324
  * - message: string; Error message.
281
325
  */
282
- export const GetBlockHeadersRangeResponseSchema = z.object({
283
- id: z.string(),
284
- jsonrpc: z.literal("2.0"),
285
- result: z
286
- .object({
287
- credits: z.number(),
288
- headers: z.array(z.object({
289
- block_size: z.number(),
290
- block_weight: z.number(),
291
- cumulative_difficulty: z.number(),
292
- cumulative_difficulty_top64: z.number(),
293
- depth: z.number(),
294
- difficulty: z.number(),
295
- difficulty_top64: z.number(),
296
- hash: z.string(),
297
- height: z.number(),
298
- long_term_weight: z.number(),
299
- major_version: z.number(),
300
- miner_tx_hash: z.string(),
301
- minor_version: z.number(),
302
- nonce: z.number(),
303
- num_txes: z.number(),
304
- orphan_status: z.boolean(),
305
- pow_hash: z.string(),
306
- prev_hash: z.string(),
307
- reward: z.number(),
308
- timestamp: z.number(),
309
- wide_cumulative_difficulty: z.string(),
310
- wide_difficulty: z.string(),
326
+ export const GetBlockHeadersRangeResponseSchema = obj({
327
+ id: str,
328
+ jsonrpc: lit("2.0"),
329
+ result: opt(obj({
330
+ credits: num,
331
+ headers: arr(obj({
332
+ block_size: num,
333
+ block_weight: num,
334
+ cumulative_difficulty: num,
335
+ cumulative_difficulty_top64: num,
336
+ depth: num,
337
+ difficulty: num,
338
+ difficulty_top64: num,
339
+ hash: str,
340
+ height: num,
341
+ long_term_weight: num,
342
+ major_version: num,
343
+ miner_tx_hash: str,
344
+ minor_version: num,
345
+ nonce: num,
346
+ num_txes: num,
347
+ orphan_status: bool,
348
+ pow_hash: str,
349
+ prev_hash: str,
350
+ reward: num,
351
+ timestamp: num,
352
+ wide_cumulative_difficulty: str,
353
+ wide_difficulty: str,
311
354
  })),
312
- status: z.string(),
313
- top_hash: z.string(),
314
- untrusted: z.boolean(),
315
- })
316
- .optional(),
317
- error: z
318
- .object({
319
- code: z.number(),
320
- message: z.string(),
321
- })
322
- .optional(),
355
+ status: str,
356
+ top_hash: str,
357
+ untrusted: bool,
358
+ })),
359
+ error: opt(obj({
360
+ code: num,
361
+ message: str,
362
+ })),
323
363
  });
324
364
  export function parseGetBlockHeadersRangeResponse(data) {
325
365
  const result = GetBlockHeadersRangeResponseSchema.safeParse(data);
@@ -1,5 +1,6 @@
1
- import { type GetBlocksBinRequest, type Output, type GetOutsBinRequest } from "./binaryEndpoints";
1
+ import { type Output, type GetBlocksBinRequest, type GetBlocksResultMeta, type GetOutsBinRequest } from "./binaryEndpoints";
2
2
  import { type FeeEstimateResponse, type GetBlockHeadersRangeParams, type GetOutputDistributionParams, type SendRawTransactionResult } from "./jsonEndpoints";
3
+ export type NETWORKS = "mainnet" | "stagenet" | "testnet";
3
4
  import { type SignedTransaction, type Input } from "../send-functionality/transactionBuilding";
4
5
  import { WasmProcessor } from "../wasm-processing/wasmProcessor";
5
6
  /**
@@ -11,14 +12,27 @@ import { WasmProcessor } from "../wasm-processing/wasmProcessor";
11
12
  export declare class NodeUrl extends WasmProcessor {
12
13
  node_url: string;
13
14
  protected constructor(node_url: string);
15
+ private _network;
16
+ get network(): NETWORKS;
17
+ private _genesis_hash;
18
+ get genesis_hash(): string;
14
19
  static create(node_url?: string): Promise<NodeUrl>;
15
20
  /**
16
- * This request helps making requests to the get_blocks.bin endpoint of the Monerod nodes.
17
- * @link https://docs.getmonero.org/rpc-library/monerod-rpc/#get_blocksbin
18
- * @param params params that will be turned into epee (moner lib that does binary serialization)
19
- * @returns after the request is made it will return epee serialized objects that are then parsed into json.
21
+ * Executes a get_blocks.bin request and returns the raw binary response buffer.
22
+ * @link https://docs.getmonero.org/rpc-library/monerod-rpc/#get_blocksbin
23
+ * @param params params for the get_blocks.bin request
24
+ * @param stopSync optional AbortSignal
25
+ * @returns the raw binary response buffer
26
+ */
27
+ getBlocksBinExecuteRequest(params: GetBlocksBinRequest, stopSync?: AbortSignal): Promise<Uint8Array<ArrayBufferLike>>;
28
+ /**
29
+ * Loads a getBlocks.bin response into the WASM module.
30
+ * Unlike getBlocksBinExecuteRequest, this does not make a request, it parses an already fetched
31
+ * binary response and stores it in WASM memory. Subsequent calls overwrite the stored response.
32
+ * @param getBlocksBinResponseBuffer the raw binary response from the get_blocks.bin endpoint
33
+ * @returns metadata about the loaded blocks (new_height, daemon_height, status, block_infos)
20
34
  */
21
- getBlocksBin(params: GetBlocksBinRequest): Promise<import("./binaryEndpoints").GetBlocksBinResponse | import("./binaryEndpoints").ErrorResponse>;
35
+ loadGetBlocksBinResponse(getBlocksBinResponseBuffer: Uint8Array): Promise<GetBlocksResultMeta>;
22
36
  /**
23
37
  * This request helps making requests to the get_outs.bin endpoint of the Monerod nodes.
24
38
  * @link https://docs.getmonero.org/rpc-library/monerod-rpc/#get_outsbin
@@ -125,5 +139,6 @@ export declare class NodeUrl extends WasmProcessor {
125
139
  wide_cumulative_difficulty: string;
126
140
  wide_difficulty: string;
127
141
  }>;
142
+ addGenesisHashToBlockIds(params: GetBlocksBinRequest): Promise<GetBlocksBinRequest>;
128
143
  }
129
144
  export declare const LOCAL_NODE_DEFAULT_URL = "http://127.0.0.1:18081";
@@ -1,4 +1,4 @@
1
- import { getBlocksBinJson, getOutsBinJson, getOutsBinExecuteRequest, } from "./binaryEndpoints";
1
+ import { getBlocksBinExecuteRequest, loadGetBlocksBinResponse, getOutsBinJson, getOutsBinExecuteRequest, MAINNET_GENESIS_BLOCK_HASH, STAGENET_GENESIS_BLOCK_HASH, } from "./binaryEndpoints";
2
2
  import { get_block_headers_range, get_fee_estimate, get_info, get_output_distribution, send_raw_transaction, } from "./jsonEndpoints";
3
3
  import { makeInput, sampleDecoys, } from "../send-functionality/transactionBuilding";
4
4
  import { WasmProcessor } from "../wasm-processing/wasmProcessor";
@@ -14,19 +14,41 @@ export class NodeUrl extends WasmProcessor {
14
14
  super();
15
15
  this.node_url = node_url;
16
16
  }
17
+ _network;
18
+ get network() {
19
+ return this._network; // we set this in ViewPair.create()
20
+ }
21
+ _genesis_hash;
22
+ get genesis_hash() {
23
+ if (!this._genesis_hash) {
24
+ throw new Error("Genesis hash not set. Node not connected?");
25
+ }
26
+ return this._genesis_hash; // set in first call to ViewPair.getBlocksBin, if params.block_ids is supplied
27
+ }
17
28
  static async create(node_url) {
18
29
  const nodeUrl = new NodeUrl(node_url || LOCAL_NODE_DEFAULT_URL);
19
30
  await nodeUrl.initWasmModule();
20
31
  return nodeUrl;
21
32
  }
22
33
  /**
23
- * This request helps making requests to the get_blocks.bin endpoint of the Monerod nodes.
24
- * @link https://docs.getmonero.org/rpc-library/monerod-rpc/#get_blocksbin
25
- * @param params params that will be turned into epee (moner lib that does binary serialization)
26
- * @returns after the request is made it will return epee serialized objects that are then parsed into json.
34
+ * Executes a get_blocks.bin request and returns the raw binary response buffer.
35
+ * @link https://docs.getmonero.org/rpc-library/monerod-rpc/#get_blocksbin
36
+ * @param params params for the get_blocks.bin request
37
+ * @param stopSync optional AbortSignal
38
+ * @returns the raw binary response buffer
27
39
  */
28
- getBlocksBin(params) {
29
- return getBlocksBinJson(this, params);
40
+ async getBlocksBinExecuteRequest(params, stopSync) {
41
+ return await getBlocksBinExecuteRequest(this, await this.addGenesisHashToBlockIds(params), stopSync);
42
+ }
43
+ /**
44
+ * Loads a getBlocks.bin response into the WASM module.
45
+ * Unlike getBlocksBinExecuteRequest, this does not make a request, it parses an already fetched
46
+ * binary response and stores it in WASM memory. Subsequent calls overwrite the stored response.
47
+ * @param getBlocksBinResponseBuffer the raw binary response from the get_blocks.bin endpoint
48
+ * @returns metadata about the loaded blocks (new_height, daemon_height, status, block_infos)
49
+ */
50
+ loadGetBlocksBinResponse(getBlocksBinResponseBuffer) {
51
+ return loadGetBlocksBinResponse(this, getBlocksBinResponseBuffer);
30
52
  }
31
53
  /**
32
54
  * This request helps making requests to the get_outs.bin endpoint of the Monerod nodes.
@@ -99,7 +121,7 @@ export class NodeUrl extends WasmProcessor {
99
121
  * @returns The result object with headers, status, etc. Throws if the range is invalid:(end_height > daemonheight)
100
122
  */
101
123
  async getBlockHeadersRange(params) {
102
- return get_block_headers_range(this.node_url, params);
124
+ return await get_block_headers_range(this.node_url, params);
103
125
  }
104
126
  /**
105
127
  * Fetch general information about the Monero daemon.
@@ -109,5 +131,25 @@ export class NodeUrl extends WasmProcessor {
109
131
  async getInfo() {
110
132
  return get_info(this.node_url);
111
133
  }
134
+ async addGenesisHashToBlockIds(params) {
135
+ if (params.block_ids) {
136
+ if (!this._genesis_hash && this.network === "mainnet") {
137
+ this._genesis_hash = MAINNET_GENESIS_BLOCK_HASH;
138
+ }
139
+ if (!this._genesis_hash && this.network === "stagenet") {
140
+ this._genesis_hash = STAGENET_GENESIS_BLOCK_HASH;
141
+ }
142
+ if (!this._genesis_hash) {
143
+ // TESTNET
144
+ const range = await this.getBlockHeadersRange({
145
+ start_height: 0,
146
+ end_height: 0,
147
+ });
148
+ this._genesis_hash = range.headers[0].hash;
149
+ }
150
+ params.block_ids.push(this.genesis_hash);
151
+ }
152
+ return params;
153
+ }
112
154
  }
113
155
  export const LOCAL_NODE_DEFAULT_URL = "http://127.0.0.1:18081";
@@ -1,6 +1,10 @@
1
- import { type CacheChangedCallback, type CacheChangedCallbackParameters } from "./scanresult/scanCache";
2
- export declare function scanWallets(cacheChanged?: CacheChangedCallback, stopSync?: AbortSignal, scan_settings_path?: string, pathPrefix?: string): Promise<void>;
3
- export declare function createWebworker(handle_result?: (result: CacheChangedCallbackParameters) => void, scan_settings_path?: string, pathPrefix?: string, handle_error?: (error: unknown) => void): Promise<Worker | undefined>;
4
- export declare function makeWebworkerScript(scan_settings_path?: string, pathPrefix?: string): string;
5
- export declare function startWebworker(worker_script: string, handle_result?: (result: unknown) => void, handle_error?: (error: unknown) => void): Worker;
6
- export declare const WW_ERROR_FORWARDING_SNIPPET = "\n\nself.onerror = (e) => self.postMessage({ type: 'ERROR', payload: e });\nself.addEventListener('unhandledrejection', (e) => \n self.postMessage({ type: 'ERROR', payload: e.reason })\n); \n";
1
+ import { type CacheChangedCallbackParameters } from "./scanresult/scanCache";
2
+ export declare const CPU_POOL_SIZE = 4;
3
+ export type WorkerSet = {
4
+ fetchWorker: Worker;
5
+ cpuWorkers: Worker[];
6
+ terminate: () => void;
7
+ };
8
+ export declare function createWebworker(handle_result?: (result: CacheChangedCallbackParameters) => void, scan_settings_path?: string, pathPrefix?: string, handle_error?: (error: unknown) => void): Promise<WorkerSet | undefined>;
9
+ export declare function makeWebworkerScript(): string;
10
+ export declare function startWebworker(handle_result?: (result: unknown) => void, handle_error?: (error: unknown) => void): Worker;
@@ -1,66 +1,115 @@
1
- import { get_info, ViewPair } from "../api";
2
1
  import { LOCAL_NODE_DEFAULT_URL } from "../node-interaction/nodeUrl";
3
2
  import {} from "./scanresult/scanCache";
4
- import { openNonHaltedWallets, readNodeUrlFromScanSettings, walletSettingsPlusKeys, } from "./scanSettings";
3
+ import { openScanSettingsFile, SCAN_SETTINGS_STORE_NAME_DEFAULT, } from "./scanSettings";
4
+ import { log, setupLoggingPath } from "../io/logging";
5
5
  import { workerMainCode } from "./worker-entrypoints/worker";
6
- export async function scanWallets(cacheChanged = (params) => console.log(params), stopSync, scan_settings_path, pathPrefix) {
7
- const nonHaltedWallets = await openNonHaltedWallets(scan_settings_path);
8
- const masterWalletSettings = nonHaltedWallets[0];
9
- if (!masterWalletSettings)
10
- return;
11
- const masterWithKeys = await walletSettingsPlusKeys(masterWalletSettings);
12
- const masterViewPair = await ViewPair.create(masterWalletSettings.primary_address, masterWithKeys.secret_view_key, masterWalletSettings.subaddress_index, masterWalletSettings.node_url);
13
- await masterViewPair.scan(cacheChanged, stopSync, scan_settings_path, pathPrefix);
14
- }
6
+ export const CPU_POOL_SIZE = 4;
15
7
  export async function createWebworker(handle_result, scan_settings_path, pathPrefix, handle_error) {
16
8
  try {
17
- const node_url = (await readNodeUrlFromScanSettings(scan_settings_path)) ||
18
- LOCAL_NODE_DEFAULT_URL;
19
- // check if the node connection works before starting the sync worker
20
- const getInfo = await get_info(node_url);
21
- const worker_script = makeWebworkerScript(scan_settings_path, pathPrefix);
22
- return startWebworker(worker_script, handle_result, handle_error);
9
+ const resolvedPath = scan_settings_path || SCAN_SETTINGS_STORE_NAME_DEFAULT;
10
+ const scanSettings = await openScanSettingsFile(resolvedPath);
11
+ await setupLoggingPath(resolvedPath, pathPrefix ?? "", "mainthread");
12
+ const node_url = scanSettings?.node_url || LOCAL_NODE_DEFAULT_URL;
13
+ const cpu_worker_count = typeof scanSettings?.cpu_worker_count !== "undefined"
14
+ ? scanSettings?.cpu_worker_count
15
+ : CPU_POOL_SIZE;
16
+ // spawn CPU workers
17
+ const cpuWorkers = [];
18
+ const cpuPorts = [];
19
+ for (let i = 0; i < cpu_worker_count; i++) {
20
+ const channel = new MessageChannel();
21
+ const cpuWorker = await startWebworkerReady();
22
+ cpuWorker.postMessage({
23
+ type: "setup",
24
+ role: "cpubound",
25
+ cpu_worker_id: i,
26
+ scan_settings_path: resolvedPath,
27
+ pathPrefix,
28
+ }, [channel.port1]);
29
+ cpuWorker.onerror = (e) => {
30
+ if (handle_error)
31
+ handle_error("cpu worker " + i + " error: " + (e.message ?? String(e)));
32
+ };
33
+ cpuWorkers.push(cpuWorker);
34
+ cpuPorts.push(channel.port2);
35
+ }
36
+ const coordinationWorker = await startWebworkerReady();
37
+ coordinationWorker.postMessage({
38
+ type: "setup",
39
+ scan_settings_path: resolvedPath,
40
+ pathPrefix,
41
+ role: "coordinator",
42
+ node_url,
43
+ start_height: 0,
44
+ }, cpuPorts); // transfer CPU port2s to coordination worker
45
+ log("createWebworker", [
46
+ "coordinator worker started, node_url=" + node_url,
47
+ ", cpuPorts=" + cpuPorts.length,
48
+ ]);
49
+ coordinationWorker.onmessage = (event) => {
50
+ if (event.data.type === "ERROR") {
51
+ if (handle_error)
52
+ handle_error(event.data.payload);
53
+ }
54
+ else if (event.data.type === "SCAN_RESULT") {
55
+ if (handle_result)
56
+ handle_result(event.data.payload);
57
+ }
58
+ };
59
+ return {
60
+ fetchWorker: coordinationWorker,
61
+ cpuWorkers,
62
+ terminate: () => {
63
+ coordinationWorker.terminate();
64
+ for (const w of cpuWorkers)
65
+ w.terminate();
66
+ },
67
+ };
23
68
  }
24
69
  catch (error) {
25
70
  handle_error?.(error);
26
71
  }
27
72
  }
28
- export function makeWebworkerScript(scan_settings_path, pathPrefix) {
29
- const settingsPathLine = scan_settings_path
30
- ? `const scan_settings_path = '${scan_settings_path}';`
31
- : "const scan_settings_path = undefined;";
32
- const pathPrefixLine = pathPrefix
33
- ? `const pathPrefix = '${pathPrefix}';`
34
- : "const pathPrefix = undefined;";
35
- return `\n
36
- ${settingsPathLine}
37
- ${pathPrefixLine}
38
- ${workerMainCode}
39
- `;
73
+ export function makeWebworkerScript() {
74
+ return workerMainCode;
40
75
  }
41
- export function startWebworker(worker_script, handle_result, handle_error) {
42
- const blob = new Blob([WW_ERROR_FORWARDING_SNIPPET + worker_script], {
76
+ export function startWebworker(handle_result, handle_error) {
77
+ const blob = new Blob([workerMainCode], {
43
78
  type: "text/javascript",
44
79
  });
45
80
  const url = URL.createObjectURL(blob);
46
81
  const worker = new Worker(url, { type: "module" });
47
82
  worker.onmessage = (event) => {
48
83
  switch (event.data.type) {
49
- case "RESULT": // Handle normal messages
84
+ case "RESULT":
50
85
  if (handle_result)
51
86
  handle_result(event.data.payload);
52
87
  break;
53
88
  case "ERROR":
89
+ case "scan_error":
54
90
  if (handle_error)
55
- handle_error(event.data.payload);
91
+ handle_error(event.data.payload ?? event.data.error);
92
+ break;
93
+ case "DEBUG":
94
+ log("startWebworker", ["worker debug:", event.data.payload]);
56
95
  break;
57
96
  }
58
97
  };
59
98
  return worker;
60
99
  }
61
- // Autoforward ALL errors with error type (global handlers)
62
- export const WW_ERROR_FORWARDING_SNIPPET = `\n
63
- self.onerror = (e) => self.postMessage({ type: 'ERROR', payload: e });
64
- self.addEventListener('unhandledrejection', (e) =>
65
- self.postMessage({ type: 'ERROR', payload: e.reason })
66
- ); \n`;
100
+ // creates a worker via startWebworker, then waits for it to signal
101
+ // WORKER_READY (meaning self.onmessage = handleMessage has executed).
102
+ // after that, postMessage is safe, no race between worker startup
103
+ // and message delivery.
104
+ function startWebworkerReady() {
105
+ const worker = startWebworker();
106
+ return new Promise((resolve) => {
107
+ const onReady = (e) => {
108
+ if (e.data?.type === "WORKER_READY") {
109
+ worker.removeEventListener("message", onReady);
110
+ resolve(worker);
111
+ }
112
+ };
113
+ worker.addEventListener("message", onReady);
114
+ });
115
+ }