@miden-sdk/miden-sdk 0.13.0-next.2 → 0.13.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Cargo-c25ff7e6.js → Cargo-eb2eb6d6.js} +10319 -5909
- package/dist/Cargo-eb2eb6d6.js.map +1 -0
- package/dist/assets/miden_client_web.wasm +0 -0
- package/dist/crates/miden_client_web.d.ts +2556 -517
- package/dist/index.d.ts +130 -122
- package/dist/index.js +462 -122
- package/dist/index.js.map +1 -1
- package/dist/wasm.js +1 -1
- package/dist/workers/{Cargo-c25ff7e6-c25ff7e6.js → Cargo-eb2eb6d6-eb2eb6d6.js} +10319 -5909
- package/dist/workers/Cargo-eb2eb6d6-eb2eb6d6.js.map +1 -0
- package/dist/workers/assets/miden_client_web.wasm +0 -0
- package/dist/workers/web-client-methods-worker.js +159 -28
- package/dist/workers/web-client-methods-worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/Cargo-c25ff7e6.js.map +0 -1
- package/dist/workers/Cargo-c25ff7e6-c25ff7e6.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import loadWasm from './wasm.js';
|
|
2
|
-
export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountType, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault,
|
|
2
|
+
export { Account, AccountArray, AccountBuilder, AccountBuilderResult, AccountCode, AccountComponent, AccountComponentCode, AccountDelta, AccountFile, AccountHeader, AccountId, AccountIdArray, AccountInterface, AccountStorage, AccountStorageDelta, AccountStorageMode, AccountStorageRequirements, AccountType, AccountVaultDelta, Address, AdviceInputs, AdviceMap, AssetVault, AuthFalcon512RpoMultisigConfig, AuthScheme, AuthSecretKey, BasicFungibleFaucetComponent, BlockHeader, CodeBuilder, CommittedNote, ConsumableNoteRecord, Endpoint, ExecutedTransaction, Felt, FeltArray, FetchedAccount, FetchedNote, FlattenedU8Vec, ForeignAccount, ForeignAccountArray, FungibleAsset, FungibleAssetDelta, FungibleAssetDeltaItem, GetProceduresResultItem, InputNote, InputNoteRecord, InputNoteState, InputNotes, IntoUnderlyingByteSource, IntoUnderlyingSink, IntoUnderlyingSource, JsAccountUpdate, JsStateSyncUpdate, JsStorageMapEntry, JsStorageSlot, JsVaultAsset, Library, MerklePath, NetworkId, NetworkType, Note, NoteAndArgs, NoteAndArgsArray, NoteAssets, NoteAttachment, NoteAttachmentKind, NoteAttachmentScheme, NoteConsumability, NoteConsumptionStatus, NoteDetails, NoteDetailsAndTag, NoteDetailsAndTagArray, NoteExecutionHint, NoteFile, NoteFilter, NoteFilterTypes, NoteHeader, NoteId, NoteIdAndArgs, NoteIdAndArgsArray, NoteInclusionProof, NoteInputs, NoteLocation, NoteMetadata, NoteRecipient, NoteRecipientArray, NoteScript, NoteSyncInfo, NoteTag, NoteType, OutputNote, OutputNoteArray, OutputNoteRecord, OutputNoteState, OutputNotes, OutputNotesArray, Package, PartialNote, ProcedureThreshold, Program, ProvenTransaction, PublicKey, RpcClient, Rpo256, SerializedInputNoteData, SerializedOutputNoteData, SerializedTransactionData, Signature, SigningInputs, SigningInputsType, SlotAndKeys, SparseMerklePath, StorageMap, StorageSlot, StorageSlotArray, SyncSummary, TestUtils, TokenSymbol, TransactionArgs, TransactionFilter, TransactionId, TransactionProver, TransactionRecord, TransactionRequest, TransactionRequestBuilder, TransactionResult, TransactionScript, TransactionScriptInputPair, TransactionScriptInputPairArray, TransactionStatus, TransactionStoreUpdate, TransactionSummary, Word, createAuthFalcon512RpoMultisig, initSync } from './Cargo-eb2eb6d6.js';
|
|
3
3
|
|
|
4
4
|
const WorkerAction = Object.freeze({
|
|
5
5
|
INIT: "init",
|
|
6
|
+
INIT_MOCK: "initMock",
|
|
6
7
|
CALL_METHOD: "callMethod",
|
|
7
8
|
EXECUTE_CALLBACK: "executeCallback",
|
|
8
9
|
});
|
|
@@ -21,10 +22,223 @@ const MethodName = Object.freeze({
|
|
|
21
22
|
PROVE_TRANSACTION: "proveTransaction",
|
|
22
23
|
SUBMIT_NEW_TRANSACTION: "submitNewTransaction",
|
|
23
24
|
SUBMIT_NEW_TRANSACTION_MOCK: "submitNewTransactionMock",
|
|
25
|
+
SUBMIT_NEW_TRANSACTION_WITH_PROVER: "submitNewTransactionWithProver",
|
|
26
|
+
SUBMIT_NEW_TRANSACTION_WITH_PROVER_MOCK: "submitNewTransactionWithProverMock",
|
|
24
27
|
SYNC_STATE: "syncState",
|
|
25
28
|
SYNC_STATE_MOCK: "syncStateMock",
|
|
26
29
|
});
|
|
27
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Sync Lock Module
|
|
33
|
+
*
|
|
34
|
+
* Provides coordination for concurrent syncState() calls using the Web Locks API
|
|
35
|
+
* with an in-process mutex fallback for older browsers.
|
|
36
|
+
*
|
|
37
|
+
* Behavior:
|
|
38
|
+
* - Uses "coalescing": if a sync is in progress, subsequent callers wait and receive
|
|
39
|
+
* the same result
|
|
40
|
+
* - Web Locks for cross-tab coordination (Chrome 69+, Safari 15.4+)
|
|
41
|
+
* - In-process mutex fallback when Web Locks unavailable
|
|
42
|
+
* - Optional timeout support
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if the Web Locks API is available.
|
|
47
|
+
*/
|
|
48
|
+
function hasWebLocks() {
|
|
49
|
+
return (
|
|
50
|
+
typeof navigator !== "undefined" &&
|
|
51
|
+
navigator.locks !== undefined &&
|
|
52
|
+
typeof navigator.locks.request === "function"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Internal state for tracking in-progress syncs and waiters per database.
|
|
58
|
+
*/
|
|
59
|
+
const syncStates = new Map();
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get or create sync state for a database.
|
|
63
|
+
*/
|
|
64
|
+
function getSyncState(dbId) {
|
|
65
|
+
let state = syncStates.get(dbId);
|
|
66
|
+
if (!state) {
|
|
67
|
+
state = {
|
|
68
|
+
inProgress: false,
|
|
69
|
+
result: null,
|
|
70
|
+
error: null,
|
|
71
|
+
waiters: [],
|
|
72
|
+
releaseLock: null,
|
|
73
|
+
syncGeneration: 0,
|
|
74
|
+
};
|
|
75
|
+
syncStates.set(dbId, state);
|
|
76
|
+
}
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Acquire a sync lock for the given database.
|
|
82
|
+
*
|
|
83
|
+
* If a sync is already in progress:
|
|
84
|
+
* - Returns { acquired: false, coalescedResult } after waiting for the result
|
|
85
|
+
*
|
|
86
|
+
* If no sync is in progress:
|
|
87
|
+
* - Returns { acquired: true } and the caller should perform the sync,
|
|
88
|
+
* then call releaseSyncLock() or releaseSyncLockWithError()
|
|
89
|
+
*
|
|
90
|
+
* @param {string} dbId - The database ID to lock
|
|
91
|
+
* @param {number} timeoutMs - Optional timeout in milliseconds (0 = no timeout)
|
|
92
|
+
* @returns {Promise<{acquired: boolean, coalescedResult?: any}>}
|
|
93
|
+
*/
|
|
94
|
+
async function acquireSyncLock(dbId, timeoutMs = 0) {
|
|
95
|
+
const state = getSyncState(dbId);
|
|
96
|
+
|
|
97
|
+
// If a sync is already in progress, wait for it to complete (coalescing)
|
|
98
|
+
if (state.inProgress) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
let timeoutId;
|
|
101
|
+
if (timeoutMs > 0) {
|
|
102
|
+
timeoutId = setTimeout(() => {
|
|
103
|
+
const idx = state.waiters.findIndex((w) => w.resolve === onResult);
|
|
104
|
+
if (idx !== -1) {
|
|
105
|
+
state.waiters.splice(idx, 1);
|
|
106
|
+
}
|
|
107
|
+
reject(new Error("Sync lock acquisition timed out"));
|
|
108
|
+
}, timeoutMs);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const onResult = (result) => {
|
|
112
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
113
|
+
resolve({ acquired: false, coalescedResult: result });
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const onError = (error) => {
|
|
117
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
118
|
+
reject(error);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
state.waiters.push({ resolve: onResult, reject: onError });
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Mark sync as in progress and increment generation
|
|
126
|
+
state.inProgress = true;
|
|
127
|
+
state.result = null;
|
|
128
|
+
state.error = null;
|
|
129
|
+
state.syncGeneration++;
|
|
130
|
+
const currentGeneration = state.syncGeneration;
|
|
131
|
+
|
|
132
|
+
// Try to acquire Web Lock if available
|
|
133
|
+
if (hasWebLocks()) {
|
|
134
|
+
const lockName = `miden-sync-${dbId}`;
|
|
135
|
+
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
let timeoutId;
|
|
138
|
+
let timedOut = false;
|
|
139
|
+
|
|
140
|
+
if (timeoutMs > 0) {
|
|
141
|
+
timeoutId = setTimeout(() => {
|
|
142
|
+
timedOut = true;
|
|
143
|
+
if (state.syncGeneration === currentGeneration) {
|
|
144
|
+
state.inProgress = false;
|
|
145
|
+
const error = new Error("Sync lock acquisition timed out");
|
|
146
|
+
for (const waiter of state.waiters) {
|
|
147
|
+
waiter.reject(error);
|
|
148
|
+
}
|
|
149
|
+
state.waiters = [];
|
|
150
|
+
}
|
|
151
|
+
reject(new Error("Sync lock acquisition timed out"));
|
|
152
|
+
}, timeoutMs);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
navigator.locks
|
|
156
|
+
.request(lockName, { mode: "exclusive" }, async () => {
|
|
157
|
+
if (timedOut || state.syncGeneration !== currentGeneration) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
162
|
+
|
|
163
|
+
return new Promise((releaseLock) => {
|
|
164
|
+
state.releaseLock = releaseLock;
|
|
165
|
+
resolve({ acquired: true });
|
|
166
|
+
});
|
|
167
|
+
})
|
|
168
|
+
.catch((err) => {
|
|
169
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
170
|
+
if (state.syncGeneration === currentGeneration) {
|
|
171
|
+
state.inProgress = false;
|
|
172
|
+
}
|
|
173
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
// Fallback: no Web Locks, just use in-process state
|
|
178
|
+
return { acquired: true };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Release the sync lock with a successful result.
|
|
184
|
+
*
|
|
185
|
+
* This notifies all waiting callers with the result and releases the lock.
|
|
186
|
+
*
|
|
187
|
+
* @param {string} dbId - The database ID
|
|
188
|
+
* @param {any} result - The sync result to pass to waiters
|
|
189
|
+
*/
|
|
190
|
+
function releaseSyncLock(dbId, result) {
|
|
191
|
+
const state = getSyncState(dbId);
|
|
192
|
+
|
|
193
|
+
if (!state.inProgress) {
|
|
194
|
+
console.warn("releaseSyncLock called but no sync was in progress");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
state.result = result;
|
|
199
|
+
state.inProgress = false;
|
|
200
|
+
|
|
201
|
+
for (const waiter of state.waiters) {
|
|
202
|
+
waiter.resolve(result);
|
|
203
|
+
}
|
|
204
|
+
state.waiters = [];
|
|
205
|
+
|
|
206
|
+
if (state.releaseLock) {
|
|
207
|
+
state.releaseLock();
|
|
208
|
+
state.releaseLock = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Release the sync lock due to an error.
|
|
214
|
+
*
|
|
215
|
+
* This notifies all waiting callers that the sync failed.
|
|
216
|
+
*
|
|
217
|
+
* @param {string} dbId - The database ID
|
|
218
|
+
* @param {Error} error - The error to pass to waiters
|
|
219
|
+
*/
|
|
220
|
+
function releaseSyncLockWithError(dbId, error) {
|
|
221
|
+
const state = getSyncState(dbId);
|
|
222
|
+
|
|
223
|
+
if (!state.inProgress) {
|
|
224
|
+
console.warn("releaseSyncLockWithError called but no sync was in progress");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
state.error = error;
|
|
229
|
+
state.inProgress = false;
|
|
230
|
+
|
|
231
|
+
for (const waiter of state.waiters) {
|
|
232
|
+
waiter.reject(error);
|
|
233
|
+
}
|
|
234
|
+
state.waiters = [];
|
|
235
|
+
|
|
236
|
+
if (state.releaseLock) {
|
|
237
|
+
state.releaseLock();
|
|
238
|
+
state.releaseLock = null;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
28
242
|
const buildTypedArraysExport = (exportObject) => {
|
|
29
243
|
return Object.entries(exportObject).reduce(
|
|
30
244
|
(exports, [exportName, _export]) => {
|
|
@@ -37,6 +251,27 @@ const buildTypedArraysExport = (exportObject) => {
|
|
|
37
251
|
);
|
|
38
252
|
};
|
|
39
253
|
|
|
254
|
+
const deserializeError = (errorLike) => {
|
|
255
|
+
if (!errorLike) {
|
|
256
|
+
return new Error("Unknown error received from worker");
|
|
257
|
+
}
|
|
258
|
+
const { name, message, stack, cause, ...rest } = errorLike;
|
|
259
|
+
const reconstructedError = new Error(message ?? "Unknown worker error");
|
|
260
|
+
reconstructedError.name = name ?? reconstructedError.name;
|
|
261
|
+
if (stack) {
|
|
262
|
+
reconstructedError.stack = stack;
|
|
263
|
+
}
|
|
264
|
+
if (cause) {
|
|
265
|
+
reconstructedError.cause = deserializeError(cause);
|
|
266
|
+
}
|
|
267
|
+
Object.entries(rest).forEach(([key, value]) => {
|
|
268
|
+
if (value !== undefined) {
|
|
269
|
+
reconstructedError[key] = value;
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
return reconstructedError;
|
|
273
|
+
};
|
|
274
|
+
|
|
40
275
|
const MidenArrays = {};
|
|
41
276
|
|
|
42
277
|
let wasmModule = null;
|
|
@@ -103,25 +338,35 @@ class WebClient {
|
|
|
103
338
|
*
|
|
104
339
|
* @param {string | undefined} rpcUrl - RPC endpoint URL used by the client.
|
|
105
340
|
* @param {Uint8Array | undefined} seed - Optional seed for account initialization.
|
|
341
|
+
* @param {string | undefined} storeName - Optional name for the store to be used by the client.
|
|
106
342
|
* @param {(pubKey: Uint8Array) => Promise<Uint8Array | null | undefined> | Uint8Array | null | undefined} [getKeyCb]
|
|
107
343
|
* - Callback to retrieve the secret key bytes for a given public key. The `pubKey`
|
|
108
344
|
* parameter is the serialized public key (from `PublicKey.serialize()`). Return the
|
|
109
345
|
* corresponding secret key as a `Uint8Array`, or `null`/`undefined` if not found. The
|
|
110
346
|
* return value may be provided synchronously or via a `Promise`.
|
|
111
|
-
* @param {(pubKey: Uint8Array,
|
|
347
|
+
* @param {(pubKey: Uint8Array, AuthSecretKey: Uint8Array) => Promise<void> | void} [insertKeyCb]
|
|
112
348
|
* - Callback to persist a secret key. `pubKey` is the serialized public key, and
|
|
113
|
-
* `
|
|
349
|
+
* `authSecretKey` is the serialized secret key (from `AuthSecretKey.serialize()`). May return
|
|
114
350
|
* `void` or a `Promise<void>`.
|
|
115
|
-
* @param {(pubKey: Uint8Array, signingInputs: Uint8Array) => Promise<
|
|
116
|
-
* - Callback to produce signature
|
|
351
|
+
* @param {(pubKey: Uint8Array, signingInputs: Uint8Array) => Promise<Uint8Array> | Uint8Array} [signCb]
|
|
352
|
+
* - Callback to produce serialized signature bytes for the provided inputs. `pubKey` is the
|
|
117
353
|
* serialized public key, and `signingInputs` is a `Uint8Array` produced by
|
|
118
|
-
* `SigningInputs.serialize()`. Must return
|
|
119
|
-
*
|
|
354
|
+
* `SigningInputs.serialize()`. Must return a `Uint8Array` containing the serialized
|
|
355
|
+
* signature, either directly or wrapped in a `Promise`.
|
|
120
356
|
*/
|
|
121
|
-
constructor(
|
|
357
|
+
constructor(
|
|
358
|
+
rpcUrl,
|
|
359
|
+
noteTransportUrl,
|
|
360
|
+
seed,
|
|
361
|
+
storeName,
|
|
362
|
+
getKeyCb,
|
|
363
|
+
insertKeyCb,
|
|
364
|
+
signCb
|
|
365
|
+
) {
|
|
122
366
|
this.rpcUrl = rpcUrl;
|
|
123
367
|
this.noteTransportUrl = noteTransportUrl;
|
|
124
368
|
this.seed = seed;
|
|
369
|
+
this.storeName = storeName;
|
|
125
370
|
this.getKeyCb = getKeyCb;
|
|
126
371
|
this.insertKeyCb = insertKeyCb;
|
|
127
372
|
this.signCb = signCb;
|
|
@@ -200,11 +445,13 @@ class WebClient {
|
|
|
200
445
|
const { resolve, reject } = this.pendingRequests.get(requestId);
|
|
201
446
|
this.pendingRequests.delete(requestId);
|
|
202
447
|
if (error) {
|
|
448
|
+
const workerError =
|
|
449
|
+
error instanceof Error ? error : deserializeError(error);
|
|
203
450
|
console.error(
|
|
204
451
|
`WebClient: Error from worker in ${methodName}:`,
|
|
205
|
-
|
|
452
|
+
workerError
|
|
206
453
|
);
|
|
207
|
-
reject(
|
|
454
|
+
reject(workerError);
|
|
208
455
|
} else {
|
|
209
456
|
resolve(result);
|
|
210
457
|
}
|
|
@@ -212,19 +459,7 @@ class WebClient {
|
|
|
212
459
|
});
|
|
213
460
|
|
|
214
461
|
// Once the worker script has loaded, initialize the worker.
|
|
215
|
-
this.loaded.then(() =>
|
|
216
|
-
this.worker.postMessage({
|
|
217
|
-
action: WorkerAction.INIT,
|
|
218
|
-
args: [
|
|
219
|
-
this.rpcUrl,
|
|
220
|
-
this.noteTransportUrl,
|
|
221
|
-
this.seed,
|
|
222
|
-
!!this.getKeyCb,
|
|
223
|
-
!!this.insertKeyCb,
|
|
224
|
-
!!this.signCb,
|
|
225
|
-
],
|
|
226
|
-
});
|
|
227
|
-
});
|
|
462
|
+
this.loaded.then(() => this.initializeWorker());
|
|
228
463
|
} else {
|
|
229
464
|
console.log("WebClient: Web Workers are not available.");
|
|
230
465
|
// Worker not available; set up fallback values.
|
|
@@ -239,6 +474,24 @@ class WebClient {
|
|
|
239
474
|
this.wasmWebClientPromise = null;
|
|
240
475
|
}
|
|
241
476
|
|
|
477
|
+
// TODO: This will soon conflict with some changes in main.
|
|
478
|
+
// More context here:
|
|
479
|
+
// https://github.com/0xMiden/miden-client/pull/1645?notification_referrer_id=NT_kwHOA1yg7NoAJVJlcG9zaXRvcnk7NjU5MzQzNzAyO0lzc3VlOzM3OTY4OTU1Nzk¬ifications_query=is%3Aunread#discussion_r2696075480
|
|
480
|
+
initializeWorker() {
|
|
481
|
+
this.worker.postMessage({
|
|
482
|
+
action: WorkerAction.INIT,
|
|
483
|
+
args: [
|
|
484
|
+
this.rpcUrl,
|
|
485
|
+
this.noteTransportUrl,
|
|
486
|
+
this.seed,
|
|
487
|
+
this.storeName,
|
|
488
|
+
!!this.getKeyCb,
|
|
489
|
+
!!this.insertKeyCb,
|
|
490
|
+
!!this.signCb,
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
242
495
|
async getWasmWebClient() {
|
|
243
496
|
if (this.wasmWebClient) {
|
|
244
497
|
return this.wasmWebClient;
|
|
@@ -261,15 +514,16 @@ class WebClient {
|
|
|
261
514
|
* @param {string} rpcUrl - The RPC URL.
|
|
262
515
|
* @param {string} noteTransportUrl - The note transport URL (optional).
|
|
263
516
|
* @param {string} seed - The seed for the account.
|
|
517
|
+
* @param {string | undefined} network - Optional name for the store. Setting this allows multiple clients to be used in the same browser.
|
|
264
518
|
* @returns {Promise<WebClient>} The fully initialized WebClient.
|
|
265
519
|
*/
|
|
266
|
-
static async createClient(rpcUrl, noteTransportUrl, seed) {
|
|
520
|
+
static async createClient(rpcUrl, noteTransportUrl, seed, network) {
|
|
267
521
|
// Construct the instance (synchronously).
|
|
268
|
-
const instance = new WebClient(rpcUrl, noteTransportUrl, seed);
|
|
522
|
+
const instance = new WebClient(rpcUrl, noteTransportUrl, seed, network);
|
|
269
523
|
|
|
270
524
|
// Wait for the underlying wasmWebClient to be initialized.
|
|
271
525
|
const wasmWebClient = await instance.getWasmWebClient();
|
|
272
|
-
await wasmWebClient.createClient(rpcUrl, noteTransportUrl, seed);
|
|
526
|
+
await wasmWebClient.createClient(rpcUrl, noteTransportUrl, seed, network);
|
|
273
527
|
|
|
274
528
|
// Wait for the worker to be ready
|
|
275
529
|
await instance.ready;
|
|
@@ -301,6 +555,7 @@ class WebClient {
|
|
|
301
555
|
* @param {string} rpcUrl - The RPC URL.
|
|
302
556
|
* @param {string | undefined} noteTransportUrl - The note transport URL (optional).
|
|
303
557
|
* @param {string | undefined} seed - The seed for the account.
|
|
558
|
+
* @param {string | undefined} storeName - Optional name for the store. Setting this allows multiple clients to be used in the same browser.
|
|
304
559
|
* @param {Function | undefined} getKeyCb - The get key callback.
|
|
305
560
|
* @param {Function | undefined} insertKeyCb - The insert key callback.
|
|
306
561
|
* @param {Function | undefined} signCb - The sign callback.
|
|
@@ -310,6 +565,7 @@ class WebClient {
|
|
|
310
565
|
rpcUrl,
|
|
311
566
|
noteTransportUrl,
|
|
312
567
|
seed,
|
|
568
|
+
storeName,
|
|
313
569
|
getKeyCb,
|
|
314
570
|
insertKeyCb,
|
|
315
571
|
signCb
|
|
@@ -319,19 +575,23 @@ class WebClient {
|
|
|
319
575
|
rpcUrl,
|
|
320
576
|
noteTransportUrl,
|
|
321
577
|
seed,
|
|
578
|
+
storeName,
|
|
322
579
|
getKeyCb,
|
|
323
580
|
insertKeyCb,
|
|
324
581
|
signCb
|
|
325
582
|
);
|
|
583
|
+
// Wait for the underlying wasmWebClient to be initialized.
|
|
326
584
|
const wasmWebClient = await instance.getWasmWebClient();
|
|
327
585
|
await wasmWebClient.createClientWithExternalKeystore(
|
|
328
586
|
rpcUrl,
|
|
329
587
|
noteTransportUrl,
|
|
330
588
|
seed,
|
|
589
|
+
storeName,
|
|
331
590
|
getKeyCb,
|
|
332
591
|
insertKeyCb,
|
|
333
592
|
signCb
|
|
334
593
|
);
|
|
594
|
+
|
|
335
595
|
await instance.ready;
|
|
336
596
|
// Return a proxy that forwards missing properties to wasmWebClient.
|
|
337
597
|
return new Proxy(instance, {
|
|
@@ -400,7 +660,7 @@ class WebClient {
|
|
|
400
660
|
);
|
|
401
661
|
return wasm.Account.deserialize(new Uint8Array(serializedAccountBytes));
|
|
402
662
|
} catch (error) {
|
|
403
|
-
console.error("INDEX.JS: Error in newWallet:", error
|
|
663
|
+
console.error("INDEX.JS: Error in newWallet:", error);
|
|
404
664
|
throw error;
|
|
405
665
|
}
|
|
406
666
|
}
|
|
@@ -440,7 +700,7 @@ class WebClient {
|
|
|
440
700
|
|
|
441
701
|
return wasm.Account.deserialize(new Uint8Array(serializedAccountBytes));
|
|
442
702
|
} catch (error) {
|
|
443
|
-
console.error("INDEX.JS: Error in newFaucet:", error
|
|
703
|
+
console.error("INDEX.JS: Error in newFaucet:", error);
|
|
444
704
|
throw error;
|
|
445
705
|
}
|
|
446
706
|
}
|
|
@@ -469,83 +729,69 @@ class WebClient {
|
|
|
469
729
|
|
|
470
730
|
return transactionResult.id();
|
|
471
731
|
} catch (error) {
|
|
472
|
-
console.error(
|
|
473
|
-
"INDEX.JS: Error in submitNewTransaction:",
|
|
474
|
-
error.toString()
|
|
475
|
-
);
|
|
732
|
+
console.error("INDEX.JS: Error in submitNewTransaction:", error);
|
|
476
733
|
throw error;
|
|
477
734
|
}
|
|
478
735
|
}
|
|
479
736
|
|
|
480
|
-
async
|
|
737
|
+
async submitNewTransactionWithProver(accountId, transactionRequest, prover) {
|
|
481
738
|
try {
|
|
482
739
|
if (!this.worker) {
|
|
483
740
|
const wasmWebClient = await this.getWasmWebClient();
|
|
484
|
-
return await wasmWebClient.
|
|
741
|
+
return await wasmWebClient.submitNewTransactionWithProver(
|
|
485
742
|
accountId,
|
|
486
|
-
transactionRequest
|
|
743
|
+
transactionRequest,
|
|
744
|
+
prover
|
|
487
745
|
);
|
|
488
746
|
}
|
|
489
747
|
|
|
490
748
|
const wasm = await getWasmOrThrow();
|
|
491
749
|
const serializedTransactionRequest = transactionRequest.serialize();
|
|
492
|
-
const
|
|
493
|
-
|
|
750
|
+
const proverPayload = prover.serialize();
|
|
751
|
+
const result = await this.callMethodWithWorker(
|
|
752
|
+
MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER,
|
|
494
753
|
accountId.toString(),
|
|
495
|
-
serializedTransactionRequest
|
|
754
|
+
serializedTransactionRequest,
|
|
755
|
+
proverPayload
|
|
496
756
|
);
|
|
497
757
|
|
|
498
|
-
|
|
499
|
-
new Uint8Array(
|
|
758
|
+
const transactionResult = wasm.TransactionResult.deserialize(
|
|
759
|
+
new Uint8Array(result.serializedTransactionResult)
|
|
500
760
|
);
|
|
761
|
+
|
|
762
|
+
return transactionResult.id();
|
|
501
763
|
} catch (error) {
|
|
502
|
-
console.error(
|
|
764
|
+
console.error(
|
|
765
|
+
"INDEX.JS: Error in submitNewTransactionWithProver:",
|
|
766
|
+
error
|
|
767
|
+
);
|
|
503
768
|
throw error;
|
|
504
769
|
}
|
|
505
770
|
}
|
|
506
771
|
|
|
507
|
-
async
|
|
772
|
+
async executeTransaction(accountId, transactionRequest) {
|
|
508
773
|
try {
|
|
509
774
|
if (!this.worker) {
|
|
510
775
|
const wasmWebClient = await this.getWasmWebClient();
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
);
|
|
515
|
-
const submissionHeight = await wasmWebClient.submitProvenTransaction(
|
|
516
|
-
proven,
|
|
517
|
-
transactionResult
|
|
518
|
-
);
|
|
519
|
-
return await wasmWebClient.applyTransaction(
|
|
520
|
-
transactionResult,
|
|
521
|
-
submissionHeight
|
|
776
|
+
return await wasmWebClient.executeTransaction(
|
|
777
|
+
accountId,
|
|
778
|
+
transactionRequest
|
|
522
779
|
);
|
|
523
780
|
}
|
|
524
781
|
|
|
525
782
|
const wasm = await getWasmOrThrow();
|
|
526
|
-
const
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
serializedTransactionResult,
|
|
533
|
-
proverPayload
|
|
534
|
-
);
|
|
535
|
-
|
|
536
|
-
if (this instanceof MockWebClient) {
|
|
537
|
-
return wasm.TransactionStoreUpdate.deserialize(
|
|
538
|
-
new Uint8Array(serializedTransactionUpdate)
|
|
539
|
-
);
|
|
540
|
-
}
|
|
783
|
+
const serializedTransactionRequest = transactionRequest.serialize();
|
|
784
|
+
const serializedResultBytes = await this.callMethodWithWorker(
|
|
785
|
+
MethodName.EXECUTE_TRANSACTION,
|
|
786
|
+
accountId.toString(),
|
|
787
|
+
serializedTransactionRequest
|
|
788
|
+
);
|
|
541
789
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
transactionResult,
|
|
545
|
-
submissionHeight
|
|
790
|
+
return wasm.TransactionResult.deserialize(
|
|
791
|
+
new Uint8Array(serializedResultBytes)
|
|
546
792
|
);
|
|
547
793
|
} catch (error) {
|
|
548
|
-
console.error("INDEX.JS: Error in
|
|
794
|
+
console.error("INDEX.JS: Error in executeTransaction:", error);
|
|
549
795
|
throw error;
|
|
550
796
|
}
|
|
551
797
|
}
|
|
@@ -571,28 +817,73 @@ class WebClient {
|
|
|
571
817
|
new Uint8Array(serializedProvenBytes)
|
|
572
818
|
);
|
|
573
819
|
} catch (error) {
|
|
574
|
-
console.error("INDEX.JS: Error in proveTransaction:", error
|
|
820
|
+
console.error("INDEX.JS: Error in proveTransaction:", error);
|
|
575
821
|
throw error;
|
|
576
822
|
}
|
|
577
823
|
}
|
|
578
824
|
|
|
825
|
+
/**
|
|
826
|
+
* Syncs the client state with the node.
|
|
827
|
+
*
|
|
828
|
+
* This method coordinates concurrent sync calls using the Web Locks API when available,
|
|
829
|
+
* with an in-process mutex fallback for older browsers. If a sync is already in progress,
|
|
830
|
+
* subsequent callers will wait and receive the same result (coalescing behavior).
|
|
831
|
+
*
|
|
832
|
+
* @returns {Promise<SyncSummary>} The sync summary
|
|
833
|
+
*/
|
|
579
834
|
async syncState() {
|
|
835
|
+
return this.syncStateWithTimeout(0);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Syncs the client state with the node with an optional timeout.
|
|
840
|
+
*
|
|
841
|
+
* This method coordinates concurrent sync calls using the Web Locks API when available,
|
|
842
|
+
* with an in-process mutex fallback for older browsers. If a sync is already in progress,
|
|
843
|
+
* subsequent callers will wait and receive the same result (coalescing behavior).
|
|
844
|
+
*
|
|
845
|
+
* @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
|
|
846
|
+
* @returns {Promise<SyncSummary>} The sync summary
|
|
847
|
+
*/
|
|
848
|
+
async syncStateWithTimeout(timeoutMs = 0) {
|
|
849
|
+
// Use storeName as the database ID for lock coordination
|
|
850
|
+
const dbId = this.storeName || "default";
|
|
851
|
+
|
|
580
852
|
try {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
853
|
+
// Acquire the sync lock (coordinates concurrent calls)
|
|
854
|
+
const lockHandle = await acquireSyncLock(dbId, timeoutMs);
|
|
855
|
+
|
|
856
|
+
if (!lockHandle.acquired) {
|
|
857
|
+
// We're coalescing - return the result from the in-progress sync
|
|
858
|
+
return lockHandle.coalescedResult;
|
|
584
859
|
}
|
|
585
860
|
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
861
|
+
// We acquired the lock - perform the sync
|
|
862
|
+
try {
|
|
863
|
+
let result;
|
|
864
|
+
if (!this.worker) {
|
|
865
|
+
const wasmWebClient = await this.getWasmWebClient();
|
|
866
|
+
result = await wasmWebClient.syncStateImpl();
|
|
867
|
+
} else {
|
|
868
|
+
const wasm = await getWasmOrThrow();
|
|
869
|
+
const serializedSyncSummaryBytes = await this.callMethodWithWorker(
|
|
870
|
+
MethodName.SYNC_STATE
|
|
871
|
+
);
|
|
872
|
+
result = wasm.SyncSummary.deserialize(
|
|
873
|
+
new Uint8Array(serializedSyncSummaryBytes)
|
|
874
|
+
);
|
|
875
|
+
}
|
|
590
876
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
877
|
+
// Release the lock with the result
|
|
878
|
+
releaseSyncLock(dbId, result);
|
|
879
|
+
return result;
|
|
880
|
+
} catch (error) {
|
|
881
|
+
// Release the lock with the error
|
|
882
|
+
releaseSyncLockWithError(dbId, error);
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
594
885
|
} catch (error) {
|
|
595
|
-
console.error("INDEX.JS: Error in syncState:", error
|
|
886
|
+
console.error("INDEX.JS: Error in syncState:", error);
|
|
596
887
|
throw error;
|
|
597
888
|
}
|
|
598
889
|
}
|
|
@@ -606,7 +897,14 @@ class WebClient {
|
|
|
606
897
|
|
|
607
898
|
class MockWebClient extends WebClient {
|
|
608
899
|
constructor(seed) {
|
|
609
|
-
super(null, seed);
|
|
900
|
+
super(null, null, seed, "mock");
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
initializeWorker() {
|
|
904
|
+
this.worker.postMessage({
|
|
905
|
+
action: WorkerAction.INIT_MOCK,
|
|
906
|
+
args: [this.seed],
|
|
907
|
+
});
|
|
610
908
|
}
|
|
611
909
|
|
|
612
910
|
/**
|
|
@@ -656,65 +954,103 @@ class MockWebClient extends WebClient {
|
|
|
656
954
|
});
|
|
657
955
|
}
|
|
658
956
|
|
|
957
|
+
/**
|
|
958
|
+
* Syncs the mock client state.
|
|
959
|
+
*
|
|
960
|
+
* This method coordinates concurrent sync calls using the Web Locks API when available,
|
|
961
|
+
* with an in-process mutex fallback for older browsers. If a sync is already in progress,
|
|
962
|
+
* subsequent callers will wait and receive the same result (coalescing behavior).
|
|
963
|
+
*
|
|
964
|
+
* @returns {Promise<SyncSummary>} The sync summary
|
|
965
|
+
*/
|
|
659
966
|
async syncState() {
|
|
967
|
+
return this.syncStateWithTimeout(0);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Syncs the mock client state with an optional timeout.
|
|
972
|
+
*
|
|
973
|
+
* @param {number} timeoutMs - Timeout in milliseconds (0 = no timeout)
|
|
974
|
+
* @returns {Promise<SyncSummary>} The sync summary
|
|
975
|
+
*/
|
|
976
|
+
async syncStateWithTimeout(timeoutMs = 0) {
|
|
977
|
+
const dbId = this.storeName || "mock";
|
|
978
|
+
|
|
660
979
|
try {
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
|
|
980
|
+
const lockHandle = await acquireSyncLock(dbId, timeoutMs);
|
|
981
|
+
|
|
982
|
+
if (!lockHandle.acquired) {
|
|
983
|
+
return lockHandle.coalescedResult;
|
|
664
984
|
}
|
|
665
985
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
wasmWebClient.
|
|
986
|
+
try {
|
|
987
|
+
let result;
|
|
988
|
+
const wasmWebClient = await this.getWasmWebClient();
|
|
669
989
|
|
|
670
|
-
|
|
990
|
+
if (!this.worker) {
|
|
991
|
+
result = await wasmWebClient.syncStateImpl();
|
|
992
|
+
} else {
|
|
993
|
+
let serializedMockChain = wasmWebClient.serializeMockChain().buffer;
|
|
994
|
+
let serializedMockNoteTransportNode =
|
|
995
|
+
wasmWebClient.serializeMockNoteTransportNode().buffer;
|
|
671
996
|
|
|
672
|
-
|
|
673
|
-
MethodName.SYNC_STATE_MOCK,
|
|
674
|
-
serializedMockChain,
|
|
675
|
-
serializedMockNoteTransportNode
|
|
676
|
-
);
|
|
997
|
+
const wasm = await getWasmOrThrow();
|
|
677
998
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
999
|
+
const serializedSyncSummaryBytes = await this.callMethodWithWorker(
|
|
1000
|
+
MethodName.SYNC_STATE_MOCK,
|
|
1001
|
+
serializedMockChain,
|
|
1002
|
+
serializedMockNoteTransportNode
|
|
1003
|
+
);
|
|
1004
|
+
|
|
1005
|
+
result = wasm.SyncSummary.deserialize(
|
|
1006
|
+
new Uint8Array(serializedSyncSummaryBytes)
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
releaseSyncLock(dbId, result);
|
|
1011
|
+
return result;
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
releaseSyncLockWithError(dbId, error);
|
|
1014
|
+
throw error;
|
|
1015
|
+
}
|
|
681
1016
|
} catch (error) {
|
|
682
|
-
console.error("INDEX.JS: Error in syncState:", error
|
|
1017
|
+
console.error("INDEX.JS: Error in syncState:", error);
|
|
683
1018
|
throw error;
|
|
684
1019
|
}
|
|
685
1020
|
}
|
|
686
1021
|
|
|
687
|
-
async
|
|
1022
|
+
async submitNewTransaction(accountId, transactionRequest) {
|
|
688
1023
|
try {
|
|
689
1024
|
if (!this.worker) {
|
|
690
|
-
return await super.
|
|
1025
|
+
return await super.submitNewTransaction(accountId, transactionRequest);
|
|
691
1026
|
}
|
|
692
1027
|
|
|
693
1028
|
const wasmWebClient = await this.getWasmWebClient();
|
|
694
1029
|
const wasm = await getWasmOrThrow();
|
|
695
|
-
const
|
|
696
|
-
const proverPayload = prover ? prover.serialize() : null;
|
|
1030
|
+
const serializedTransactionRequest = transactionRequest.serialize();
|
|
697
1031
|
const serializedMockChain = wasmWebClient.serializeMockChain().buffer;
|
|
698
1032
|
const serializedMockNoteTransportNode =
|
|
699
1033
|
wasmWebClient.serializeMockNoteTransportNode().buffer;
|
|
700
1034
|
|
|
701
1035
|
const result = await this.callMethodWithWorker(
|
|
702
|
-
MethodName.
|
|
703
|
-
|
|
704
|
-
|
|
1036
|
+
MethodName.SUBMIT_NEW_TRANSACTION_MOCK,
|
|
1037
|
+
accountId.toString(),
|
|
1038
|
+
serializedTransactionRequest,
|
|
705
1039
|
serializedMockChain,
|
|
706
1040
|
serializedMockNoteTransportNode
|
|
707
1041
|
);
|
|
1042
|
+
|
|
708
1043
|
const newMockChain = new Uint8Array(result.serializedMockChain);
|
|
709
1044
|
const newMockNoteTransportNode = result.serializedMockNoteTransportNode
|
|
710
1045
|
? new Uint8Array(result.serializedMockNoteTransportNode)
|
|
711
1046
|
: undefined;
|
|
712
1047
|
|
|
1048
|
+
const transactionResult = wasm.TransactionResult.deserialize(
|
|
1049
|
+
new Uint8Array(result.serializedTransactionResult)
|
|
1050
|
+
);
|
|
1051
|
+
|
|
713
1052
|
if (!(this instanceof MockWebClient)) {
|
|
714
|
-
return
|
|
715
|
-
transactionResult,
|
|
716
|
-
result.submissionHeight
|
|
717
|
-
);
|
|
1053
|
+
return transactionResult.id();
|
|
718
1054
|
}
|
|
719
1055
|
|
|
720
1056
|
this.wasmWebClient = new wasm.WebClient();
|
|
@@ -725,32 +1061,36 @@ class MockWebClient extends WebClient {
|
|
|
725
1061
|
newMockNoteTransportNode
|
|
726
1062
|
);
|
|
727
1063
|
|
|
728
|
-
return
|
|
729
|
-
new Uint8Array(result.serializedTransactionUpdate)
|
|
730
|
-
);
|
|
1064
|
+
return transactionResult.id();
|
|
731
1065
|
} catch (error) {
|
|
732
|
-
console.error("INDEX.JS: Error in
|
|
1066
|
+
console.error("INDEX.JS: Error in submitNewTransaction:", error);
|
|
733
1067
|
throw error;
|
|
734
1068
|
}
|
|
735
1069
|
}
|
|
736
1070
|
|
|
737
|
-
async
|
|
1071
|
+
async submitNewTransactionWithProver(accountId, transactionRequest, prover) {
|
|
738
1072
|
try {
|
|
739
1073
|
if (!this.worker) {
|
|
740
|
-
return await super.
|
|
1074
|
+
return await super.submitNewTransactionWithProver(
|
|
1075
|
+
accountId,
|
|
1076
|
+
transactionRequest,
|
|
1077
|
+
prover
|
|
1078
|
+
);
|
|
741
1079
|
}
|
|
742
1080
|
|
|
743
1081
|
const wasmWebClient = await this.getWasmWebClient();
|
|
744
1082
|
const wasm = await getWasmOrThrow();
|
|
745
1083
|
const serializedTransactionRequest = transactionRequest.serialize();
|
|
1084
|
+
const proverPayload = prover.serialize();
|
|
746
1085
|
const serializedMockChain = wasmWebClient.serializeMockChain().buffer;
|
|
747
1086
|
const serializedMockNoteTransportNode =
|
|
748
1087
|
wasmWebClient.serializeMockNoteTransportNode().buffer;
|
|
749
1088
|
|
|
750
1089
|
const result = await this.callMethodWithWorker(
|
|
751
|
-
MethodName.
|
|
1090
|
+
MethodName.SUBMIT_NEW_TRANSACTION_WITH_PROVER_MOCK,
|
|
752
1091
|
accountId.toString(),
|
|
753
1092
|
serializedTransactionRequest,
|
|
1093
|
+
proverPayload,
|
|
754
1094
|
serializedMockChain,
|
|
755
1095
|
serializedMockNoteTransportNode
|
|
756
1096
|
);
|
|
@@ -779,8 +1119,8 @@ class MockWebClient extends WebClient {
|
|
|
779
1119
|
return transactionResult.id();
|
|
780
1120
|
} catch (error) {
|
|
781
1121
|
console.error(
|
|
782
|
-
"INDEX.JS: Error in
|
|
783
|
-
error
|
|
1122
|
+
"INDEX.JS: Error in submitNewTransactionWithProver:",
|
|
1123
|
+
error
|
|
784
1124
|
);
|
|
785
1125
|
throw error;
|
|
786
1126
|
}
|