@spirobel/monero-wallet-api 0.4.1 → 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.
- package/README.md +80 -8
- package/dist/api.d.ts +4 -0
- package/dist/api.js +4 -0
- package/dist/io/logging.d.ts +4 -4
- package/dist/io/logging.js +2 -0
- package/dist/keypairs-seeds/keypairs.d.ts +8 -0
- package/dist/keypairs-seeds/keypairs.js +31 -3
- package/dist/multisig/DistributedKeyGenerator.d.ts +125 -0
- package/dist/multisig/DistributedKeyGenerator.js +197 -0
- package/dist/multisig/MultiSigTxSigner.d.ts +68 -0
- package/dist/multisig/MultiSigTxSigner.js +123 -0
- package/dist/multisig/escrow.d.ts +58 -0
- package/dist/multisig/escrow.js +104 -0
- package/dist/multisig/multisig.d.ts +36 -0
- package/dist/multisig/multisig.js +140 -0
- package/dist/node-interaction/nodeUrl.js +2 -1
- package/dist/scanning-syncing/backgroundWorker.d.ts +1 -0
- package/dist/scanning-syncing/backgroundWorker.js +1 -1
- package/dist/scanning-syncing/scanSettings.d.ts +1 -0
- package/dist/scanning-syncing/scanSettings.js +9 -3
- package/dist/scanning-syncing/scanresult/computeKeyImage.d.ts +2 -1
- package/dist/scanning-syncing/scanresult/computeKeyImage.js +4 -2
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +11 -0
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +104 -11
- package/dist/scanning-syncing/scanresult/scanResult.js +7 -3
- package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
- package/dist/scanning-syncing/worker-mains/multisig-main.d.ts +13 -0
- package/dist/scanning-syncing/worker-mains/multisig-main.js +31 -0
- package/dist/scanning-syncing/worker-mains/worker.js +12 -0
- package/dist/send-functionality/multisig.d.ts +72 -0
- package/dist/send-functionality/multisig.js +97 -0
- package/dist/send-functionality/transactionBuilding.d.ts +1 -0
- package/dist/send-functionality/transactionBuilding.js +23 -2
- package/dist/viewpair/ViewPair.d.ts +8 -0
- package/dist/viewpair/ViewPair.js +16 -8
- package/dist/wasm-processing/wasmFile.d.ts +1 -0
- package/dist/wasm-processing/wasmFile.js +8 -3
- package/dist/wasm-processing/wasmProcessor.d.ts +2 -2
- package/dist/wasm-processing/wasmProcessor.js +4 -5
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,27 +1,99 @@
|
|
|
1
1
|
# @spirobel/monero-wallet-api
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
7
|
-
bun install
|
|
66
|
+
bun add @spirobel/monero-wallet-api
|
|
8
67
|
```
|
|
9
68
|
|
|
10
|
-
|
|
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
|
-
|
|
90
|
+
### prerequisite: init submodule (frost-dkg uses serai submodule)
|
|
18
91
|
|
|
19
92
|
```bash
|
|
20
|
-
|
|
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";
|
package/dist/io/logging.d.ts
CHANGED
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
* "logs": "file" writes to `<coordinator|cpubound|mainthread>-<id>-<timestamp>.log`.
|
|
24
24
|
* "logs": "console-and-file" writes to file and console.
|
|
25
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"];
|
|
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
27
|
export type PossibleLogs = (typeof LOGGING_FUNCTIONS)[number];
|
|
28
28
|
export type LogSetting = "console" | "file" | "console-and-file" | "off";
|
|
29
29
|
export type FileLogMessage = {
|
|
30
30
|
timestamp: string;
|
|
31
31
|
message: any;
|
|
32
32
|
};
|
|
33
|
-
export declare function setupLoggingPath(scan_settings_path: string, path_prefix: string, role: "coordinator" | "cpubound" | "mainthread", cpu_worker_id?: number): Promise<{
|
|
33
|
+
export declare function setupLoggingPath(scan_settings_path: string, path_prefix: string, role: "coordinator" | "cpubound" | "mainthread" | "multisig", cpu_worker_id?: number): Promise<{
|
|
34
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")[] | 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")[] | 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
37
|
file_logbuffer: FileLogMessage[];
|
|
38
38
|
global_logging_path: string;
|
|
39
39
|
}>;
|
package/dist/io/logging.js
CHANGED
|
@@ -57,6 +57,8 @@ export const LOGGING_FUNCTIONS = [
|
|
|
57
57
|
"CPU_PORT_HANDLER",
|
|
58
58
|
// io/atomicWrite.ts
|
|
59
59
|
"atomicWrite",
|
|
60
|
+
// multisig-main.ts
|
|
61
|
+
"multisigMainWorkerCall",
|
|
60
62
|
];
|
|
61
63
|
export async function setupLoggingPath(scan_settings_path, path_prefix, role, cpu_worker_id) {
|
|
62
64
|
const scanSettings = await openScanSettingsFile(scan_settings_path);
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { frost_dkg_wasm } from "../wasm-processing/wasmFile";
|
|
2
|
+
import { WasmProcessor } from "../wasm-processing/wasmProcessor";
|
|
3
|
+
/**
|
|
4
|
+
* get the DKG public key from a 64-byte DKG secret key
|
|
5
|
+
* @param dkgSecretKey - 64 byte Uint8Array dkg secret key
|
|
6
|
+
* @returns - dkg public key as hex string
|
|
7
|
+
*/
|
|
8
|
+
export async function getDkgPublicKey(dkgSecretKey) {
|
|
9
|
+
const dkg = await DistributedKeyGenerator.create();
|
|
10
|
+
const pubResult = dkg.getPublicKey(dkgSecretKey);
|
|
11
|
+
if ("message" in pubResult) {
|
|
12
|
+
throw new Error(`getPublicKey failed: ${pubResult.message}`);
|
|
13
|
+
}
|
|
14
|
+
return pubResult.dkg_public_key;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* derive a Monero address (mainnet, stagenet, testnet) from a DKG group key.
|
|
18
|
+
* spend_public_key is the group_key hex from dkg_verify output.
|
|
19
|
+
* view_secret_key is the 32-byte view secret key hex. (see performEscrowViewPairECDH on how to get one)
|
|
20
|
+
*
|
|
21
|
+
* @returns object with view_key, mainnet_primary, stagenet_primary, testnet_primary
|
|
22
|
+
*/
|
|
23
|
+
export async function getDkgMoneroAddress(spend_public_key, view_secret_key) {
|
|
24
|
+
const dkg = await DistributedKeyGenerator.create();
|
|
25
|
+
const result = dkg.getMoneroAddress({ spend_public_key, view_secret_key });
|
|
26
|
+
if ("message" in result) {
|
|
27
|
+
throw new Error(`getDkgMoneroAddress failed: ${result.message}`);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
export class DistributedKeyGenerator extends WasmProcessor {
|
|
32
|
+
/**
|
|
33
|
+
* create distributed key generator wasm instance,
|
|
34
|
+
* without initializing generators
|
|
35
|
+
* (in case you just want to get the public dkg key from a secret dkg key)
|
|
36
|
+
*
|
|
37
|
+
* @returns - Promise<DistributedKeyGenerator>
|
|
38
|
+
*/
|
|
39
|
+
static async create() {
|
|
40
|
+
const dkg = new DistributedKeyGenerator();
|
|
41
|
+
await dkg.initWasmModule(frost_dkg_wasm);
|
|
42
|
+
return dkg;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* set up a distributed key generator with t threshold and n total participants
|
|
46
|
+
* @param t - total number of multisig participants
|
|
47
|
+
* @param n - threshold to sign a transaction
|
|
48
|
+
* @returns - Promise<DistributedKeyGenerator>
|
|
49
|
+
*/
|
|
50
|
+
static async createAndSetupGenerators(t, n) {
|
|
51
|
+
const dkg = new DistributedKeyGenerator();
|
|
52
|
+
await dkg.initWasmModule(frost_dkg_wasm);
|
|
53
|
+
dkg.setupGenerators(t, n);
|
|
54
|
+
return dkg;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* setupGenerators
|
|
58
|
+
* configure the max threshold and max participants for the DKG generators.
|
|
59
|
+
* if never called, defaults to 16, 16 used automatically on first DKG operation.
|
|
60
|
+
*/
|
|
61
|
+
setupGenerators(t, n) {
|
|
62
|
+
//@ts-ignore
|
|
63
|
+
this.tinywasi.instance.exports.setup_generators(t, n);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* derive the DKG public key from a 64-byte DKG secret key.
|
|
67
|
+
*
|
|
68
|
+
* @param dkgSecretKeyBytes 64-byte DKG secret key (from seed function)
|
|
69
|
+
* @returns the DKG public key as a hex string, or an error object
|
|
70
|
+
*/
|
|
71
|
+
getPublicKey(dkgSecretKeyBytes) {
|
|
72
|
+
// set up write callback: rust will call input(64) to read the secret key bytes
|
|
73
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
74
|
+
this.writeArray(ptr, len, dkgSecretKeyBytes);
|
|
75
|
+
};
|
|
76
|
+
// set up read callback: rust will call output_string() to return the result JSON
|
|
77
|
+
let result;
|
|
78
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
79
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
80
|
+
};
|
|
81
|
+
//@ts-ignore
|
|
82
|
+
this.tinywasi.instance.exports.dkg_get_public_key();
|
|
83
|
+
if (!result) {
|
|
84
|
+
return { message: "No response from dkg_get_public_key" };
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* participate in a DKG round (nothrow).
|
|
90
|
+
*
|
|
91
|
+
* @param params dkg_secret_key as 64-byte hex string, context, dkg_public_keys array (length is n implicitly), t (threshold)
|
|
92
|
+
* @returns The participation message as hex, or an error object
|
|
93
|
+
*/
|
|
94
|
+
participateNoThrow(params) {
|
|
95
|
+
const jsonStr = JSON.stringify(params);
|
|
96
|
+
// set up write callback: rust will call input_string(json_len) to read the JSON
|
|
97
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
98
|
+
this.writeString(ptr, len, jsonStr);
|
|
99
|
+
};
|
|
100
|
+
// set up read callback: rust will call output_string() to return the result JSON
|
|
101
|
+
let result;
|
|
102
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
103
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
104
|
+
};
|
|
105
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
106
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
107
|
+
};
|
|
108
|
+
//@ts-ignore
|
|
109
|
+
this.tinywasi.instance.exports.dkg_participate(jsonStr.length);
|
|
110
|
+
if (!result) {
|
|
111
|
+
return { message: "No response from dkg_participate" };
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* verify DKG participations and extract the group key (nothrow).
|
|
117
|
+
*
|
|
118
|
+
* @param params dkg_secret_key as 64-byte hex string, context, t (threshold), dkg_public_keys array (length is n implicitly),
|
|
119
|
+
* participations [ paricipant index -> hex participation message ]
|
|
120
|
+
* @returns The group key and params, faulty participants, not-enough message, or error
|
|
121
|
+
*/
|
|
122
|
+
verifyNoThrow(params) {
|
|
123
|
+
const jsonStr = JSON.stringify(params);
|
|
124
|
+
// set up write callback: rust will call input_string(json_len) to read the JSON
|
|
125
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
126
|
+
this.writeString(ptr, len, jsonStr);
|
|
127
|
+
};
|
|
128
|
+
// set up read callback: rust will call output_string() to return the result JSON
|
|
129
|
+
let result;
|
|
130
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
131
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
132
|
+
};
|
|
133
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
134
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
135
|
+
};
|
|
136
|
+
//@ts-ignore
|
|
137
|
+
this.tinywasi.instance.exports.dkg_verify(jsonStr.length);
|
|
138
|
+
if (!result) {
|
|
139
|
+
return { message: "No response from dkg_verify" };
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* derive a Monero address from a DKG group key.
|
|
145
|
+
*
|
|
146
|
+
* Input JSON keys expected by the rust wasm:
|
|
147
|
+
* spend_public_key (hex32) - the group_key hex from dkg_verify output
|
|
148
|
+
* view_secret_key (hex32) - 32-byte view secret key (see performEscrowViewPairECDH on how to get one)
|
|
149
|
+
*
|
|
150
|
+
* @param params - spendPublicKey and viewSecretKey as hex strings
|
|
151
|
+
* @returns object with view_key, mainnet_primary, stagenet_primary, testnet_primary
|
|
152
|
+
*/
|
|
153
|
+
getMoneroAddress(params) {
|
|
154
|
+
const jsonStr = JSON.stringify(params);
|
|
155
|
+
// set up write callback: rust calls input_string(json_len)
|
|
156
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
157
|
+
this.writeString(ptr, len, jsonStr);
|
|
158
|
+
};
|
|
159
|
+
// set up read callback: rust calls output_string()
|
|
160
|
+
let result;
|
|
161
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
162
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
163
|
+
};
|
|
164
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
165
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
166
|
+
};
|
|
167
|
+
//@ts-ignore
|
|
168
|
+
this.tinywasi.instance.exports.dkg_get_monero_address(jsonStr.length);
|
|
169
|
+
if (!result) {
|
|
170
|
+
return { message: "No response from dkg_get_monero_address" };
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* participate in a DKG round (throws on error).
|
|
176
|
+
*/
|
|
177
|
+
participate(params) {
|
|
178
|
+
const result = this.participateNoThrow(params);
|
|
179
|
+
if ("message" in result) {
|
|
180
|
+
throw new Error(`participate failed: ${result.message}`);
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* verify DKG participations and extract the group key (throws on error).
|
|
186
|
+
*/
|
|
187
|
+
verify(params) {
|
|
188
|
+
const result = this.verifyNoThrow(params);
|
|
189
|
+
if ("message" in result) {
|
|
190
|
+
throw new Error(`verify failed: ${result.message}`);
|
|
191
|
+
}
|
|
192
|
+
if ("faulty_participants" in result) {
|
|
193
|
+
throw new Error(`verify failed: ${result.faulty_participants}`);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { WasmProcessor } from "../wasm-processing/wasmProcessor";
|
|
2
|
+
export type MoneroPreprocessParams = {
|
|
3
|
+
threshold_key: string;
|
|
4
|
+
unsigned_tx: string;
|
|
5
|
+
};
|
|
6
|
+
export type MoneroPreprocessResult = {
|
|
7
|
+
preprocess: string;
|
|
8
|
+
};
|
|
9
|
+
export type MoneroSignParams = {
|
|
10
|
+
preprocesses: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
export type MoneroSignResult = {
|
|
13
|
+
share: string;
|
|
14
|
+
};
|
|
15
|
+
export type MoneroCompleteParams = {
|
|
16
|
+
shares: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
export type MoneroCompleteResult = {
|
|
19
|
+
signed_tx: string;
|
|
20
|
+
};
|
|
21
|
+
export type MoneroErrorResponse = {
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
export declare class MultiSigTxSigner extends WasmProcessor {
|
|
25
|
+
static create(): Promise<MultiSigTxSigner>;
|
|
26
|
+
/**
|
|
27
|
+
* preprocess
|
|
28
|
+
*
|
|
29
|
+
* @param params threshold_key hex, unsigned_tx hex
|
|
30
|
+
* @returns preprocess hex
|
|
31
|
+
*/
|
|
32
|
+
preprocess(params: MoneroPreprocessParams): MoneroPreprocessResult;
|
|
33
|
+
/**
|
|
34
|
+
* preprocess - does not throw, with return error type
|
|
35
|
+
*
|
|
36
|
+
* @param params threshold_key hex, unsigned_tx hex
|
|
37
|
+
* @returns preprocess hex
|
|
38
|
+
*/
|
|
39
|
+
preprocessNoThrow(params: MoneroPreprocessParams): MoneroPreprocessResult | MoneroErrorResponse;
|
|
40
|
+
/**
|
|
41
|
+
* sign
|
|
42
|
+
*
|
|
43
|
+
* @param params preprocesses map from all signers
|
|
44
|
+
* @returns share hex
|
|
45
|
+
*/
|
|
46
|
+
sign(params: MoneroSignParams): MoneroSignResult;
|
|
47
|
+
/**
|
|
48
|
+
* sign - does not throw, with return error type
|
|
49
|
+
*
|
|
50
|
+
* @param params preprocesses map from all signers
|
|
51
|
+
* @returns share hex
|
|
52
|
+
*/
|
|
53
|
+
signNoThrow(params: MoneroSignParams): MoneroSignResult | MoneroErrorResponse;
|
|
54
|
+
/**
|
|
55
|
+
* complete
|
|
56
|
+
*
|
|
57
|
+
* @param params shares map from all signers
|
|
58
|
+
* @returns signed_tx hex
|
|
59
|
+
*/
|
|
60
|
+
complete(params: MoneroCompleteParams): MoneroCompleteResult;
|
|
61
|
+
/**
|
|
62
|
+
* complete - does not throw, with return error type
|
|
63
|
+
*
|
|
64
|
+
* @param params shares map from all signers
|
|
65
|
+
* @returns signed_tx hex
|
|
66
|
+
*/
|
|
67
|
+
completeNoThrow(params: MoneroCompleteParams): MoneroCompleteResult | MoneroErrorResponse;
|
|
68
|
+
}
|