@spirobel/monero-wallet-api 0.3.16 → 0.4.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/api.d.ts +5 -2
- package/dist/api.js +5 -1
- package/dist/io/atomicWrite.js +2 -1
- package/dist/io/indexedDB.js +7 -2
- package/dist/keypairs-seeds/keypairs.d.ts +21 -0
- package/dist/keypairs-seeds/keypairs.js +21 -0
- package/dist/node-interaction/binaryEndpoints.d.ts +31 -2
- package/dist/node-interaction/binaryEndpoints.js +61 -43
- package/dist/node-interaction/jsonEndpoints.d.ts +257 -130
- package/dist/node-interaction/jsonEndpoints.js +159 -119
- package/dist/node-interaction/nodeUrl.d.ts +21 -6
- package/dist/node-interaction/nodeUrl.js +50 -8
- package/dist/scanning-syncing/backgroundWorker.d.ts +9 -6
- package/dist/scanning-syncing/backgroundWorker.js +76 -39
- package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.d.ts +44 -0
- package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +353 -0
- package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.d.ts +15 -0
- package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +27 -0
- package/dist/scanning-syncing/{scanresult → blocksbuffer}/getBlocksbinBuffer.d.ts +5 -5
- package/dist/scanning-syncing/{scanresult → blocksbuffer}/getBlocksbinBuffer.js +7 -5
- package/dist/scanning-syncing/connectionStatus.d.ts +33 -9
- package/dist/scanning-syncing/connectionStatus.js +89 -19
- package/dist/scanning-syncing/scanSettings.d.ts +45 -0
- package/dist/scanning-syncing/scanSettings.js +63 -0
- package/dist/scanning-syncing/scanresult/reorg.d.ts +0 -11
- package/dist/scanning-syncing/scanresult/reorg.js +39 -16
- package/dist/scanning-syncing/scanresult/scanCache.d.ts +16 -2
- package/dist/scanning-syncing/scanresult/scanCache.js +58 -2
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +18 -0
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +56 -11
- package/dist/scanning-syncing/scanresult/scanCoordination.d.ts +127 -0
- package/dist/scanning-syncing/scanresult/scanCoordination.js +611 -0
- package/dist/scanning-syncing/scanresult/scanLoop.d.ts +20 -0
- package/dist/scanning-syncing/scanresult/scanLoop.js +116 -0
- package/dist/scanning-syncing/scanresult/scanResult.d.ts +24 -3
- package/dist/scanning-syncing/scanresult/scanResult.js +151 -98
- 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 +23 -0
- package/dist/scanning-syncing/worker-mains/cpubound-main.d.ts +5 -0
- package/dist/scanning-syncing/worker-mains/cpubound-main.js +112 -0
- package/dist/scanning-syncing/worker-mains/worker.d.ts +1 -1
- package/dist/scanning-syncing/worker-mains/worker.js +58 -6
- package/dist/viewpair/ViewPair.d.ts +30 -7
- package/dist/viewpair/ViewPair.js +56 -118
- package/dist/wasm-processing/wasmFile.js +1 -1
- package/package.json +22 -24
- package/dist/io/extension.d.ts +0 -18
- package/dist/io/extension.js +0 -11
|
@@ -5,7 +5,7 @@ import { spendable } from "./scanResult";
|
|
|
5
5
|
import { openScanSettingsFile, readPrivateSpendKeyFromEnv, readWalletFromScanSettings, SCAN_SETTINGS_STORE_NAME_DEFAULT, walletSettingsPlusKeys, writeNodeUrlToScanSettings, writeStartHeightToScanSettings, writeWalletToScanSettings, } from "../scanSettings";
|
|
6
6
|
import { findRange, lastRange, readCacheFileDefaultLocation, writeCacheFileDefaultLocationThrows, } from "./scanCache";
|
|
7
7
|
import { alignScanStatsWithCache, isSelfSpent, processTxlogInputs, processTxlogPayments, writeStatsFileDefaultLocation, } from "./scanStats";
|
|
8
|
-
import { readWriteConnectionStatusFile, } from "../connectionStatus";
|
|
8
|
+
import { updateSyncETA, readWriteConnectionStatusFile, } from "../connectionStatus";
|
|
9
9
|
export class ScanCacheOpened {
|
|
10
10
|
view_pair;
|
|
11
11
|
wallet_route;
|
|
@@ -17,6 +17,22 @@ export class ScanCacheOpened {
|
|
|
17
17
|
scan_settings_path;
|
|
18
18
|
pathPrefix;
|
|
19
19
|
workerError;
|
|
20
|
+
/** how many decoys to sample per input (default 20, ring size is 11) */
|
|
21
|
+
decoySampleCount = 20;
|
|
22
|
+
/**
|
|
23
|
+
* when true, retry makeInput with higher sample counts on failure.
|
|
24
|
+
*
|
|
25
|
+
* PRIVACY WARNING: retrying contacts the node multiple times for the same
|
|
26
|
+
* input, each time with a different set of candidate indices. this lets the
|
|
27
|
+
* node correlate which output is the real spend across the retries.
|
|
28
|
+
* only ever enable this on your own local trusted node, never on a remote
|
|
29
|
+
* public node.
|
|
30
|
+
*
|
|
31
|
+
* defaults to false, on failure the original error propagates.
|
|
32
|
+
*/
|
|
33
|
+
decoyRetry = false;
|
|
34
|
+
/** sample sizes to try when decoyRetry is enabled, in order */
|
|
35
|
+
decoyRetrySizes = [20, 50, 100, 200, 500];
|
|
20
36
|
static async create(params) {
|
|
21
37
|
const theCatchToBeOpened = await readCacheFileDefaultLocation(params.primary_address, params.pathPrefix);
|
|
22
38
|
const walletSettings = await readWalletFromScanSettings(params.primary_address, params.scan_settings_path);
|
|
@@ -193,16 +209,30 @@ export class ScanCacheOpened {
|
|
|
193
209
|
// 4. get output distribution
|
|
194
210
|
const node = await NodeUrl.create(this.node_url);
|
|
195
211
|
const distibution = await node.getOutputDistribution();
|
|
196
|
-
const
|
|
212
|
+
const inputs = [];
|
|
197
213
|
for (const input of selectedInputs) {
|
|
198
214
|
// 5. sample decoys & get outs from node: here is where a privacy compromising event could happen
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
215
|
+
const sizesToTry = this.decoyRetry
|
|
216
|
+
? this.decoyRetrySizes
|
|
217
|
+
: [this.decoySampleCount];
|
|
218
|
+
let madeInput;
|
|
219
|
+
for (const size of sizesToTry) {
|
|
220
|
+
if (madeInput)
|
|
221
|
+
break;
|
|
222
|
+
try {
|
|
223
|
+
const prepared = prepareInput(node, distibution, input, size);
|
|
224
|
+
const wasmInput = node.makeInput(prepared.input, prepared.sample.candidates, await prepared.outsResponse);
|
|
225
|
+
madeInput = wasmInput;
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
if (size === sizesToTry[sizesToTry.length - 1])
|
|
229
|
+
throw e;
|
|
230
|
+
// fall through to next size
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (!madeInput)
|
|
234
|
+
throw new Error("failed to make input");
|
|
235
|
+
inputs.push(madeInput);
|
|
206
236
|
}
|
|
207
237
|
// 7. make transaction: combine inputs, payments + fee info
|
|
208
238
|
const unsignedTx = this.view_pair.makeTransaction({
|
|
@@ -429,10 +459,12 @@ export class ScanCacheOpened {
|
|
|
429
459
|
if (!this.worker && !this.no_worker) {
|
|
430
460
|
this.worker = await createWebworker(async (params) => await this.feed(params), this.scan_settings_path, this.pathPrefix, (error) => {
|
|
431
461
|
const workerErrCB = this.workerError;
|
|
462
|
+
this.stopWorker();
|
|
432
463
|
readWriteConnectionStatusFile((cs) => {
|
|
433
464
|
if (cs?.last_packet.status === "catastrophic_reorg")
|
|
434
465
|
return;
|
|
435
466
|
const connectionStatus = {
|
|
467
|
+
...cs,
|
|
436
468
|
last_packet: {
|
|
437
469
|
status: "connection_failed",
|
|
438
470
|
bytes_read: 0,
|
|
@@ -441,7 +473,7 @@ export class ScanCacheOpened {
|
|
|
441
473
|
},
|
|
442
474
|
};
|
|
443
475
|
return connectionStatus;
|
|
444
|
-
}).then(() => {
|
|
476
|
+
}, this.scan_settings_path).then(() => {
|
|
445
477
|
if (workerErrCB)
|
|
446
478
|
workerErrCB(error);
|
|
447
479
|
});
|
|
@@ -471,7 +503,13 @@ export class ScanCacheOpened {
|
|
|
471
503
|
selectOneInput(amount) {
|
|
472
504
|
return this.spendableInputs()
|
|
473
505
|
.filter((output) => output.amount >= amount)
|
|
474
|
-
.sort((a, b) =>
|
|
506
|
+
.sort((a, b) => {
|
|
507
|
+
if (b.amount > a.amount)
|
|
508
|
+
return -1;
|
|
509
|
+
if (b.amount < a.amount)
|
|
510
|
+
return 1;
|
|
511
|
+
return a.block_height - b.block_height;
|
|
512
|
+
})[0];
|
|
475
513
|
}
|
|
476
514
|
/**
|
|
477
515
|
* selectMultipleInputs larger than amount, sorted from largest to smallest until total reaches amount
|
|
@@ -509,6 +547,11 @@ export class ScanCacheOpened {
|
|
|
509
547
|
return;
|
|
510
548
|
this._cache = params.newCache;
|
|
511
549
|
this._stats = await alignScanStatsWithCache(this._cache, this.view_pair, this.primary_address, this.pathPrefix, undefined, lastRange(this._cache.scanned_ranges)?.end);
|
|
550
|
+
if (!this.no_worker) {
|
|
551
|
+
const etaResult = await updateSyncETA(this._cache.daemon_height, this.current_height || 0, this.last_eta_height, this.last_eta_timestamp, this.scan_settings_path);
|
|
552
|
+
this.last_eta_height = etaResult.last_height;
|
|
553
|
+
this.last_eta_timestamp = etaResult.last_timestamp;
|
|
554
|
+
}
|
|
512
555
|
for (const listener of this.notifyListeners) {
|
|
513
556
|
if (listener)
|
|
514
557
|
listener(params);
|
|
@@ -522,6 +565,8 @@ export class ScanCacheOpened {
|
|
|
522
565
|
primary_address: "",
|
|
523
566
|
};
|
|
524
567
|
worker = undefined;
|
|
568
|
+
last_eta_height = null;
|
|
569
|
+
last_eta_timestamp = null;
|
|
525
570
|
constructor(view_pair, wallet_route, wallet_name, wallet_slot, no_worker, masterCacheChanged, _start_height, scan_settings_path, pathPrefix, workerError) {
|
|
526
571
|
this.view_pair = view_pair;
|
|
527
572
|
this.wallet_route = wallet_route;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { type GetBlocksBinBufferItem, type BlocksBufferLoopResult, type BlocksBufferIteratorResult, type ProcessScanResult, type BlockInfo } from "../../api";
|
|
2
|
+
import { type ScanLoopYield } from "./scanLoop";
|
|
3
|
+
import { type WorkItem } from "./scanLoop";
|
|
4
|
+
import { type ScanSettings } from "../scanSettings";
|
|
5
|
+
import { type CacheRange, type ScanCache } from "./scanCache";
|
|
6
|
+
export type WalletConfig = {
|
|
7
|
+
primary_address: string;
|
|
8
|
+
secret_view_key: string;
|
|
9
|
+
secret_spend_key?: string;
|
|
10
|
+
subaddress_index: number;
|
|
11
|
+
};
|
|
12
|
+
export type WalletConfigPlusCache = {
|
|
13
|
+
primary_address: string;
|
|
14
|
+
secret_view_key: string;
|
|
15
|
+
secret_spend_key?: string;
|
|
16
|
+
subaddress_index: number;
|
|
17
|
+
cache: ScanCache;
|
|
18
|
+
};
|
|
19
|
+
export type WorkToBeDone = {
|
|
20
|
+
start_height: number;
|
|
21
|
+
wallet_configs: WalletConfigPlusCache[];
|
|
22
|
+
anchor_range: CacheRange;
|
|
23
|
+
scan_settings: ScanSettings;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* this depends only on ScanSettings.json start_height and wallet caches scanned_ranges
|
|
27
|
+
* side effect: will init wallet cache file if it does not exist
|
|
28
|
+
* side effect: will merge scan ranges + add subaddreses to existing cache files
|
|
29
|
+
* @param scan_settings_path
|
|
30
|
+
*/
|
|
31
|
+
export declare function findWorkToBeDone(scan_settings_path?: string, pathPrefix?: string): Promise<WorkToBeDone | false>;
|
|
32
|
+
export declare function workToBeDoneForBatch(cache: ScanCache, batch_meta_infos: BlockInfo[]): "skip" | {
|
|
33
|
+
from: number;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* called when the blocks buffer generator yields "blocks_buffer_changed".
|
|
37
|
+
* adds new work items for blocks buffer items not yet referenced.
|
|
38
|
+
* per wallet
|
|
39
|
+
*/
|
|
40
|
+
export declare function makeWorkItemsFromBlocksBuffer(blocksBuffer: GetBlocksBinBufferItem[], workItemBuffer: WorkItem[], walletConfig: WalletConfigPlusCache, from?: number, to?: number): void;
|
|
41
|
+
export declare function makeWorkItemsForAllWallets(wallet_configs: WalletConfigPlusCache[], blocksBuffer: GetBlocksBinBufferItem[], workBuffer: WorkItem[]): void;
|
|
42
|
+
/**
|
|
43
|
+
* called when a work item at the left end of the work buffer is done.
|
|
44
|
+
* shifts done items off the left, and removes their batch from the
|
|
45
|
+
* blocks buffer if no remaining work items reference it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reconcileWorkItemDone(blocksBuffer: GetBlocksBinBufferItem[], workItemBuffer: WorkItem[]): void;
|
|
48
|
+
/**
|
|
49
|
+
* one round of the select race. takes two primed promises (blocks, scan),
|
|
50
|
+
* races them, returns winner info. the loser stays in flight.
|
|
51
|
+
*/
|
|
52
|
+
export declare function raceStep(blocksPromise: Promise<IteratorResult<BlocksBufferLoopResult>>, scanPromise: Promise<IteratorResult<ScanLoopYield, void>>): Promise<{
|
|
53
|
+
src: "blocks" | "scan";
|
|
54
|
+
value: IteratorResult<BlocksBufferLoopResult> | IteratorResult<ScanLoopYield, void>;
|
|
55
|
+
done: boolean;
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* handle a yield from the blocks buffer fetch loop.
|
|
59
|
+
*/
|
|
60
|
+
export declare function handleBlocksYield(value: BlocksBufferLoopResult, scanSettingsPath?: string): Promise<{
|
|
61
|
+
isBlocksBufferChanged: boolean;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* process a scan result for a completed work item.
|
|
65
|
+
* updates the cache, writes to disk, marks work item done, reconciles.
|
|
66
|
+
*/
|
|
67
|
+
export declare function processScanResultForWorkItem(value: ScanLoopYield, workBuffer: WorkItem[], blocksBuffer: GetBlocksBinBufferItem[], pathPrefix: string, secret_spend_key?: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* process a scan result for a completed work item.
|
|
70
|
+
* updates the cache, writes to disk, marks work item done, reconciles blockbuffer with this.
|
|
71
|
+
*/
|
|
72
|
+
export declare function processWorkItem(item: WorkItem, workBuffer: WorkItem[], blocksBuffer: GetBlocksBinBufferItem[], pathPrefix: string, secret_spend_key?: string): Promise<ProcessScanResult>;
|
|
73
|
+
export type CoordinatorEvent = {
|
|
74
|
+
type: "blocks_buffer_changed";
|
|
75
|
+
} | {
|
|
76
|
+
type: "connection_status";
|
|
77
|
+
status: any;
|
|
78
|
+
} | {
|
|
79
|
+
type: "scan_ready";
|
|
80
|
+
address: string;
|
|
81
|
+
result: ScanLoopYield;
|
|
82
|
+
newCache: ScanCache;
|
|
83
|
+
changed_outputs: {
|
|
84
|
+
output: any;
|
|
85
|
+
change_reason: string;
|
|
86
|
+
}[];
|
|
87
|
+
} | {
|
|
88
|
+
type: "all_idle";
|
|
89
|
+
} | {
|
|
90
|
+
type: "error";
|
|
91
|
+
error: Error;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* coordinator main: async generator that drives the full scan cycle.
|
|
95
|
+
* takes only scanSettingsPath, derives everything else internally.
|
|
96
|
+
* yields events for some of the underlying generator events,
|
|
97
|
+
* look at CoordinatorEvent, processing not decoupled from cpu bound scan work,
|
|
98
|
+
* as this single threaded, there is no point
|
|
99
|
+
*/
|
|
100
|
+
export declare function coordinatorMain(scanSettingsPath?: string, pathPrefix?: string): AsyncGenerator<CoordinatorEvent>;
|
|
101
|
+
export declare function setupCoordinator(scanSettingsPath?: string, pathPrefix?: string): Promise<false | {
|
|
102
|
+
blocksGenerator: AsyncGenerator<BlocksBufferLoopResult, any, any>;
|
|
103
|
+
workBuffer: WorkItem[];
|
|
104
|
+
blocksBuffer: GetBlocksBinBufferItem[];
|
|
105
|
+
work_to_be_done: WorkToBeDone;
|
|
106
|
+
}>;
|
|
107
|
+
export type WalletSyncInfo = {
|
|
108
|
+
walletConfig: WalletConfig;
|
|
109
|
+
work_buffer: WorkItem[];
|
|
110
|
+
};
|
|
111
|
+
export type PortStatus = {
|
|
112
|
+
port: MessagePort;
|
|
113
|
+
promise: Promise<ScanLoopYield> | null;
|
|
114
|
+
};
|
|
115
|
+
export type BlocksBufferRacer = {
|
|
116
|
+
src: "blocks";
|
|
117
|
+
value: BlocksBufferIteratorResult;
|
|
118
|
+
};
|
|
119
|
+
export type ScanLoopRacer = ScanLoopYield;
|
|
120
|
+
export type Racers = BlocksBufferRacer | ScanLoopRacer;
|
|
121
|
+
/**
|
|
122
|
+
* coordinator main (multithreaded): dispatches scan work to CPU workers
|
|
123
|
+
* via MessagePorts, processes results in order per wallet.
|
|
124
|
+
* falls back to single-threaded coordinator if no cpuPorts provided.
|
|
125
|
+
*/
|
|
126
|
+
export declare function coordinatorMainMultithreaded(scanSettingsPath?: string, pathPrefix?: string, cpuPorts?: MessagePort[]): AsyncGenerator<CoordinatorEvent>;
|
|
127
|
+
export declare function scheduleWorkOnCpuPorts(ports: PortStatus[], work_buffer: WorkItem[]): Promise<void>;
|