@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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { frost_dkg_wasm } from "../wasm-processing/wasmFile";
|
|
2
|
+
import { WasmProcessor } from "../wasm-processing/wasmProcessor";
|
|
3
|
+
export class MultiSigTxSigner extends WasmProcessor {
|
|
4
|
+
static async create() {
|
|
5
|
+
const signer = new MultiSigTxSigner();
|
|
6
|
+
await signer.initWasmModule(frost_dkg_wasm);
|
|
7
|
+
return signer;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* preprocess
|
|
11
|
+
*
|
|
12
|
+
* @param params threshold_key hex, unsigned_tx hex
|
|
13
|
+
* @returns preprocess hex
|
|
14
|
+
*/
|
|
15
|
+
preprocess(params) {
|
|
16
|
+
const result = this.preprocessNoThrow(params);
|
|
17
|
+
if ("message" in result) {
|
|
18
|
+
throw new Error(`preprocess failed: ${result.message}`);
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* preprocess - does not throw, with return error type
|
|
24
|
+
*
|
|
25
|
+
* @param params threshold_key hex, unsigned_tx hex
|
|
26
|
+
* @returns preprocess hex
|
|
27
|
+
*/
|
|
28
|
+
preprocessNoThrow(params) {
|
|
29
|
+
const jsonStr = JSON.stringify(params);
|
|
30
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
31
|
+
this.writeString(ptr, len, jsonStr);
|
|
32
|
+
};
|
|
33
|
+
let result;
|
|
34
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
35
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
36
|
+
};
|
|
37
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
38
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
39
|
+
};
|
|
40
|
+
//@ts-ignore
|
|
41
|
+
this.tinywasi.instance.exports.monero_preprocess(jsonStr.length);
|
|
42
|
+
if (!result) {
|
|
43
|
+
return { message: "No response from monero_preprocess" };
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* sign
|
|
49
|
+
*
|
|
50
|
+
* @param params preprocesses map from all signers
|
|
51
|
+
* @returns share hex
|
|
52
|
+
*/
|
|
53
|
+
sign(params) {
|
|
54
|
+
const result = this.signNoThrow(params);
|
|
55
|
+
if ("message" in result) {
|
|
56
|
+
throw new Error(`sign failed: ${result.message}`);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* sign - does not throw, with return error type
|
|
62
|
+
*
|
|
63
|
+
* @param params preprocesses map from all signers
|
|
64
|
+
* @returns share hex
|
|
65
|
+
*/
|
|
66
|
+
signNoThrow(params) {
|
|
67
|
+
const jsonStr = JSON.stringify(params);
|
|
68
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
69
|
+
this.writeString(ptr, len, jsonStr);
|
|
70
|
+
};
|
|
71
|
+
let result;
|
|
72
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
73
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
74
|
+
};
|
|
75
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
76
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
77
|
+
};
|
|
78
|
+
//@ts-ignore
|
|
79
|
+
this.tinywasi.instance.exports.monero_sign(jsonStr.length);
|
|
80
|
+
if (!result) {
|
|
81
|
+
return { message: "No response from monero_sign" };
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* complete
|
|
87
|
+
*
|
|
88
|
+
* @param params shares map from all signers
|
|
89
|
+
* @returns signed_tx hex
|
|
90
|
+
*/
|
|
91
|
+
complete(params) {
|
|
92
|
+
const result = this.completeNoThrow(params);
|
|
93
|
+
if ("message" in result) {
|
|
94
|
+
throw new Error(`complete failed: ${result.message}`);
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* complete - does not throw, with return error type
|
|
100
|
+
*
|
|
101
|
+
* @param params shares map from all signers
|
|
102
|
+
* @returns signed_tx hex
|
|
103
|
+
*/
|
|
104
|
+
completeNoThrow(params) {
|
|
105
|
+
const jsonStr = JSON.stringify(params);
|
|
106
|
+
this.writeToWasmMemory = (ptr, len) => {
|
|
107
|
+
this.writeString(ptr, len, jsonStr);
|
|
108
|
+
};
|
|
109
|
+
let result;
|
|
110
|
+
this.readFromWasmMemory = (ptr, len) => {
|
|
111
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
112
|
+
};
|
|
113
|
+
this.readErrorFromWasmMemory = (ptr, len) => {
|
|
114
|
+
result = JSON.parse(this.readString(ptr, len));
|
|
115
|
+
};
|
|
116
|
+
//@ts-ignore
|
|
117
|
+
this.tinywasi.instance.exports.monero_complete(jsonStr.length);
|
|
118
|
+
if (!result) {
|
|
119
|
+
return { message: "No response from monero_complete" };
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare function makeEscrowContext(context_index: number): {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
error: string;
|
|
4
|
+
context?: undefined;
|
|
5
|
+
context_index?: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
ok: boolean;
|
|
8
|
+
context: string;
|
|
9
|
+
context_index: number;
|
|
10
|
+
error?: undefined;
|
|
11
|
+
};
|
|
12
|
+
export declare function parseEscrowContext(input: string): {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
error: string;
|
|
15
|
+
context?: undefined;
|
|
16
|
+
context_index?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
context: string;
|
|
20
|
+
context_index: string;
|
|
21
|
+
error?: undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* To give users the ability to conveniently partake in escrow transactions,
|
|
25
|
+
* the necessary secrets need to be organized well.
|
|
26
|
+
*
|
|
27
|
+
* The seedphrase package in this repository provides methods to derive
|
|
28
|
+
* secrets from a "walletroute" and a bip39 seedphrase.
|
|
29
|
+
*
|
|
30
|
+
* The "walletroute" is a string that is human readable and gives
|
|
31
|
+
* information on the context the wallet is created for.
|
|
32
|
+
*
|
|
33
|
+
* getWalletSecret() in the seedphrase package returns:
|
|
34
|
+
* 64 bytes of key data - uses KDF ( bip39.mnemonicToSeedSync of noble bip39)
|
|
35
|
+
*
|
|
36
|
+
* We use this method to derive the escrow-viewpair-comms secret from the
|
|
37
|
+
* "comms" key type of a walletroute. (consult the seedphrase package readme for more information)
|
|
38
|
+
*
|
|
39
|
+
* @param bip39_secret : 64 bytes key data
|
|
40
|
+
* @returns 32 bytes secret
|
|
41
|
+
*/
|
|
42
|
+
export declare function deriveEscrowViewpairCommsSecret(bip39_secret: Uint8Array): Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>;
|
|
43
|
+
export declare function escrowViewPairECDHgetPublicKey(vp_comms_secret: Uint8Array): Uint8Array<ArrayBufferLike> & Uint8Array<ArrayBuffer>;
|
|
44
|
+
/**
|
|
45
|
+
* In the typical escrow setup, from the perspective of the customer,
|
|
46
|
+
* the customer_sk is used together with the merchant pk to make the
|
|
47
|
+
* escrow wallet viewpair.
|
|
48
|
+
*
|
|
49
|
+
* From the perspective of the merchant,
|
|
50
|
+
* the merchant_sk is used together with the customer pk to make the
|
|
51
|
+
* escrow wallet viewpair.
|
|
52
|
+
*
|
|
53
|
+
* In the dispute flow case, the disputer shares the shared secret,
|
|
54
|
+
* that resulted from this ECDH exchange with the arbitrator.
|
|
55
|
+
* @param alice_sk - your viewpair secret
|
|
56
|
+
* @param bob_pk - the other multisig (escrow) party's viewpair public key
|
|
57
|
+
*/
|
|
58
|
+
export declare function performEscrowViewPairECDH(alice_sk: Uint8Array, bob_pk: Uint8Array): Promise<string>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ed25519, x25519 } from "@noble/curves/ed25519.js";
|
|
2
|
+
import { numberToBytesLE } from "@noble/curves/utils.js";
|
|
3
|
+
import { blake3 } from "@noble/hashes/blake3.js";
|
|
4
|
+
import { vk_from_entropy } from "../api";
|
|
5
|
+
export function makeEscrowContext(context_index) {
|
|
6
|
+
if (Number.isNaN(parseInt(String(context_index)))) {
|
|
7
|
+
return { ok: false, error: `invalid context_index: "${context_index}"` };
|
|
8
|
+
}
|
|
9
|
+
if (parseInt(String(context_index)) < 0) {
|
|
10
|
+
return {
|
|
11
|
+
ok: false,
|
|
12
|
+
error: `invalid context_index: "${context_index} < 0"`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (parseInt(String(context_index)) > 10000) {
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
error: `invalid context_index: "${context_index} > 10000"`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const context = "escrow-" + String(context_index);
|
|
22
|
+
return { ok: true, context, context_index };
|
|
23
|
+
}
|
|
24
|
+
export function parseEscrowContext(input) {
|
|
25
|
+
const parts = input.split("-");
|
|
26
|
+
if (parts.length < 1 || !parts[0] || parts[0] !== "escrow") {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: "missing escrow string before - ${context_index}",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const context_index = parts[1];
|
|
33
|
+
if (Number.isNaN(parseInt(String(context_index)))) {
|
|
34
|
+
return { ok: false, error: `invalid context_index: "${context_index}"` };
|
|
35
|
+
}
|
|
36
|
+
if (parseInt(String(context_index)) < 0) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: `invalid context_index: "${context_index} < 0"`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (parseInt(String(context_index)) > 10000) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
error: `invalid context_index: "${context_index} > 10000"`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const context = "escrow-" + String(context_index);
|
|
49
|
+
return { ok: true, context, context_index };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* To give users the ability to conveniently partake in escrow transactions,
|
|
53
|
+
* the necessary secrets need to be organized well.
|
|
54
|
+
*
|
|
55
|
+
* The seedphrase package in this repository provides methods to derive
|
|
56
|
+
* secrets from a "walletroute" and a bip39 seedphrase.
|
|
57
|
+
*
|
|
58
|
+
* The "walletroute" is a string that is human readable and gives
|
|
59
|
+
* information on the context the wallet is created for.
|
|
60
|
+
*
|
|
61
|
+
* getWalletSecret() in the seedphrase package returns:
|
|
62
|
+
* 64 bytes of key data - uses KDF ( bip39.mnemonicToSeedSync of noble bip39)
|
|
63
|
+
*
|
|
64
|
+
* We use this method to derive the escrow-viewpair-comms secret from the
|
|
65
|
+
* "comms" key type of a walletroute. (consult the seedphrase package readme for more information)
|
|
66
|
+
*
|
|
67
|
+
* @param bip39_secret : 64 bytes key data
|
|
68
|
+
* @returns 32 bytes secret
|
|
69
|
+
*/
|
|
70
|
+
export function deriveEscrowViewpairCommsSecret(bip39_secret) {
|
|
71
|
+
if (bip39_secret.length !== 64) {
|
|
72
|
+
throw new Error("expected exactly 64 bytes for Multisig secret viewkey");
|
|
73
|
+
}
|
|
74
|
+
const txt = new TextEncoder();
|
|
75
|
+
return blake3(bip39_secret, { context: txt.encode("escrow-viewpair-comms") });
|
|
76
|
+
}
|
|
77
|
+
export function escrowViewPairECDHgetPublicKey(vp_comms_secret) {
|
|
78
|
+
return ed25519.getPublicKey(vp_comms_secret);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* In the typical escrow setup, from the perspective of the customer,
|
|
82
|
+
* the customer_sk is used together with the merchant pk to make the
|
|
83
|
+
* escrow wallet viewpair.
|
|
84
|
+
*
|
|
85
|
+
* From the perspective of the merchant,
|
|
86
|
+
* the merchant_sk is used together with the customer pk to make the
|
|
87
|
+
* escrow wallet viewpair.
|
|
88
|
+
*
|
|
89
|
+
* In the dispute flow case, the disputer shares the shared secret,
|
|
90
|
+
* that resulted from this ECDH exchange with the arbitrator.
|
|
91
|
+
* @param alice_sk - your viewpair secret
|
|
92
|
+
* @param bob_pk - the other multisig (escrow) party's viewpair public key
|
|
93
|
+
*/
|
|
94
|
+
export async function performEscrowViewPairECDH(alice_sk, bob_pk) {
|
|
95
|
+
const txt = new TextEncoder();
|
|
96
|
+
const aliceSecX = ed25519.utils.toMontgomerySecret(alice_sk);
|
|
97
|
+
const bobPubX = ed25519.utils.toMontgomery(bob_pk);
|
|
98
|
+
const sharedKey = x25519.getSharedSecret(aliceSecX, bobPubX);
|
|
99
|
+
const sharedSecret = blake3(sharedKey, {
|
|
100
|
+
context: txt.encode("escrow-viewpair-comms"),
|
|
101
|
+
});
|
|
102
|
+
const scalar = await vk_from_entropy(sharedSecret);
|
|
103
|
+
return scalar;
|
|
104
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { DkgErrorResponse, DkgParticipateParams, DkgParticipateResult, DkgVerifyParams, DkgVerifyResult, DkgVerifyValidResult } from "./DistributedKeyGenerator";
|
|
2
|
+
export declare class MultiSig {
|
|
3
|
+
private worker?;
|
|
4
|
+
/**
|
|
5
|
+
* set up a distributed key generator with t threshold and n total participants
|
|
6
|
+
* @param t - total number of multisig participants
|
|
7
|
+
* @param n - threshold to sign a transaction
|
|
8
|
+
* @returns - Promise<DistributedKeyGenerator>
|
|
9
|
+
*/
|
|
10
|
+
static createAndSetupGenerators(t: number, n: number): Promise<MultiSig>;
|
|
11
|
+
stopWorker(): void;
|
|
12
|
+
startWorker(t: number, n: number): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* participate in a DKG round (nothrow).
|
|
15
|
+
*
|
|
16
|
+
* @param params dkg_secret_key as 64-byte hex string, context, dkg_public_keys array (length is n implicitly), t (threshold)
|
|
17
|
+
* @returns The participation message as hex, or an error object
|
|
18
|
+
*/
|
|
19
|
+
participateNoThrow(params: DkgParticipateParams): Promise<DkgParticipateResult | DkgErrorResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* verify DKG participations and extract the group key (nothrow).
|
|
22
|
+
*
|
|
23
|
+
* @param params dkg_secret_key as 64-byte hex string, context, t (threshold), dkg_public_keys array (length is n implicitly),
|
|
24
|
+
* participations [ paricipant index -> hex participation message ]
|
|
25
|
+
* @returns The group key and params, faulty participants, not-enough message, or error
|
|
26
|
+
*/
|
|
27
|
+
verifyNoThrow(params: DkgVerifyParams): Promise<DkgVerifyResult | DkgErrorResponse>;
|
|
28
|
+
/**
|
|
29
|
+
* participate in a DKG round (throws on error).
|
|
30
|
+
*/
|
|
31
|
+
participate(params: DkgParticipateParams): Promise<DkgParticipateResult>;
|
|
32
|
+
/**
|
|
33
|
+
* verify DKG participations and extract the group key (throws on error).
|
|
34
|
+
*/
|
|
35
|
+
verify(params: DkgVerifyParams): Promise<DkgVerifyValidResult>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { log } from "../io/logging";
|
|
2
|
+
import { startWebworkerReady } from "../scanning-syncing/backgroundWorker";
|
|
3
|
+
export class MultiSig {
|
|
4
|
+
worker = undefined;
|
|
5
|
+
/**
|
|
6
|
+
* set up a distributed key generator with t threshold and n total participants
|
|
7
|
+
* @param t - total number of multisig participants
|
|
8
|
+
* @param n - threshold to sign a transaction
|
|
9
|
+
* @returns - Promise<DistributedKeyGenerator>
|
|
10
|
+
*/
|
|
11
|
+
static async createAndSetupGenerators(t, n) {
|
|
12
|
+
const dkg = new MultiSig();
|
|
13
|
+
await dkg.startWorker(t, n);
|
|
14
|
+
return dkg;
|
|
15
|
+
}
|
|
16
|
+
stopWorker() {
|
|
17
|
+
if (this.worker) {
|
|
18
|
+
this.worker.terminate();
|
|
19
|
+
delete this.worker;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async startWorker(t, n) {
|
|
23
|
+
if (!this.worker) {
|
|
24
|
+
this.worker = await startWebworkerReady();
|
|
25
|
+
const that = this;
|
|
26
|
+
const result_promise = new Promise((resolve) => {
|
|
27
|
+
if (!that.worker) {
|
|
28
|
+
resolve();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
that.worker.onmessage = (e) => {
|
|
32
|
+
const msg = e.data;
|
|
33
|
+
if (msg.type === "multisig-ready") {
|
|
34
|
+
resolve();
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
handleUnknownMessage(msg, "startWorker");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
that.worker.postMessage({
|
|
41
|
+
type: "setup",
|
|
42
|
+
t,
|
|
43
|
+
n,
|
|
44
|
+
role: "multisig",
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
return await result_promise;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.stopWorker();
|
|
51
|
+
this.startWorker(t, n);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* participate in a DKG round (nothrow).
|
|
56
|
+
*
|
|
57
|
+
* @param params dkg_secret_key as 64-byte hex string, context, dkg_public_keys array (length is n implicitly), t (threshold)
|
|
58
|
+
* @returns The participation message as hex, or an error object
|
|
59
|
+
*/
|
|
60
|
+
participateNoThrow(params) {
|
|
61
|
+
const msg = { type: "multisig-call", functionName: "participate", params };
|
|
62
|
+
const that = this;
|
|
63
|
+
const result_promise = new Promise((resolve) => {
|
|
64
|
+
if (!that.worker) {
|
|
65
|
+
handleNoWorkerButMethodCalled("participate");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
that.worker.onmessage = (e) => {
|
|
69
|
+
const msg = e.data;
|
|
70
|
+
if (msg.type === "multisig-call-result") {
|
|
71
|
+
resolve(msg.result);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
handleUnknownMessage(msg, "participate");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
that.worker.postMessage(msg);
|
|
78
|
+
});
|
|
79
|
+
return result_promise;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* verify DKG participations and extract the group key (nothrow).
|
|
83
|
+
*
|
|
84
|
+
* @param params dkg_secret_key as 64-byte hex string, context, t (threshold), dkg_public_keys array (length is n implicitly),
|
|
85
|
+
* participations [ paricipant index -> hex participation message ]
|
|
86
|
+
* @returns The group key and params, faulty participants, not-enough message, or error
|
|
87
|
+
*/
|
|
88
|
+
verifyNoThrow(params) {
|
|
89
|
+
const msg = { type: "multisig-call", functionName: "verify", params };
|
|
90
|
+
const that = this;
|
|
91
|
+
const result_promise = new Promise((resolve) => {
|
|
92
|
+
if (!that.worker) {
|
|
93
|
+
handleNoWorkerButMethodCalled("participate");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
that.worker.onmessage = (e) => {
|
|
97
|
+
const msg = e.data;
|
|
98
|
+
if (msg.type === "multisig-call-result") {
|
|
99
|
+
resolve(msg.result);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
handleUnknownMessage(msg, "participate");
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
that.worker.postMessage(msg);
|
|
106
|
+
});
|
|
107
|
+
return result_promise;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* participate in a DKG round (throws on error).
|
|
111
|
+
*/
|
|
112
|
+
async participate(params) {
|
|
113
|
+
const result = await this.participateNoThrow(params);
|
|
114
|
+
if ("message" in result) {
|
|
115
|
+
throw new Error(`participate failed: ${result.message}`);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* verify DKG participations and extract the group key (throws on error).
|
|
121
|
+
*/
|
|
122
|
+
async verify(params) {
|
|
123
|
+
const result = await this.verifyNoThrow(params);
|
|
124
|
+
if ("message" in result) {
|
|
125
|
+
throw new Error(`verify failed: ${result.message}`);
|
|
126
|
+
}
|
|
127
|
+
if ("faulty_participants" in result) {
|
|
128
|
+
throw new Error(`verify failed: ${result.faulty_participants}`);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function handleUnknownMessage(msg, functionName) {
|
|
134
|
+
log("multisig", [`${functionName}(): unknown message type: ${msg.type}`]);
|
|
135
|
+
throw new Error(`[multisig] ${functionName}(): unknown message type: ${msg.type}`);
|
|
136
|
+
}
|
|
137
|
+
function handleNoWorkerButMethodCalled(functionName) {
|
|
138
|
+
log("multisig", [`${functionName}(): no worker`]);
|
|
139
|
+
throw new Error(`[multisig] ${functionName}(): no worker`);
|
|
140
|
+
}
|
|
@@ -2,6 +2,7 @@ import { getBlocksBinExecuteRequest, loadGetBlocksBinResponse, getOutsBinJson, g
|
|
|
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";
|
|
5
|
+
import { monero_wallet_api_wasm } from "../wasm-processing/wasmFile";
|
|
5
6
|
/**
|
|
6
7
|
* This class is useful to interact with Moneros DaemonRpc binary requests in a convenient way.
|
|
7
8
|
* (similar to how you would interact with a REST api that gives you json back.)
|
|
@@ -27,7 +28,7 @@ export class NodeUrl extends WasmProcessor {
|
|
|
27
28
|
}
|
|
28
29
|
static async create(node_url) {
|
|
29
30
|
const nodeUrl = new NodeUrl(node_url || LOCAL_NODE_DEFAULT_URL);
|
|
30
|
-
await nodeUrl.initWasmModule();
|
|
31
|
+
await nodeUrl.initWasmModule(monero_wallet_api_wasm);
|
|
31
32
|
return nodeUrl;
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
@@ -8,3 +8,4 @@ export type WorkerSet = {
|
|
|
8
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
9
|
export declare function makeWebworkerScript(): string;
|
|
10
10
|
export declare function startWebworker(handle_result?: (result: unknown) => void, handle_error?: (error: unknown) => void): Worker;
|
|
11
|
+
export declare function startWebworkerReady(): Promise<Worker>;
|
|
@@ -101,7 +101,7 @@ export function startWebworker(handle_result, handle_error) {
|
|
|
101
101
|
// WORKER_READY (meaning self.onmessage = handleMessage has executed).
|
|
102
102
|
// after that, postMessage is safe, no race between worker startup
|
|
103
103
|
// and message delivery.
|
|
104
|
-
function startWebworkerReady() {
|
|
104
|
+
export function startWebworkerReady() {
|
|
105
105
|
const worker = startWebworker();
|
|
106
106
|
return new Promise((resolve) => {
|
|
107
107
|
const onReady = (e) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { LogSetting, PossibleLogs } from "../io/logging";
|
|
2
2
|
export declare const SCAN_SETTINGS_STORE_NAME_DEFAULT = "ScanSettings.json";
|
|
3
|
+
export declare const SUB_ADDRESS_INDEX_DEFAULT_VALUE = 1;
|
|
3
4
|
export type ScanSetting = {
|
|
4
5
|
primary_address: string;
|
|
5
6
|
subaddress_index?: number;
|
|
@@ -3,6 +3,10 @@ import { atomicWrite } from "../io/atomicWrite";
|
|
|
3
3
|
import { refreshEnvIndexedDB } from "../io/indexedDB";
|
|
4
4
|
import { LOCAL_NODE_DEFAULT_URL } from "../node-interaction/nodeUrl";
|
|
5
5
|
export const SCAN_SETTINGS_STORE_NAME_DEFAULT = "ScanSettings.json";
|
|
6
|
+
// default value for subaddress highest minor index eg up to which minor index,
|
|
7
|
+
// to add subaddresses to the scanner
|
|
8
|
+
// (we never touch major index, having two values is just too confusing)
|
|
9
|
+
export const SUB_ADDRESS_INDEX_DEFAULT_VALUE = 1;
|
|
6
10
|
/**
|
|
7
11
|
* Writes scan settings to the default or specified storage file in json.
|
|
8
12
|
*
|
|
@@ -231,7 +235,7 @@ export async function writeWalletToScanSettings(params) {
|
|
|
231
235
|
wallets: [
|
|
232
236
|
{
|
|
233
237
|
primary_address: params.primary_address,
|
|
234
|
-
subaddress_index: params.subaddress_index ||
|
|
238
|
+
subaddress_index: params.subaddress_index || SUB_ADDRESS_INDEX_DEFAULT_VALUE,
|
|
235
239
|
halted: params.halted,
|
|
236
240
|
wallet_route: params.wallet_route,
|
|
237
241
|
wallet_name: params.wallet_name,
|
|
@@ -261,7 +265,7 @@ export async function writeWalletToScanSettings(params) {
|
|
|
261
265
|
// wallet does not exist yet in settings
|
|
262
266
|
scanSettings.wallets.push({
|
|
263
267
|
primary_address: params.primary_address,
|
|
264
|
-
subaddress_index: params.subaddress_index ||
|
|
268
|
+
subaddress_index: params.subaddress_index || SUB_ADDRESS_INDEX_DEFAULT_VALUE,
|
|
265
269
|
halted: params.halted,
|
|
266
270
|
wallet_route: params.wallet_route,
|
|
267
271
|
wallet_name: params.wallet_name,
|
|
@@ -273,7 +277,9 @@ export async function writeWalletToScanSettings(params) {
|
|
|
273
277
|
const wallet = scanSettings.wallets[already_has_settings];
|
|
274
278
|
if (wallet) {
|
|
275
279
|
wallet.subaddress_index =
|
|
276
|
-
params.subaddress_index ||
|
|
280
|
+
params.subaddress_index ||
|
|
281
|
+
wallet.subaddress_index ||
|
|
282
|
+
SUB_ADDRESS_INDEX_DEFAULT_VALUE;
|
|
277
283
|
wallet.halted = params.halted || wallet.halted;
|
|
278
284
|
wallet.wallet_route = params.wallet_route || wallet.wallet_route;
|
|
279
285
|
wallet.wallet_name = params.wallet_name || wallet.wallet_name;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { Output } from "../../api";
|
|
2
|
+
import { WasmProcessor } from "../../wasm-processing/wasmProcessor";
|
|
2
3
|
export type KeyImage = string;
|
|
3
|
-
export declare function computeKeyImage(output: Output, sender_spend_key: string): Promise<KeyImage | undefined>;
|
|
4
|
+
export declare function computeKeyImage(output: Output, sender_spend_key: string, wasmProcessor?: WasmProcessor): Promise<KeyImage | undefined>;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { monero_wallet_api_wasm } from "../../wasm-processing/wasmFile";
|
|
1
2
|
import { WasmProcessor } from "../../wasm-processing/wasmProcessor";
|
|
2
|
-
export async function computeKeyImage(output, sender_spend_key) {
|
|
3
|
-
|
|
3
|
+
export async function computeKeyImage(output, sender_spend_key, wasmProcessor) {
|
|
4
|
+
if (!wasmProcessor)
|
|
5
|
+
wasmProcessor = await WasmProcessor.init(monero_wallet_api_wasm);
|
|
4
6
|
wasmProcessor.writeToWasmMemory = (ptr, len) => {
|
|
5
7
|
wasmProcessor.writeString(ptr, len, output.serialized);
|
|
6
8
|
wasmProcessor.writeToWasmMemory = (ptr, len) => {
|
|
@@ -46,6 +46,7 @@ export declare class ScanCacheOpened {
|
|
|
46
46
|
readonly decoyRetrySizes: number[];
|
|
47
47
|
static create(params: ScanCacheOpenedCreateParams): Promise<ScanCacheOpened>;
|
|
48
48
|
get start_height(): number | null;
|
|
49
|
+
get subaddress_index(): number;
|
|
49
50
|
get current_height(): number | null;
|
|
50
51
|
get current_top_range_height(): number | null;
|
|
51
52
|
changeStartHeight(start_height: number | null): Promise<void>;
|
|
@@ -60,6 +61,7 @@ export declare class ScanCacheOpened {
|
|
|
60
61
|
retry(): Promise<boolean>;
|
|
61
62
|
sendTransaction(signedTx: string): Promise<SendRawTransactionResult>;
|
|
62
63
|
signTransaction(unsignedTx: string): Promise<string>;
|
|
64
|
+
getFeeEstimate(): Promise<FeeEstimateResponse>;
|
|
63
65
|
calculateFeeAndSelectInputs(params: CreateTransactionParams): Promise<{
|
|
64
66
|
selectedInputs: Output[];
|
|
65
67
|
feeEstimate: {
|
|
@@ -70,10 +72,16 @@ export declare class ScanCacheOpened {
|
|
|
70
72
|
};
|
|
71
73
|
}>;
|
|
72
74
|
makeTransactionFromSelectedInputs(payments: Payment[], selectedInputs: Output[], feeEstimate: FeeEstimateResponse): Promise<string>;
|
|
75
|
+
makeSweepTransactionFromSelectedInputs(destination_address: string, selectedInputs: Output[], feeEstimate: FeeEstimateResponse): Promise<string>;
|
|
73
76
|
/**
|
|
74
77
|
* this function returns the unsigned transaction, throws {@link SendError}
|
|
75
78
|
*/
|
|
76
79
|
makeTransaction(params: CreateTransactionParams): Promise<string>;
|
|
80
|
+
/**
|
|
81
|
+
* sweep inputs to external wallet address (the wallet will receive input amount - fee)
|
|
82
|
+
* @param inputs use spendableInputs() to find inputs to put in
|
|
83
|
+
*/
|
|
84
|
+
sweepToExternalWallet(destination_address: string, inputs: Output[]): Promise<string>;
|
|
77
85
|
get daemon_height(): number;
|
|
78
86
|
get amount(): bigint;
|
|
79
87
|
get pending_amount(): bigint;
|
|
@@ -134,6 +142,9 @@ export declare class ScanCacheOpened {
|
|
|
134
142
|
* for all primary addresses
|
|
135
143
|
*/
|
|
136
144
|
feed(params: CacheChangedCallbackParameters): Promise<void>;
|
|
145
|
+
private _highest_subaddress_index;
|
|
146
|
+
private _no_stats;
|
|
147
|
+
get no_stats(): boolean;
|
|
137
148
|
private _cache;
|
|
138
149
|
private worker?;
|
|
139
150
|
private last_eta_height;
|