@spirobel/monero-wallet-api 0.4.0 → 0.5.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.
Files changed (48) hide show
  1. package/README.md +80 -8
  2. package/dist/api.d.ts +4 -0
  3. package/dist/api.js +4 -0
  4. package/dist/io/atomicWrite.js +2 -1
  5. package/dist/io/logging.d.ts +40 -0
  6. package/dist/io/logging.js +174 -0
  7. package/dist/keypairs-seeds/keypairs.d.ts +8 -0
  8. package/dist/keypairs-seeds/keypairs.js +31 -3
  9. package/dist/multisig/DistributedKeyGenerator.d.ts +125 -0
  10. package/dist/multisig/DistributedKeyGenerator.js +197 -0
  11. package/dist/multisig/MultiSigTxSigner.d.ts +68 -0
  12. package/dist/multisig/MultiSigTxSigner.js +123 -0
  13. package/dist/multisig/escrow.d.ts +58 -0
  14. package/dist/multisig/escrow.js +104 -0
  15. package/dist/multisig/multisig.d.ts +36 -0
  16. package/dist/multisig/multisig.js +140 -0
  17. package/dist/node-interaction/nodeUrl.js +2 -1
  18. package/dist/scanning-syncing/backgroundWorker.d.ts +2 -0
  19. package/dist/scanning-syncing/backgroundWorker.js +20 -8
  20. package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +9 -7
  21. package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +2 -1
  22. package/dist/scanning-syncing/scanSettings.d.ts +11 -0
  23. package/dist/scanning-syncing/scanSettings.js +18 -3
  24. package/dist/scanning-syncing/scanresult/computeKeyImage.d.ts +2 -1
  25. package/dist/scanning-syncing/scanresult/computeKeyImage.js +4 -2
  26. package/dist/scanning-syncing/scanresult/scanCache.js +7 -3
  27. package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +11 -0
  28. package/dist/scanning-syncing/scanresult/scanCacheOpened.js +104 -11
  29. package/dist/scanning-syncing/scanresult/scanCoordination.js +63 -72
  30. package/dist/scanning-syncing/scanresult/scanResult.d.ts +1 -1
  31. package/dist/scanning-syncing/scanresult/scanResult.js +19 -6
  32. package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
  33. package/dist/scanning-syncing/worker-mains/coordinator-main.js +3 -2
  34. package/dist/scanning-syncing/worker-mains/cpubound-main.js +24 -14
  35. package/dist/scanning-syncing/worker-mains/multisig-main.d.ts +13 -0
  36. package/dist/scanning-syncing/worker-mains/multisig-main.js +31 -0
  37. package/dist/scanning-syncing/worker-mains/worker.js +23 -7
  38. package/dist/send-functionality/multisig.d.ts +72 -0
  39. package/dist/send-functionality/multisig.js +97 -0
  40. package/dist/send-functionality/transactionBuilding.d.ts +1 -0
  41. package/dist/send-functionality/transactionBuilding.js +23 -2
  42. package/dist/viewpair/ViewPair.d.ts +8 -0
  43. package/dist/viewpair/ViewPair.js +16 -8
  44. package/dist/wasm-processing/wasmFile.d.ts +1 -0
  45. package/dist/wasm-processing/wasmFile.js +8 -3
  46. package/dist/wasm-processing/wasmProcessor.d.ts +2 -2
  47. package/dist/wasm-processing/wasmProcessor.js +4 -5
  48. package/package.json +6 -2
package/README.md CHANGED
@@ -1,27 +1,99 @@
1
1
  # @spirobel/monero-wallet-api
2
2
 
