@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.
- package/README.md +80 -8
- package/dist/api.d.ts +4 -0
- package/dist/api.js +4 -0
- package/dist/io/atomicWrite.js +2 -1
- package/dist/io/logging.d.ts +40 -0
- package/dist/io/logging.js +174 -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 +2 -0
- package/dist/scanning-syncing/backgroundWorker.js +20 -8
- package/dist/scanning-syncing/blocksbuffer/blocksBufferFetchLoop.js +9 -7
- package/dist/scanning-syncing/blocksbuffer/blocksbufferCoordination.js +2 -1
- package/dist/scanning-syncing/scanSettings.d.ts +11 -0
- package/dist/scanning-syncing/scanSettings.js +18 -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/scanCache.js +7 -3
- package/dist/scanning-syncing/scanresult/scanCacheOpened.d.ts +11 -0
- package/dist/scanning-syncing/scanresult/scanCacheOpened.js +104 -11
- package/dist/scanning-syncing/scanresult/scanCoordination.js +63 -72
- package/dist/scanning-syncing/scanresult/scanResult.d.ts +1 -1
- package/dist/scanning-syncing/scanresult/scanResult.js +19 -6
- package/dist/scanning-syncing/worker-entrypoints/worker.js +1 -1
- package/dist/scanning-syncing/worker-mains/coordinator-main.js +3 -2
- package/dist/scanning-syncing/worker-mains/cpubound-main.js +24 -14
- 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 +23 -7
- 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,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
|
+
}
|
|
@@ -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
|
+
}
|