3
- To install dependencies:
3
+ ## quick start
4
+
5
+ ```ts
6
+ import { openWallets } from "@spirobel/monero-wallet-api";
7
+
8
+ // scan all non-halted wallets from the settings file:
9
+ const wallets = await openWallets();
10
+ ```
11
+
12
+ ## kitchen sink + transaction sending
13
+
14
+ ```ts
15
+ import { openWallets } from "@spirobel/monero-wallet-api";
16
+
17
+ // add a wallet address to ScanSettings.json first
18
+
19
+ // convenience script to make a regtest wallet and write to RegtestScanSettings.json + keys to .env.local:
20
+ // bun run scripts/regtest_gen.ts
21
+
22
+ // scan all non-halted wallets from the settings file:
23
+ const wallets = await openWallets({
24
+ scan_settings_path: "./ScanSettings.json", // this is the default scan settings file name
25
+ pathPrefix: "./", // default path for wallet scan caches, stats files, connection status file
26
+ notifyMasterChanged: async (params) => {
27
+ // called on each scan result: outputs, new_height, etc.
28
+ console.log("progress", params.newCache.current_height);
29
+ },
30
+ });
31
+
32
+ // change node url mid scan
33
+ await wallets.changeNodeUrl("http://127.0.0.1:18081");
34
+
35
+ // send a transaction
36
+ wallets.wallets[0].makeSignSendTransaction();
37
+ wallets.wallets[0].makeStandardTransaction(
38
+ escrow_address.mainnet_primary,
39
+ "133700000000",
40
+ );
41
+ wallets.wallets[0].signTransaction();
42
+ wallets.wallets[0].sendTransaction();
43
+
44
+ // sweep all funds to another wallet
45
+ const unsigned_tx_hex = await escrowWallet.sweepToExternalWallet(
46
+ merchant_final_address,
47
+ escrowWallet.spendableInputs(),
48
+ );
49
+
50
+ // stop scan worker
51
+ wallets.stopWorker();
52
+ ```
53
+
54
+ See the [reorg handling test](tests/acceptance/reorg_handling.test.ts) for a regtest reorg example.
55
+
56
+ After running this test a local node is available, which
57
+ makes it possible to easily run the [escrow test](tests/acceptance/escrow.test.ts) described in detail [here](https://monerochan.news/article/19).
58
+
59
+ See the [acceptance test](tests/acceptance/dont_rescan.test.ts) for a full working sync example with mainnet data.
60
+
61
+ ## install
62
+
63
+ To install the package:
4
64
 
5
65
  ```bash
6
- cd typescript || cd ../typescript
7
- bun install
66
+ bun add @spirobel/monero-wallet-api
8
67
  ```
9
68
 
10
- To build:
69
+ ## build
70
+
71
+ #### 1. rust release build
72
+
73
+ ```bash
74
+ cd rust || cd ../rust
75
+ cargo build --target wasm32-wasip1 --release --lib
76
+ ```
77
+
78
+ ```bash
79
+ cd frost-dkg || cd ../frost-dkg
80
+ cargo build --target wasm32-wasip1 --release --lib
81
+ ```
82
+
83
+ #### 2. build typescript:
11
84
 
12
85
  ```bash
13
86
  cd typescript || cd ../typescript
14
87
  bun build
15
88
  ```
16
89
 
17
- rust release build
90
+ ### prerequisite: init submodule (frost-dkg uses serai submodule)
18
91
 
19
92
  ```bash
20
- cd rust || cd ../rust
21
- cargo build --target wasm32-wasip1 --release --lib
93
+ git submodule update --init --recursive
22
94
  ```
23
95
 
24
- prerequisite: install rust
96
+ ### prerequisite: install rust (or use docker image, see below)
25
97
 
26
98
  ```bash
27
99
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
package/dist/api.d.ts CHANGED
@@ -11,6 +11,10 @@ export * from "./io/sleep";
11
11
  export { writeScanSettings, readScanSettings, } from "./scanning-syncing/scanSettings";
12
12
  export { signTransaction, parseAddress, type ParsedAddress, type ParseAddressError, } from "./send-functionality/transactionBuilding";
13
13
  export { computeKeyImage } from "./scanning-syncing/scanresult/computeKeyImage";
14
+ export * from "./multisig/multisig";
15
+ export * from "./multisig/DistributedKeyGenerator";
16
+ export * from "./multisig/escrow";
17
+ export * from "./multisig/MultiSigTxSigner";
14
18
  export { startWebworker, createWebworker, makeWebworkerScript, } from "./scanning-syncing/backgroundWorker";
15
19
  export { spendable } from "./scanning-syncing/scanresult/scanResult";
16
20
  export { openWallets, openWallet } from "./scanning-syncing/openWallet";
package/dist/api.js CHANGED
@@ -10,6 +10,10 @@ export * from "./io/sleep";
10
10
  export { writeScanSettings, readScanSettings, } from "./scanning-syncing/scanSettings";
11
11
  export { signTransaction, parseAddress, } from "./send-functionality/transactionBuilding";
12
12
  export { computeKeyImage } from "./scanning-syncing/scanresult/computeKeyImage";
13
+ export * from "./multisig/multisig";
14
+ export * from "./multisig/DistributedKeyGenerator";
15
+ export * from "./multisig/escrow";
16
+ export * from "./multisig/MultiSigTxSigner";
13
17
  export { startWebworker, createWebworker, makeWebworkerScript, } from "./scanning-syncing/backgroundWorker";
14
18
  export { spendable } from "./scanning-syncing/scanresult/scanResult";
15
19
  export { openWallets, openWallet } from "./scanning-syncing/openWallet";
@@ -1,5 +1,6 @@
1
+ import { log } from "./logging";
1
2
  export async function atomicWrite(targetPath, data) {
2
- //console.log("[atomicWrite] writing to", targetPath);
3
+ log("atomicWrite", ["writing to", targetPath]);
3
4
  // in the browser we don't have rename + indexedDB writes are atomic in any case
4
5
  if (areWeInTheBrowser)
5
6
  return await Bun.write(targetPath, data);
@@ -0,0 +1,40 @@
1
+ /**
2
+ * logging function names registry.
3
+ *
4
+ * useful filter combinations for `scanSettings.json`:
5
+ *
6
+ * "logs": "console",
7
+ * "logs_include": ["handleCpuboundScan", "logBufStatus"]
8
+ * -> cpu worker progress + buffer/work status.
9
+ *
10
+ * "logs_include": ["coordinatorMainMultithreaded", "scheduleWorkOnCpuPorts", "workToBeDoneForBatch"]
11
+ * -> coordinator events: race count, work dispatch, batch decisions.
12
+ *
13
+ * "logs_include": ["blocksBufferFetchLoop", "makeWorkItemsFromBlocksBuffer", "logBufStatus"]
14
+ * -> block fetching, reconciliation, buffer state.
15
+ *
16
+ * "logs_include": ["processScanResult", "processWorkItem", "processScanResultForWorkItem"]
17
+ * -> result processing, cache updates, reorg detection.
18
+ *
19
+ * "logs_include": ["handleConnectionStatusChanges", "handleScanError"]
20
+ * -> connection events and scan errors.
21
+ *
22
+ * "logs": "off" disables all logging.
23
+ * "logs": "file" writes to `<coordinator|cpubound|mainthread>-<id>-<timestamp>.log`.
24
+ * "logs": "console-and-file" writes to file and console.
25
+ */
26
+ export declare const LOGGING_FUNCTIONS: readonly ["findWorkToBeDone", "workToBeDoneForBatch", "makeWorkItemsFromBlocksBuffer", "reconcileWorkItemDone", "processScanResultForWorkItem", "processWorkItem", "logBufStatus", "coordinatorMain", "coordinatorMainMultithreaded", "scheduleWorkOnCpuPorts", "processScanResult", "handleScanError", "createWebworker", "startWebworker", "blocksBufferFetchLoop", "reduceStartHeightToTip", "handleConnectionStatusChanges", "handleCpuboundScan", "coordinatorMainWorker", "CPU_PORT_HANDLER", "atomicWrite", "multisigMainWorkerCall"];
27
+ export type PossibleLogs = (typeof LOGGING_FUNCTIONS)[number];
28
+ export type LogSetting = "console" | "file" | "console-and-file" | "off";
29
+ export type FileLogMessage = {
30
+ timestamp: string;
31
+ message: any;
32
+ };
33
+ export declare function setupLoggingPath(scan_settings_path: string, path_prefix: string, role: "coordinator" | "cpubound" | "mainthread" | "multisig", cpu_worker_id?: number): Promise<{
34
+ logs: LogSetting | undefined;
35
+ logs_include: ("findWorkToBeDone" | "workToBeDoneForBatch" | "makeWorkItemsFromBlocksBuffer" | "reconcileWorkItemDone" | "processScanResultForWorkItem" | "processWorkItem" | "logBufStatus" | "coordinatorMain" | "coordinatorMainMultithreaded" | "scheduleWorkOnCpuPorts" | "processScanResult" | "handleScanError" | "createWebworker" | "startWebworker" | "blocksBufferFetchLoop" | "reduceStartHeightToTip" | "handleConnectionStatusChanges" | "handleCpuboundScan" | "coordinatorMainWorker" | "CPU_PORT_HANDLER" | "atomicWrite" | "multisigMainWorkerCall")[] | undefined;
36
+ logs_exclude: ("findWorkToBeDone" | "workToBeDoneForBatch" | "makeWorkItemsFromBlocksBuffer" | "reconcileWorkItemDone" | "processScanResultForWorkItem" | "processWorkItem" | "logBufStatus" | "coordinatorMain" | "coordinatorMainMultithreaded" | "scheduleWorkOnCpuPorts" | "processScanResult" | "handleScanError" | "createWebworker" | "startWebworker" | "blocksBufferFetchLoop" | "reduceStartHeightToTip" | "handleConnectionStatusChanges" | "handleCpuboundScan" | "coordinatorMainWorker" | "CPU_PORT_HANDLER" | "atomicWrite" | "multisigMainWorkerCall")[] | undefined;
37
+ file_logbuffer: FileLogMessage[];
38
+ global_logging_path: string;
39
+ }>;
40
+ export declare function log(fnname: string, message: any): void;
@@ -0,0 +1,174 @@
1
+ import { getPathPrefix, openScanSettingsFile } from "../api";
2
+ import { atomicWrite } from "./atomicWrite";
3
+ /**
4
+ * logging function names registry.
5
+ *
6
+ * useful filter combinations for `scanSettings.json`:
7
+ *
8
+ * "logs": "console",
9
+ * "logs_include": ["handleCpuboundScan", "logBufStatus"]
10
+ * -> cpu worker progress + buffer/work status.
11
+ *
12
+ * "logs_include": ["coordinatorMainMultithreaded", "scheduleWorkOnCpuPorts", "workToBeDoneForBatch"]
13
+ * -> coordinator events: race count, work dispatch, batch decisions.
14
+ *
15
+ * "logs_include": ["blocksBufferFetchLoop", "makeWorkItemsFromBlocksBuffer", "logBufStatus"]
16
+ * -> block fetching, reconciliation, buffer state.
17
+ *
18
+ * "logs_include": ["processScanResult", "processWorkItem", "processScanResultForWorkItem"]
19
+ * -> result processing, cache updates, reorg detection.
20
+ *
21
+ * "logs_include": ["handleConnectionStatusChanges", "handleScanError"]
22
+ * -> connection events and scan errors.
23
+ *
24
+ * "logs": "off" disables all logging.
25
+ * "logs": "file" writes to `<coordinator|cpubound|mainthread>-<id>-<timestamp>.log`.
26
+ * "logs": "console-and-file" writes to file and console.
27
+ */
28
+ export const LOGGING_FUNCTIONS = [
29
+ // scanCoordination.ts
30
+ "findWorkToBeDone",
31
+ "workToBeDoneForBatch",
32
+ "makeWorkItemsFromBlocksBuffer",
33
+ "reconcileWorkItemDone",
34
+ "processScanResultForWorkItem",
35
+ "processWorkItem",
36
+ "logBufStatus",
37
+ "coordinatorMain",
38
+ "coordinatorMainMultithreaded",
39
+ "scheduleWorkOnCpuPorts",
40
+ // scanResult.ts
41
+ "processScanResult",
42
+ // scanCache.ts
43
+ "handleScanError",
44
+ // backgroundWorker.ts
45
+ "createWebworker",
46
+ "startWebworker",
47
+ // blocksBufferFetchLoop.ts
48
+ "blocksBufferFetchLoop",
49
+ "reduceStartHeightToTip",
50
+ // blocksbufferCoordination.ts
51
+ "handleConnectionStatusChanges",
52
+ // cpubound-main.ts
53
+ "handleCpuboundScan",
54
+ // coordinator-main.ts
55
+ "coordinatorMainWorker",
56
+ // worker.ts
57
+ "CPU_PORT_HANDLER",
58
+ // io/atomicWrite.ts
59
+ "atomicWrite",
60
+ // multisig-main.ts
61
+ "multisigMainWorkerCall",
62
+ ];
63
+ export async function setupLoggingPath(scan_settings_path, path_prefix, role, cpu_worker_id) {
64
+ const scanSettings = await openScanSettingsFile(scan_settings_path);
65
+ const logs = scanSettings?.logs;
66
+ const logs_include = scanSettings?.logs_include;
67
+ const logs_exclude = scanSettings?.logs_exclude;
68
+ const file_logbuffer = [];
69
+ const logging_path_prefix = getPathPrefix(scan_settings_path, path_prefix);
70
+ const timestamp = new Date().toISOString().slice(0, 19).replace("T", " ");
71
+ const global_logging_path = `${logging_path_prefix}${role}-${cpu_worker_id ?? ""}-${timestamp}.log`;
72
+ const global_log_setup = {
73
+ logs,
74
+ logs_include,
75
+ logs_exclude,
76
+ file_logbuffer,
77
+ global_logging_path,
78
+ };
79
+ globalThis.__global_log_setup = global_log_setup;
80
+ startLogFlusher(global_log_setup);
81
+ return global_log_setup;
82
+ }
83
+ function safeStringify(value) {
84
+ try {
85
+ return JSON.stringify(value, (_key, v) => {
86
+ if (typeof v === "bigint")
87
+ return v.toString();
88
+ if (typeof v === "function")
89
+ return `fn ${v.name || "anon"}`;
90
+ if (v !== null && typeof v === "object") {
91
+ // promise or thenable
92
+ if (typeof v.then === "function") {
93
+ try {
94
+ return typeof Bun !== "undefined" && Bun.inspect
95
+ ? Bun.inspect(v)
96
+ : "Promise";
97
+ }
98
+ catch {
99
+ return "Promise";
100
+ }
101
+ }
102
+ // message port
103
+ const ctor = v.constructor;
104
+ if (ctor?.name === "MessagePort")
105
+ return "MessagePort";
106
+ }
107
+ return v;
108
+ });
109
+ }
110
+ catch {
111
+ return String(value);
112
+ }
113
+ }
114
+ const flusherRef = new WeakMap();
115
+ function startLogFlusher(setup) {
116
+ if (setup.logs !== "file" && setup.logs !== "console-and-file")
117
+ return;
118
+ if (flusherRef.has(setup))
119
+ return; // already running
120
+ let writtenIndex = 0;
121
+ const id = setInterval(async () => {
122
+ const buffer = setup.file_logbuffer;
123
+ if (buffer.length <= writtenIndex)
124
+ return; // nothing new
125
+ const newEntries = buffer.slice(writtenIndex);
126
+ writtenIndex = buffer.length;
127
+ if (newEntries.length === 0)
128
+ return;
129
+ const lines = newEntries
130
+ .map((e) => `[${e.timestamp}] ${safeStringify(e.message)}`)
131
+ .join("\n");
132
+ try {
133
+ const existing = await Bun.file(setup.global_logging_path)
134
+ .text()
135
+ .catch(() => "");
136
+ const newContent = existing ? existing + "\n" + lines : lines;
137
+ await atomicWrite(setup.global_logging_path, newContent);
138
+ }
139
+ catch (err) {
140
+ console.error("[logFlusher] write failed:", err);
141
+ }
142
+ }, 2000);
143
+ flusherRef.set(setup, id);
144
+ }
145
+ export function log(fnname, message) {
146
+ const setup = globalThis.__global_log_setup;
147
+ if (!setup)
148
+ return;
149
+ const { logs, logs_include, logs_exclude, file_logbuffer } = setup;
150
+ if (logs === "off")
151
+ return;
152
+ // Apply include/exclude filters
153
+ if (logs_include && logs_include.length > 0 && !logs_include.includes(fnname))
154
+ return;
155
+ if (logs_exclude && logs_exclude.includes(fnname))
156
+ return;
157
+ // skip messages that are about writing to the log file itself
158
+ // (prevents atomicWrite calls inside the flusher from feeding back)
159
+ const logPath = setup.global_logging_path;
160
+ if (logPath) {
161
+ const msg = message;
162
+ if ((typeof msg === "string" && msg.includes(logPath)) ||
163
+ (Array.isArray(msg) &&
164
+ msg.some((m) => typeof m === "string" && m.includes(logPath))))
165
+ return;
166
+ }
167
+ const timestamp = new Date().toISOString().slice(0, 19).replace("T", " ");
168
+ if (logs === "console" || logs === "console-and-file") {
169
+ console.log(`[${fnname}]`, ...(Array.isArray(message) ? message : [message]));
170
+ }
171
+ if (logs === "file" || logs === "console-and-file") {
172
+ file_logbuffer.push({ timestamp, message });
173
+ }
174
+ }
@@ -48,3 +48,11 @@ export type ViewPairJson = {
48
48
  * @returns the viewkey that was derive from the spendkey
49
49
  */
50
50
  export declare function makeViewKey(spend_private_key: string): Promise<ViewPairJson>;
51
+ /**
52
+ * entropy is hashed with keccak to make the view private key
53
+ * this is according to the convention in the monero code base,
54
+ * read the comment at the top of the file for more details & link to source
55
+ * @param entropy the spendkey the viewkey will be derived from
56
+ * @returns the viewkey that was derived from entropy as hex
57
+ */
58
+ export declare function vk_from_entropy(entropy: Uint8Array): Promise<string>;
@@ -60,6 +60,7 @@
60
60
  // simple wallet turns spend private key (recovery_val) into mnemonic words with this call:
61
61
  // 4849 crypto::ElectrumWords::bytes_to_words(recovery_val, electrum_words, mnemonic_language);
62
62
  // https://github.com/monero-project/monero/blob/48ad374b0d6d6e045128729534dc2508e6999afe/src/simplewallet/simplewallet.cpp#L4849
63
+ import { monero_wallet_api_wasm } from "../wasm-processing/wasmFile";
63
64
  import { WasmProcessor } from "../wasm-processing/wasmProcessor";
64
65
  /**
65
66
  * use this function to generate a keypair for testing
@@ -76,7 +77,7 @@ export async function makeTestKeyPair() {
76
77
  * @returns SpendKey
77
78
  */
78
79
  export async function makeSpendKey() {
79
- const wasmProcessor = await WasmProcessor.init();
80
+ const wasmProcessor = await WasmProcessor.init(monero_wallet_api_wasm);
80
81
  let result = undefined;
81
82
  wasmProcessor.readFromWasmMemory = (ptr, len) => {
82
83
  result = String(wasmProcessor.readString(ptr, len));
@@ -93,7 +94,7 @@ export async function makeSpendKeyFromSeed(wallet_secret) {
93
94
  throw new Error(`Invalid wallet secret length: ${wallet_secret.length}.
94
95
  Expected 128 hex characters (64 bytes).`);
95
96
  }
96
- const wasmProcessor = await WasmProcessor.init();
97
+ const wasmProcessor = await WasmProcessor.init(monero_wallet_api_wasm);
97
98
  wasmProcessor.writeToWasmMemory = (ptr, len) => {
98
99
  wasmProcessor.writeString(ptr, len, wallet_secret);
99
100
  };
@@ -120,7 +121,7 @@ export async function makeViewKey(spend_private_key) {
120
121
  throw new Error(`Invalid spendkey length: ${spend_private_key.length}.
121
122
  Expected 64 hex characters (32 bytes).`);
122
123
  }
123
- const wasmProcessor = await WasmProcessor.init();
124
+ const wasmProcessor = await WasmProcessor.init(monero_wallet_api_wasm);
124
125
  wasmProcessor.writeToWasmMemory = (ptr, len) => {
125
126
  wasmProcessor.writeString(ptr, len, spend_private_key);
126
127
  };
@@ -135,6 +136,33 @@ export async function makeViewKey(spend_private_key) {
135
136
  }
136
137
  return result;
137
138
  }
139
+ /**
140
+ * entropy is hashed with keccak to make the view private key
141
+ * this is according to the convention in the monero code base,
142
+ * read the comment at the top of the file for more details & link to source
143
+ * @param entropy the spendkey the viewkey will be derived from
144
+ * @returns the viewkey that was derived from entropy as hex
145
+ */
146
+ export async function vk_from_entropy(entropy) {
147
+ if (entropy.length !== 32) {
148
+ throw new Error(`Invalid entropy length: ${entropy.length}.
149
+ Expected 32 bytes array.`);
150
+ }
151
+ const wasmProcessor = await WasmProcessor.init(monero_wallet_api_wasm);
152
+ wasmProcessor.writeToWasmMemory = (ptr, len) => {
153
+ wasmProcessor.writeArray(ptr, len, entropy);
154
+ };
155
+ let result = undefined;
156
+ wasmProcessor.readFromWasmMemory = (ptr, len) => {
157
+ result = wasmProcessor.readString(ptr, len);
158
+ };
159
+ //@ts-ignore
160
+ wasmProcessor.tinywasi.instance.exports.vk_from_entropy(entropy.length);
161
+ if (!result) {
162
+ throw new Error("Failed to obtain view key from entropy.");
163
+ }
164
+ return result;
165
+ }
138
166
  /**
139
167
  *
140
168
  * unlike crypto-ops.c sc_reduce32, this is not unrolled
@@ -0,0 +1,125 @@
1
+ import { WasmProcessor } from "../wasm-processing/wasmProcessor";
2
+ /**
3
+ * get the DKG public key from a 64-byte DKG secret key
4
+ * @param dkgSecretKey - 64 byte Uint8Array dkg secret key
5
+ * @returns - dkg public key as hex string
6
+ */
7
+ export declare function getDkgPublicKey(dkgSecretKey: Uint8Array): Promise<string>;
8
+ /**
9
+ * derive a Monero address (mainnet, stagenet, testnet) from a DKG group key.
10
+ * spend_public_key is the group_key hex from dkg_verify output.
11
+ * view_secret_key is the 32-byte view secret key hex. (see performEscrowViewPairECDH on how to get one)
12
+ *
13
+ * @returns object with view_key, mainnet_primary, stagenet_primary, testnet_primary
14
+ */
15
+ export declare function getDkgMoneroAddress(spend_public_key: string, view_secret_key: string): Promise<DkgGetMoneroAddressResult>;
16
+ export type DkgGetPublicKeyResult = {
17
+ dkg_public_key: string;
18
+ };
19
+ export type DkgParticipateParams = {
20
+ dkg_secret_key: string;
21
+ context: string;
22
+ dkg_public_keys: string[];
23
+ t: number;
24
+ };
25
+ export type DkgParticipateResult = {
26
+ participation: string;
27
+ };
28
+ export type DkgVerifyParams = {
29
+ dkg_secret_key: string;
30
+ context: string;
31
+ t: number;
32
+ dkg_public_keys: string[];
33
+ participations: Record<string, string>;
34
+ };
35
+ export type DkgVerifyValidResult = {
36
+ group_key: string;
37
+ t: number;
38
+ n: number;
39
+ i: number;
40
+ threshold_key: string;
41
+ };
42
+ export type DkgVerifyInvalidResult = {
43
+ faulty_participants: number[];
44
+ };
45
+ export type DkgVerifyNotEnoughResult = {
46
+ message: "NotEnoughParticipants";
47
+ };
48
+ export type DkgVerifyResult = DkgVerifyValidResult | DkgVerifyInvalidResult | DkgVerifyNotEnoughResult;
49
+ export type DkgErrorResponse = {
50
+ message: string;
51
+ };
52
+ export type DkgGetMoneroAddressParams = {
53
+ spend_public_key: string;
54
+ view_secret_key: string;
55
+ };
56
+ export type DkgGetMoneroAddressResult = {
57
+ view_key: string;
58
+ mainnet_primary: string;
59
+ stagenet_primary: string;
60
+ testnet_primary: string;
61
+ };
62
+ export declare class DistributedKeyGenerator extends WasmProcessor {
63
+ /**
64
+ * create distributed key generator wasm instance,
65
+ * without initializing generators
66
+ * (in case you just want to get the public dkg key from a secret dkg key)
67
+ *
68
+ * @returns - Promise<DistributedKeyGenerator>
69
+ */
70
+ static create(): Promise<DistributedKeyGenerator>;
71
+ /**
72
+ * set up a distributed key generator with t threshold and n total participants
73
+ * @param t - total number of multisig participants
74
+ * @param n - threshold to sign a transaction
75
+ * @returns - Promise<DistributedKeyGenerator>
76
+ */
77
+ static createAndSetupGenerators(t: number, n: number): Promise<DistributedKeyGenerator>;
78
+ /**
79
+ * setupGenerators
80
+ * configure the max threshold and max participants for the DKG generators.
81
+ * if never called, defaults to 16, 16 used automatically on first DKG operation.
82
+ */
83
+ setupGenerators(t: number, n: number): void;
84
+ /**
85
+ * derive the DKG public key from a 64-byte DKG secret key.
86
+ *
87
+ * @param dkgSecretKeyBytes 64-byte DKG secret key (from seed function)
88
+ * @returns the DKG public key as a hex string, or an error object
89
+ */
90
+ getPublicKey(dkgSecretKeyBytes: Uint8Array): DkgGetPublicKeyResult | DkgErrorResponse;
91
+ /**
92
+ * participate in a DKG round (nothrow).
93
+ *
94
+ * @param params dkg_secret_key as 64-byte hex string, context, dkg_public_keys array (length is n implicitly), t (threshold)
95
+ * @returns The participation message as hex, or an error object
96
+ */
97
+ participateNoThrow(params: DkgParticipateParams): DkgParticipateResult | DkgErrorResponse;
98
+ /**
99
+ * verify DKG participations and extract the group key (nothrow).
100
+ *
101
+ * @param params dkg_secret_key as 64-byte hex string, context, t (threshold), dkg_public_keys array (length is n implicitly),
102
+ * participations [ paricipant index -> hex participation message ]
103
+ * @returns The group key and params, faulty participants, not-enough message, or error
104
+ */
105
+ verifyNoThrow(params: DkgVerifyParams): DkgVerifyResult | DkgErrorResponse;
106
+ /**
107
+ * derive a Monero address from a DKG group key.
108
+ *
109
+ * Input JSON keys expected by the rust wasm:
110
+ * spend_public_key (hex32) - the group_key hex from dkg_verify output
111
+ * view_secret_key (hex32) - 32-byte view secret key (see performEscrowViewPairECDH on how to get one)
112
+ *
113
+ * @param params - spendPublicKey and viewSecretKey as hex strings
114
+ * @returns object with view_key, mainnet_primary, stagenet_primary, testnet_primary
115
+ */
116
+ getMoneroAddress(params: DkgGetMoneroAddressParams): DkgGetMoneroAddressResult | DkgErrorResponse;
117
+ /**
118
+ * participate in a DKG round (throws on error).
119
+ */
120
+ participate(params: DkgParticipateParams): DkgParticipateResult;
121
+ /**
122
+ * verify DKG participations and extract the group key (throws on error).
123
+ */
124
+ verify(params: DkgVerifyParams): DkgVerifyValidResult;
125
+ }