@provablehq/sdk 0.10.5-rc.1 → 0.10.6-rc.1
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/mainnet/browser.cjs +927 -357
- package/dist/mainnet/browser.cjs.map +1 -1
- package/dist/mainnet/browser.d.cts +6 -4
- package/dist/mainnet/browser.d.ts +6 -4
- package/dist/mainnet/browser.js +923 -364
- package/dist/mainnet/browser.js.map +1 -1
- package/dist/mainnet/keys/keystore/indexeddb.d.cts +49 -0
- package/dist/mainnet/keys/keystore/indexeddb.d.ts +49 -0
- package/dist/mainnet/keys/provider/memory.d.cts +1 -1
- package/dist/mainnet/keys/provider/memory.d.ts +1 -1
- package/dist/mainnet/models/record-scanner/error.d.cts +1 -1
- package/dist/mainnet/models/record-scanner/error.d.ts +1 -1
- package/dist/mainnet/models/record-scanner/registrationResult.d.cts +1 -1
- package/dist/mainnet/models/record-scanner/registrationResult.d.ts +1 -1
- package/dist/mainnet/network-client.d.cts +17 -1
- package/dist/mainnet/network-client.d.ts +17 -1
- package/dist/mainnet/node.cjs +11 -0
- package/dist/mainnet/node.cjs.map +1 -1
- package/dist/mainnet/node.js +2 -2
- package/dist/mainnet/program-manager.d.cts +61 -1
- package/dist/mainnet/program-manager.d.ts +61 -1
- package/dist/mainnet/record-scanner.d.cts +2 -10
- package/dist/mainnet/record-scanner.d.ts +2 -10
- package/dist/mainnet/utils/index.d.cts +3 -0
- package/dist/mainnet/utils/index.d.ts +3 -0
- package/dist/mainnet/utils/logger.d.cts +30 -0
- package/dist/mainnet/utils/logger.d.ts +30 -0
- package/dist/mainnet/wasm.d.cts +1 -1
- package/dist/mainnet/wasm.d.ts +1 -1
- package/dist/testnet/browser.cjs +927 -357
- package/dist/testnet/browser.cjs.map +1 -1
- package/dist/testnet/browser.d.cts +6 -4
- package/dist/testnet/browser.d.ts +6 -4
- package/dist/testnet/browser.js +923 -364
- package/dist/testnet/browser.js.map +1 -1
- package/dist/testnet/keys/keystore/indexeddb.d.cts +49 -0
- package/dist/testnet/keys/keystore/indexeddb.d.ts +49 -0
- package/dist/testnet/keys/provider/memory.d.cts +1 -1
- package/dist/testnet/keys/provider/memory.d.ts +1 -1
- package/dist/testnet/models/record-scanner/error.d.cts +1 -1
- package/dist/testnet/models/record-scanner/error.d.ts +1 -1
- package/dist/testnet/models/record-scanner/registrationResult.d.cts +1 -1
- package/dist/testnet/models/record-scanner/registrationResult.d.ts +1 -1
- package/dist/testnet/network-client.d.cts +17 -1
- package/dist/testnet/network-client.d.ts +17 -1
- package/dist/testnet/node.cjs +11 -0
- package/dist/testnet/node.cjs.map +1 -1
- package/dist/testnet/node.js +2 -2
- package/dist/testnet/program-manager.d.cts +61 -1
- package/dist/testnet/program-manager.d.ts +61 -1
- package/dist/testnet/record-scanner.d.cts +2 -10
- package/dist/testnet/record-scanner.d.ts +2 -10
- package/dist/testnet/utils/index.d.cts +3 -0
- package/dist/testnet/utils/index.d.ts +3 -0
- package/dist/testnet/utils/logger.d.cts +30 -0
- package/dist/testnet/utils/logger.d.ts +30 -0
- package/dist/testnet/wasm.d.cts +1 -1
- package/dist/testnet/wasm.d.ts +1 -1
- package/package.json +2 -2
- package/dist/mainnet/models/record-scanner/registrationRequest.d.cts +0 -13
- package/dist/mainnet/models/record-scanner/registrationRequest.d.ts +0 -13
- package/dist/testnet/models/record-scanner/registrationRequest.d.cts +0 -13
- package/dist/testnet/models/record-scanner/registrationRequest.d.ts +0 -13
- /package/dist/mainnet/{utils.d.cts → utils/utils.d.cts} +0 -0
- /package/dist/mainnet/{utils.d.ts → utils/utils.d.ts} +0 -0
- /package/dist/testnet/{utils.d.cts → utils/utils.d.cts} +0 -0
- /package/dist/testnet/{utils.d.ts → utils/utils.d.ts} +0 -0
package/dist/mainnet/browser.cjs
CHANGED
|
@@ -5,6 +5,406 @@ var mainnet_js = require('@provablehq/wasm/mainnet.js');
|
|
|
5
5
|
var nobleSodium = require('@serenity-kit/noble-sodium');
|
|
6
6
|
var base = require('@scure/base');
|
|
7
7
|
|
|
8
|
+
const LEVEL_PRIORITY = {
|
|
9
|
+
silent: 0,
|
|
10
|
+
error: 1,
|
|
11
|
+
warn: 2,
|
|
12
|
+
info: 3,
|
|
13
|
+
debug: 4,
|
|
14
|
+
};
|
|
15
|
+
let currentLevel = "info";
|
|
16
|
+
/**
|
|
17
|
+
* Set the SDK log level. Levels are hierarchical:
|
|
18
|
+
* - "silent" — suppress all SDK logging
|
|
19
|
+
* - "error" — only errors
|
|
20
|
+
* - "warn" — errors + warnings
|
|
21
|
+
* - "info" — errors + warnings + info (default)
|
|
22
|
+
* - "debug" — everything including debug traces
|
|
23
|
+
*/
|
|
24
|
+
function setLogLevel(level) {
|
|
25
|
+
currentLevel = level;
|
|
26
|
+
}
|
|
27
|
+
/** Returns the current SDK log level. */
|
|
28
|
+
function getLogLevel() {
|
|
29
|
+
return currentLevel;
|
|
30
|
+
}
|
|
31
|
+
function shouldLog(level) {
|
|
32
|
+
return LEVEL_PRIORITY[level] <= LEVEL_PRIORITY[currentLevel];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* SDK logger object following console.log/warn/error/debug syntax.
|
|
36
|
+
* Respects the current log level set via setLogLevel().
|
|
37
|
+
*/
|
|
38
|
+
const logger = {
|
|
39
|
+
log(...args) {
|
|
40
|
+
if (shouldLog("info"))
|
|
41
|
+
console.log(...args);
|
|
42
|
+
},
|
|
43
|
+
warn(...args) {
|
|
44
|
+
if (shouldLog("warn"))
|
|
45
|
+
console.warn(...args);
|
|
46
|
+
},
|
|
47
|
+
error(...args) {
|
|
48
|
+
if (shouldLog("error"))
|
|
49
|
+
console.error(...args);
|
|
50
|
+
},
|
|
51
|
+
debug(...args) {
|
|
52
|
+
if (shouldLog("debug"))
|
|
53
|
+
console.debug(...args);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Error thrown when a key locator is invalid for filesystem use.
|
|
59
|
+
* Used to prevent path traversal and other filesystem injection when deriving paths from locators.
|
|
60
|
+
*
|
|
61
|
+
* @extends Error
|
|
62
|
+
*/
|
|
63
|
+
class InvalidLocatorError extends Error {
|
|
64
|
+
locator;
|
|
65
|
+
reason;
|
|
66
|
+
/**
|
|
67
|
+
* @param message - Human-readable description of the validation failure.
|
|
68
|
+
* @param locator - The invalid locator string that failed validation.
|
|
69
|
+
* @param reason - Machine-readable reason code for the failure.
|
|
70
|
+
*/
|
|
71
|
+
constructor(message, locator, reason) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.locator = locator;
|
|
74
|
+
this.reason = reason;
|
|
75
|
+
this.name = "InvalidLocatorError";
|
|
76
|
+
Object.setPrototypeOf(this, InvalidLocatorError.prototype);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Error thrown when there is a mismatch between expected and actual key metadata.
|
|
82
|
+
* This can occur during verification of either the checksum or size of a key.
|
|
83
|
+
*
|
|
84
|
+
* @extends Error
|
|
85
|
+
*/
|
|
86
|
+
class KeyVerificationError extends Error {
|
|
87
|
+
locator;
|
|
88
|
+
field;
|
|
89
|
+
expected;
|
|
90
|
+
actual;
|
|
91
|
+
/**
|
|
92
|
+
* Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
|
|
93
|
+
*
|
|
94
|
+
* @param {string} locator - The key locator where the mismatch occurred.
|
|
95
|
+
* @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
|
|
96
|
+
* @param {string} expected - The expected value of the field.
|
|
97
|
+
* @param {string} actual - The actual value encountered.
|
|
98
|
+
*/
|
|
99
|
+
constructor(locator, field, expected, actual) {
|
|
100
|
+
super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
|
|
101
|
+
this.locator = locator;
|
|
102
|
+
this.field = field;
|
|
103
|
+
this.expected = expected;
|
|
104
|
+
this.actual = actual;
|
|
105
|
+
this.name = "ChecksumMismatchError";
|
|
106
|
+
Object.setPrototypeOf(this, KeyVerificationError.prototype);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Computes the SHA-256 checksum of a given set of bytes.
|
|
111
|
+
*
|
|
112
|
+
* @param {Uint8Array} bytes - The bytes to compute the checksum of.
|
|
113
|
+
*/
|
|
114
|
+
async function sha256Hex(bytes) {
|
|
115
|
+
const hash = await crypto.subtle.digest("SHA-256", bytes);
|
|
116
|
+
return Array.from(new Uint8Array(hash))
|
|
117
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
118
|
+
.join("");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
|
|
123
|
+
* Provides functionality to compute and verify cryptographic checksums of keys, storing them
|
|
124
|
+
* in memory for subsequent verification. This implementation is primarily used for testing
|
|
125
|
+
* and development purposes where persistence is not required.
|
|
126
|
+
*
|
|
127
|
+
* Key features:
|
|
128
|
+
* - Computes SHA-256 checksums and sizes for key bytes.
|
|
129
|
+
* - Stores key fingerprints in memory using string locators.
|
|
130
|
+
* - Verifies key bytes against stored or provided fingerprints.
|
|
131
|
+
*
|
|
132
|
+
* @implements {KeyVerifier}
|
|
133
|
+
*/
|
|
134
|
+
class MemKeyVerifier {
|
|
135
|
+
keyStore = {};
|
|
136
|
+
/**
|
|
137
|
+
* Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
|
|
138
|
+
*
|
|
139
|
+
* @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
|
|
140
|
+
* @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
|
|
141
|
+
* @returns {Promise<KeyFingerprint>} Computed key metadata.
|
|
142
|
+
*/
|
|
143
|
+
async computeKeyMetadata(keyMetadata) {
|
|
144
|
+
// Compute the metadata from the key bytes
|
|
145
|
+
const computedFingerprint = {
|
|
146
|
+
checksum: await sha256Hex(keyMetadata.keyBytes),
|
|
147
|
+
size: keyMetadata.keyBytes.length
|
|
148
|
+
};
|
|
149
|
+
// If a KeyFingerprint is provided, verify it matches computed values.
|
|
150
|
+
if (keyMetadata.fingerprint) {
|
|
151
|
+
if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
|
|
152
|
+
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
|
|
153
|
+
}
|
|
154
|
+
if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
|
|
155
|
+
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// If locator is provided, store only the fingerprint
|
|
159
|
+
if (keyMetadata.locator) {
|
|
160
|
+
this.keyStore[keyMetadata.locator] = computedFingerprint;
|
|
161
|
+
}
|
|
162
|
+
return computedFingerprint;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
|
|
166
|
+
* 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
|
|
167
|
+
* 2. If a locator is provided, attempts to verify against stored fingerprint.
|
|
168
|
+
* 3. If neither is available, throws an error.
|
|
169
|
+
*
|
|
170
|
+
* @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
|
|
171
|
+
* @throws {Error} When neither fingerprint nor valid locator is provided for verification.
|
|
172
|
+
* @throws {KeyVerificationError} When size or checksum verification fails.
|
|
173
|
+
* @returns {Promise<void>} Promise that resolves when verification succeeds.
|
|
174
|
+
*/
|
|
175
|
+
async verifyKeyBytes(keyMetadata) {
|
|
176
|
+
if (!keyMetadata.keyBytes) {
|
|
177
|
+
throw new Error("Key bytes must be provided for verification.");
|
|
178
|
+
}
|
|
179
|
+
// Compute the fingerprint for the provided bytes.
|
|
180
|
+
const computedFingerprint = await this.computeKeyMetadata({
|
|
181
|
+
keyBytes: keyMetadata.keyBytes
|
|
182
|
+
});
|
|
183
|
+
// Determine which fingerprint to verify against.
|
|
184
|
+
let fingerprintToVerify;
|
|
185
|
+
if (keyMetadata.fingerprint) {
|
|
186
|
+
// If a key fingerprint is provided, use it.
|
|
187
|
+
fingerprintToVerify = keyMetadata.fingerprint;
|
|
188
|
+
}
|
|
189
|
+
else if (keyMetadata.locator) {
|
|
190
|
+
// Otherwise try to get stored fingerprint by locator.
|
|
191
|
+
fingerprintToVerify = this.keyStore[keyMetadata.locator];
|
|
192
|
+
}
|
|
193
|
+
if (!fingerprintToVerify) {
|
|
194
|
+
throw new Error("Either fingerprint or a valid locator must be provided for verification.");
|
|
195
|
+
}
|
|
196
|
+
// Verify the key size.
|
|
197
|
+
if (fingerprintToVerify.size !== computedFingerprint.size) {
|
|
198
|
+
throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
|
|
199
|
+
}
|
|
200
|
+
// Verify the key checksum.
|
|
201
|
+
if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
|
|
202
|
+
throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Browser-compatible {@link KeyStore} backed by IndexedDB.
|
|
209
|
+
*
|
|
210
|
+
* This is the browser counterpart to {@link LocalFileKeyStore} (which requires Node.js `fs`).
|
|
211
|
+
* It persists proving and verifying keys across page reloads and browser sessions using the
|
|
212
|
+
* IndexedDB API available in all modern browsers and Web Workers.
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* import { IndexedDBKeyStore, ProgramManager } from "@provablehq/sdk";
|
|
217
|
+
*
|
|
218
|
+
* const keyStore = new IndexedDBKeyStore();
|
|
219
|
+
* const pm = new ProgramManager();
|
|
220
|
+
* pm.setKeyStore(keyStore);
|
|
221
|
+
* // Keys synthesized during execution are now cached in IndexedDB
|
|
222
|
+
* // and reloaded automatically on subsequent runs.
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
class IndexedDBKeyStore {
|
|
226
|
+
dbName;
|
|
227
|
+
storeName = "keys";
|
|
228
|
+
keyVerifier = new MemKeyVerifier();
|
|
229
|
+
dbPromise = null;
|
|
230
|
+
/**
|
|
231
|
+
* @param dbName IndexedDB database name. Defaults to `"aleo-keystore"`.
|
|
232
|
+
*/
|
|
233
|
+
constructor(dbName = "aleo-keystore") {
|
|
234
|
+
this.dbName = dbName;
|
|
235
|
+
}
|
|
236
|
+
// -------------------------------------------------------
|
|
237
|
+
// IndexedDB helpers
|
|
238
|
+
// -------------------------------------------------------
|
|
239
|
+
/** Opens (or creates) the database, returning a cached promise. */
|
|
240
|
+
openDB() {
|
|
241
|
+
if (this.dbPromise)
|
|
242
|
+
return this.dbPromise;
|
|
243
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
244
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
245
|
+
request.onupgradeneeded = () => {
|
|
246
|
+
const db = request.result;
|
|
247
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
248
|
+
db.createObjectStore(this.storeName, { keyPath: "locator" });
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
request.onsuccess = () => resolve(request.result);
|
|
252
|
+
request.onerror = () => {
|
|
253
|
+
this.dbPromise = null;
|
|
254
|
+
reject(request.error);
|
|
255
|
+
};
|
|
256
|
+
request.onblocked = () => {
|
|
257
|
+
this.dbPromise = null;
|
|
258
|
+
reject(new DOMException("Database open blocked", "AbortError"));
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
return this.dbPromise;
|
|
262
|
+
}
|
|
263
|
+
/** Runs a single read-write transaction and returns the request result. */
|
|
264
|
+
async tx(mode, fn) {
|
|
265
|
+
const db = await this.openDB();
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
const txn = db.transaction(this.storeName, mode);
|
|
268
|
+
const store = txn.objectStore(this.storeName);
|
|
269
|
+
const req = fn(store);
|
|
270
|
+
let result;
|
|
271
|
+
req.onsuccess = () => { result = req.result; };
|
|
272
|
+
txn.oncomplete = () => resolve(result);
|
|
273
|
+
txn.onerror = () => reject(txn.error);
|
|
274
|
+
txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
// -------------------------------------------------------
|
|
278
|
+
// Locator serialization (mirrors LocalFileKeyStore)
|
|
279
|
+
// -------------------------------------------------------
|
|
280
|
+
validateComponent(value, label) {
|
|
281
|
+
if (value === "" || value === ".") {
|
|
282
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not be empty or "." (got "${value}")`, value, "reserved_name");
|
|
283
|
+
}
|
|
284
|
+
if (value.includes("..")) {
|
|
285
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not contain ".." (got "${value}")`, value, "path_traversal");
|
|
286
|
+
}
|
|
287
|
+
if (value.includes("/") || value.includes("\\") || value.includes("\0")) {
|
|
288
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not contain path separators or null bytes (got "${value}")`, value, "path_separator");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
validateNonNegative(value, label) {
|
|
292
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
293
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must be a non-negative integer (got ${value})`, String(value), "negative_value");
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
serializeLocator(locator) {
|
|
297
|
+
this.validateComponent(locator.program, "program");
|
|
298
|
+
this.validateComponent(locator.functionName, "functionName");
|
|
299
|
+
this.validateComponent(locator.network, "network");
|
|
300
|
+
this.validateNonNegative(locator.edition, "edition");
|
|
301
|
+
this.validateNonNegative(locator.amendment, "amendment");
|
|
302
|
+
const base = `${locator.program}.${locator.functionName}.e${locator.edition}.a${locator.amendment}.${locator.network}.${locator.keyType}`;
|
|
303
|
+
if (locator.keyType === "translation") {
|
|
304
|
+
this.validateComponent(locator.recordName, "recordName");
|
|
305
|
+
this.validateNonNegative(locator.recordInputPosition, "recordInputPosition");
|
|
306
|
+
return `${base}.${locator.recordName}.${locator.recordInputPosition}`;
|
|
307
|
+
}
|
|
308
|
+
return base;
|
|
309
|
+
}
|
|
310
|
+
checksumToFingerprint(checksum, keyBytes) {
|
|
311
|
+
if (!checksum)
|
|
312
|
+
return undefined;
|
|
313
|
+
return { checksum, size: keyBytes.length };
|
|
314
|
+
}
|
|
315
|
+
// -------------------------------------------------------
|
|
316
|
+
// KeyStore interface
|
|
317
|
+
// -------------------------------------------------------
|
|
318
|
+
async getKeyBytes(locator) {
|
|
319
|
+
const key = this.serializeLocator(locator);
|
|
320
|
+
const record = await this.tx("readonly", (store) => store.get(key));
|
|
321
|
+
if (!record)
|
|
322
|
+
return null;
|
|
323
|
+
const fingerprint = this.checksumToFingerprint(locator.checksum, record.bytes) ?? record.metadata;
|
|
324
|
+
if (fingerprint) {
|
|
325
|
+
await this.keyVerifier.verifyKeyBytes({
|
|
326
|
+
keyBytes: record.bytes,
|
|
327
|
+
locator: key,
|
|
328
|
+
fingerprint,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return record.bytes;
|
|
332
|
+
}
|
|
333
|
+
async getProvingKey(locator) {
|
|
334
|
+
const bytes = await this.getKeyBytes(locator);
|
|
335
|
+
if (!bytes)
|
|
336
|
+
return null;
|
|
337
|
+
return mainnet_js.ProvingKey.fromBytes(bytes);
|
|
338
|
+
}
|
|
339
|
+
async getVerifyingKey(locator) {
|
|
340
|
+
const bytes = await this.getKeyBytes(locator);
|
|
341
|
+
if (!bytes)
|
|
342
|
+
return null;
|
|
343
|
+
return mainnet_js.VerifyingKey.fromBytes(bytes);
|
|
344
|
+
}
|
|
345
|
+
async setKeys(proverLocator, verifierLocator, keys) {
|
|
346
|
+
const proverKey = this.serializeLocator(proverLocator);
|
|
347
|
+
const verifierKey = this.serializeLocator(verifierLocator);
|
|
348
|
+
const [provingKey, verifyingKey] = keys;
|
|
349
|
+
const [provingKeyBytes, verifyingKeyBytes] = [
|
|
350
|
+
provingKey.toBytes(),
|
|
351
|
+
verifyingKey.toBytes(),
|
|
352
|
+
];
|
|
353
|
+
const [proverFingerprint, verifierFingerprint] = await Promise.all([
|
|
354
|
+
this.keyVerifier.computeKeyMetadata({
|
|
355
|
+
keyBytes: provingKeyBytes,
|
|
356
|
+
locator: proverKey,
|
|
357
|
+
fingerprint: this.checksumToFingerprint(proverLocator.checksum, provingKeyBytes),
|
|
358
|
+
}),
|
|
359
|
+
this.keyVerifier.computeKeyMetadata({
|
|
360
|
+
keyBytes: verifyingKeyBytes,
|
|
361
|
+
locator: verifierKey,
|
|
362
|
+
fingerprint: this.checksumToFingerprint(verifierLocator.checksum, verifyingKeyBytes),
|
|
363
|
+
}),
|
|
364
|
+
]);
|
|
365
|
+
const proverRecord = { locator: proverKey, bytes: provingKeyBytes, metadata: proverFingerprint };
|
|
366
|
+
const verifierRecord = { locator: verifierKey, bytes: verifyingKeyBytes, metadata: verifierFingerprint };
|
|
367
|
+
// Write both in a single transaction for atomicity.
|
|
368
|
+
const db = await this.openDB();
|
|
369
|
+
await new Promise((resolve, reject) => {
|
|
370
|
+
const txn = db.transaction(this.storeName, "readwrite");
|
|
371
|
+
const store = txn.objectStore(this.storeName);
|
|
372
|
+
store.put(proverRecord);
|
|
373
|
+
store.put(verifierRecord);
|
|
374
|
+
txn.oncomplete = () => resolve();
|
|
375
|
+
txn.onerror = () => reject(txn.error);
|
|
376
|
+
txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
async setKeyBytes(keyBytes, locator) {
|
|
380
|
+
const key = this.serializeLocator(locator);
|
|
381
|
+
const computedMetadata = await this.keyVerifier.computeKeyMetadata({
|
|
382
|
+
keyBytes,
|
|
383
|
+
locator: key,
|
|
384
|
+
fingerprint: this.checksumToFingerprint(locator.checksum, keyBytes),
|
|
385
|
+
});
|
|
386
|
+
const record = { locator: key, bytes: keyBytes, metadata: computedMetadata };
|
|
387
|
+
await this.tx("readwrite", (store) => store.put(record));
|
|
388
|
+
}
|
|
389
|
+
async getKeyMetadata(locator) {
|
|
390
|
+
const key = this.serializeLocator(locator);
|
|
391
|
+
const record = await this.tx("readonly", (store) => store.get(key));
|
|
392
|
+
return record?.metadata ?? null;
|
|
393
|
+
}
|
|
394
|
+
async has(locator) {
|
|
395
|
+
const key = this.serializeLocator(locator);
|
|
396
|
+
const count = await this.tx("readonly", (store) => store.count(IDBKeyRange.only(key)));
|
|
397
|
+
return count > 0;
|
|
398
|
+
}
|
|
399
|
+
async delete(locator) {
|
|
400
|
+
const key = this.serializeLocator(locator);
|
|
401
|
+
await this.tx("readwrite", (store) => store.delete(key));
|
|
402
|
+
}
|
|
403
|
+
async clear() {
|
|
404
|
+
await this.tx("readwrite", (store) => store.clear());
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
8
408
|
/**
|
|
9
409
|
* Encrypt an authorization with a cryptobox X25519 public key (libsodium-compatible wire format).
|
|
10
410
|
*
|
|
@@ -164,7 +564,7 @@ class Account {
|
|
|
164
564
|
this._privateKey = this.privateKeyFromParams(params);
|
|
165
565
|
}
|
|
166
566
|
catch (e) {
|
|
167
|
-
|
|
567
|
+
logger.error("Wrong parameter", e);
|
|
168
568
|
throw new Error("Wrong Parameter");
|
|
169
569
|
}
|
|
170
570
|
this._viewKey = mainnet_js.ViewKey.from_private_key(this._privateKey);
|
|
@@ -635,7 +1035,7 @@ function environment() {
|
|
|
635
1035
|
}
|
|
636
1036
|
}
|
|
637
1037
|
function logAndThrow(message) {
|
|
638
|
-
|
|
1038
|
+
logger.error(message);
|
|
639
1039
|
throw new Error(message);
|
|
640
1040
|
}
|
|
641
1041
|
/** Default transport — wraps global fetch to avoid illegal-invocation errors in browsers. */
|
|
@@ -696,7 +1096,7 @@ async function retryWithBackoff(fn, { maxAttempts = 5, baseDelay = 100, jitter,
|
|
|
696
1096
|
const jitterAmount = jitter ?? baseDelay;
|
|
697
1097
|
const actualJitter = Math.floor(Math.random() * jitterAmount);
|
|
698
1098
|
const delay = baseDelay * 2 ** (attempt - 1) + actualJitter;
|
|
699
|
-
|
|
1099
|
+
logger.warn(`Retry ${attempt}/${maxAttempts} failed. Retrying in ${delay}ms...`);
|
|
700
1100
|
await new Promise((res) => setTimeout(res, delay));
|
|
701
1101
|
}
|
|
702
1102
|
}
|
|
@@ -865,7 +1265,7 @@ class AleoNetworkClient {
|
|
|
865
1265
|
else {
|
|
866
1266
|
this.headers = {
|
|
867
1267
|
// This is replaced by the actual version by a Rollup plugin
|
|
868
|
-
"X-Aleo-SDK-Version": "0.10.
|
|
1268
|
+
"X-Aleo-SDK-Version": "0.10.6-rc.1",
|
|
869
1269
|
"X-Aleo-environment": environment(),
|
|
870
1270
|
};
|
|
871
1271
|
}
|
|
@@ -881,7 +1281,7 @@ class AleoNetworkClient {
|
|
|
881
1281
|
else {
|
|
882
1282
|
this.headers = {
|
|
883
1283
|
// This is replaced by the actual version by a Rollup plugin
|
|
884
|
-
"X-Aleo-SDK-Version": "0.10.
|
|
1284
|
+
"X-Aleo-SDK-Version": "0.10.6-rc.1",
|
|
885
1285
|
"X-Aleo-environment": environment(),
|
|
886
1286
|
};
|
|
887
1287
|
}
|
|
@@ -1197,7 +1597,7 @@ class AleoNetworkClient {
|
|
|
1197
1597
|
continue;
|
|
1198
1598
|
}
|
|
1199
1599
|
catch (error) {
|
|
1200
|
-
|
|
1600
|
+
logger.log("Found unspent record!");
|
|
1201
1601
|
}
|
|
1202
1602
|
}
|
|
1203
1603
|
// Add the record to the list of records if the user did not specify amounts.
|
|
@@ -1257,14 +1657,14 @@ class AleoNetworkClient {
|
|
|
1257
1657
|
}
|
|
1258
1658
|
catch (error) {
|
|
1259
1659
|
// If there is an error fetching blocks, log it and keep searching
|
|
1260
|
-
|
|
1660
|
+
logger.warn("Error fetching blocks in range: " +
|
|
1261
1661
|
start.toString() +
|
|
1262
1662
|
"-" +
|
|
1263
1663
|
end.toString());
|
|
1264
|
-
|
|
1664
|
+
logger.warn("Error: ", error);
|
|
1265
1665
|
failures += 1;
|
|
1266
1666
|
if (failures > 10) {
|
|
1267
|
-
|
|
1667
|
+
logger.warn("10 failures fetching records reached. Returning records fetched so far");
|
|
1268
1668
|
return records;
|
|
1269
1669
|
}
|
|
1270
1670
|
}
|
|
@@ -1714,6 +2114,30 @@ class AleoNetworkClient {
|
|
|
1714
2114
|
this.ctx = {};
|
|
1715
2115
|
}
|
|
1716
2116
|
}
|
|
2117
|
+
/**
|
|
2118
|
+
* Returns the current edition and amendment count for a program.
|
|
2119
|
+
*
|
|
2120
|
+
* @param {string} programId - The program ID (e.g. "hello_hello.aleo")
|
|
2121
|
+
* @returns {{ program_id: string, edition: number, amendment_count: number }}
|
|
2122
|
+
*
|
|
2123
|
+
* @example
|
|
2124
|
+
* const networkClient = new AleoNetworkClient("https://api.provable.com/v2");
|
|
2125
|
+
* const info = await networkClient.getProgramAmendmentCount("hello_hello.aleo");
|
|
2126
|
+
* console.log(info.edition, info.amendment_count);
|
|
2127
|
+
*/
|
|
2128
|
+
async getProgramAmendmentCount(programId) {
|
|
2129
|
+
try {
|
|
2130
|
+
this.ctx = { "X-ALEO-METHOD": "getProgramAmendmentCount" };
|
|
2131
|
+
const raw = await this.fetchRaw("/programs/" + programId + "/amendment_count");
|
|
2132
|
+
return JSON.parse(raw);
|
|
2133
|
+
}
|
|
2134
|
+
catch (error) {
|
|
2135
|
+
throw new Error(`Error fetching amendment count for ${programId}: ${error}`);
|
|
2136
|
+
}
|
|
2137
|
+
finally {
|
|
2138
|
+
this.ctx = {};
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
1717
2141
|
/**
|
|
1718
2142
|
* Returns a program object from a program ID or program source code.
|
|
1719
2143
|
*
|
|
@@ -2381,7 +2805,7 @@ class AleoNetworkClient {
|
|
|
2381
2805
|
options.jwtData = jwtData;
|
|
2382
2806
|
}
|
|
2383
2807
|
else {
|
|
2384
|
-
|
|
2808
|
+
logger.warn('JWT or both apiKey and consumerId are required when using the Provable API');
|
|
2385
2809
|
}
|
|
2386
2810
|
}
|
|
2387
2811
|
// Create the necessary headers to hit the provable api.
|
|
@@ -2511,10 +2935,10 @@ class AleoNetworkClient {
|
|
|
2511
2935
|
let text = "";
|
|
2512
2936
|
try {
|
|
2513
2937
|
text = await res.text();
|
|
2514
|
-
|
|
2938
|
+
logger.warn("Response text from server:", text);
|
|
2515
2939
|
}
|
|
2516
2940
|
catch (err) {
|
|
2517
|
-
|
|
2941
|
+
logger.warn("Failed to read response text:", err);
|
|
2518
2942
|
}
|
|
2519
2943
|
// If the transaction ID is malformed (e.g. invalid checksum, wrong length),
|
|
2520
2944
|
// the API returns a 4XX with "Invalid URL" — we treat this as a fatal error and stop polling.
|
|
@@ -2525,7 +2949,7 @@ class AleoNetworkClient {
|
|
|
2525
2949
|
return reject(new Error(`Malformed transaction ID: ${text}`));
|
|
2526
2950
|
}
|
|
2527
2951
|
// Log and continue polling for 404s or 5XX errors in case a tx doesn't exist yet
|
|
2528
|
-
|
|
2952
|
+
logger.warn("Non-OK response (retrying):", res.status, text);
|
|
2529
2953
|
return;
|
|
2530
2954
|
}
|
|
2531
2955
|
const data = parseJSON(await res.text());
|
|
@@ -2539,7 +2963,7 @@ class AleoNetworkClient {
|
|
|
2539
2963
|
}
|
|
2540
2964
|
}
|
|
2541
2965
|
catch (err) {
|
|
2542
|
-
|
|
2966
|
+
logger.error("Polling error:", err);
|
|
2543
2967
|
}
|
|
2544
2968
|
}, checkInterval);
|
|
2545
2969
|
});
|
|
@@ -2547,7 +2971,7 @@ class AleoNetworkClient {
|
|
|
2547
2971
|
}
|
|
2548
2972
|
|
|
2549
2973
|
/**
|
|
2550
|
-
* Error thrown when a record scanner request fails (e.g. /register
|
|
2974
|
+
* Error thrown when a record scanner request fails (e.g. /register/encrypted).
|
|
2551
2975
|
* Includes HTTP status so callers can handle 422 vs 500 etc.
|
|
2552
2976
|
*/
|
|
2553
2977
|
class RecordScannerRequestError extends Error {
|
|
@@ -2707,7 +3131,7 @@ class AleoKeyProvider {
|
|
|
2707
3131
|
* @returns {FunctionKeyPair} Proving and verifying keys for the specified program
|
|
2708
3132
|
*/
|
|
2709
3133
|
getKeys(keyId) {
|
|
2710
|
-
|
|
3134
|
+
logger.debug(`Checking if key exists in cache. KeyId: ${keyId}`);
|
|
2711
3135
|
if (this.cache.has(keyId)) {
|
|
2712
3136
|
const [provingKeyBytes, verifyingKeyBytes] = (this.cache.get(keyId));
|
|
2713
3137
|
return [
|
|
@@ -2808,9 +3232,9 @@ class AleoKeyProvider {
|
|
|
2808
3232
|
];
|
|
2809
3233
|
}
|
|
2810
3234
|
else {
|
|
2811
|
-
|
|
3235
|
+
logger.debug("Fetching proving keys from url " + proverUrl);
|
|
2812
3236
|
const provingKey = (mainnet_js.ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
|
|
2813
|
-
|
|
3237
|
+
logger.debug("Fetching verifying keys " + verifierUrl);
|
|
2814
3238
|
const verifyingKey = (await this.getVerifyingKey(verifierUrl));
|
|
2815
3239
|
this.cache.set(cacheKey, [
|
|
2816
3240
|
provingKey.toBytes(),
|
|
@@ -2850,7 +3274,7 @@ class AleoKeyProvider {
|
|
|
2850
3274
|
return mainnet_js.ProvingKey.fromBytes(value[0]);
|
|
2851
3275
|
}
|
|
2852
3276
|
else {
|
|
2853
|
-
|
|
3277
|
+
logger.debug("Fetching proving keys from url " + proverUrl);
|
|
2854
3278
|
const provingKey = (mainnet_js.ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
|
|
2855
3279
|
return provingKey;
|
|
2856
3280
|
}
|
|
@@ -3032,163 +3456,13 @@ class AleoKeyProvider {
|
|
|
3032
3456
|
return (mainnet_js.VerifyingKey.fromBytes(await this.fetchBytes(verifierUri)));
|
|
3033
3457
|
}
|
|
3034
3458
|
catch (inner) {
|
|
3035
|
-
throw new Error("Invalid verifying key. Error: " + inner.message);
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
}
|
|
3039
|
-
}
|
|
3040
|
-
unBondPublicKeys() {
|
|
3041
|
-
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
|
|
3045
|
-
/**
|
|
3046
|
-
* Error thrown when there is a mismatch between expected and actual key metadata.
|
|
3047
|
-
* This can occur during verification of either the checksum or size of a key.
|
|
3048
|
-
*
|
|
3049
|
-
* @extends Error
|
|
3050
|
-
*/
|
|
3051
|
-
class KeyVerificationError extends Error {
|
|
3052
|
-
locator;
|
|
3053
|
-
field;
|
|
3054
|
-
expected;
|
|
3055
|
-
actual;
|
|
3056
|
-
/**
|
|
3057
|
-
* Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
|
|
3058
|
-
*
|
|
3059
|
-
* @param {string} locator - The key locator where the mismatch occurred.
|
|
3060
|
-
* @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
|
|
3061
|
-
* @param {string} expected - The expected value of the field.
|
|
3062
|
-
* @param {string} actual - The actual value encountered.
|
|
3063
|
-
*/
|
|
3064
|
-
constructor(locator, field, expected, actual) {
|
|
3065
|
-
super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
|
|
3066
|
-
this.locator = locator;
|
|
3067
|
-
this.field = field;
|
|
3068
|
-
this.expected = expected;
|
|
3069
|
-
this.actual = actual;
|
|
3070
|
-
this.name = "ChecksumMismatchError";
|
|
3071
|
-
Object.setPrototypeOf(this, KeyVerificationError.prototype);
|
|
3072
|
-
}
|
|
3073
|
-
}
|
|
3074
|
-
/**
|
|
3075
|
-
* Computes the SHA-256 checksum of a given set of bytes.
|
|
3076
|
-
*
|
|
3077
|
-
* @param {Uint8Array} bytes - The bytes to compute the checksum of.
|
|
3078
|
-
*/
|
|
3079
|
-
async function sha256Hex(bytes) {
|
|
3080
|
-
const hash = await crypto.subtle.digest("SHA-256", bytes);
|
|
3081
|
-
return Array.from(new Uint8Array(hash))
|
|
3082
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
3083
|
-
.join("");
|
|
3084
|
-
}
|
|
3085
|
-
|
|
3086
|
-
/**
|
|
3087
|
-
* In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
|
|
3088
|
-
* Provides functionality to compute and verify cryptographic checksums of keys, storing them
|
|
3089
|
-
* in memory for subsequent verification. This implementation is primarily used for testing
|
|
3090
|
-
* and development purposes where persistence is not required.
|
|
3091
|
-
*
|
|
3092
|
-
* Key features:
|
|
3093
|
-
* - Computes SHA-256 checksums and sizes for key bytes.
|
|
3094
|
-
* - Stores key fingerprints in memory using string locators.
|
|
3095
|
-
* - Verifies key bytes against stored or provided fingerprints.
|
|
3096
|
-
*
|
|
3097
|
-
* @implements {KeyVerifier}
|
|
3098
|
-
*/
|
|
3099
|
-
class MemKeyVerifier {
|
|
3100
|
-
keyStore = {};
|
|
3101
|
-
/**
|
|
3102
|
-
* Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
|
|
3103
|
-
*
|
|
3104
|
-
* @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
|
|
3105
|
-
* @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
|
|
3106
|
-
* @returns {Promise<KeyFingerprint>} Computed key metadata.
|
|
3107
|
-
*/
|
|
3108
|
-
async computeKeyMetadata(keyMetadata) {
|
|
3109
|
-
// Compute the metadata from the key bytes
|
|
3110
|
-
const computedFingerprint = {
|
|
3111
|
-
checksum: await sha256Hex(keyMetadata.keyBytes),
|
|
3112
|
-
size: keyMetadata.keyBytes.length
|
|
3113
|
-
};
|
|
3114
|
-
// If a KeyFingerprint is provided, verify it matches computed values.
|
|
3115
|
-
if (keyMetadata.fingerprint) {
|
|
3116
|
-
if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
|
|
3117
|
-
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
|
|
3118
|
-
}
|
|
3119
|
-
if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
|
|
3120
|
-
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
3123
|
-
// If locator is provided, store only the fingerprint
|
|
3124
|
-
if (keyMetadata.locator) {
|
|
3125
|
-
this.keyStore[keyMetadata.locator] = computedFingerprint;
|
|
3126
|
-
}
|
|
3127
|
-
return computedFingerprint;
|
|
3128
|
-
}
|
|
3129
|
-
/**
|
|
3130
|
-
* Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
|
|
3131
|
-
* 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
|
|
3132
|
-
* 2. If a locator is provided, attempts to verify against stored fingerprint.
|
|
3133
|
-
* 3. If neither is available, throws an error.
|
|
3134
|
-
*
|
|
3135
|
-
* @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
|
|
3136
|
-
* @throws {Error} When neither fingerprint nor valid locator is provided for verification.
|
|
3137
|
-
* @throws {KeyVerificationError} When size or checksum verification fails.
|
|
3138
|
-
* @returns {Promise<void>} Promise that resolves when verification succeeds.
|
|
3139
|
-
*/
|
|
3140
|
-
async verifyKeyBytes(keyMetadata) {
|
|
3141
|
-
if (!keyMetadata.keyBytes) {
|
|
3142
|
-
throw new Error("Key bytes must be provided for verification.");
|
|
3143
|
-
}
|
|
3144
|
-
// Compute the fingerprint for the provided bytes.
|
|
3145
|
-
const computedFingerprint = await this.computeKeyMetadata({
|
|
3146
|
-
keyBytes: keyMetadata.keyBytes
|
|
3147
|
-
});
|
|
3148
|
-
// Determine which fingerprint to verify against.
|
|
3149
|
-
let fingerprintToVerify;
|
|
3150
|
-
if (keyMetadata.fingerprint) {
|
|
3151
|
-
// If a key fingerprint is provided, use it.
|
|
3152
|
-
fingerprintToVerify = keyMetadata.fingerprint;
|
|
3153
|
-
}
|
|
3154
|
-
else if (keyMetadata.locator) {
|
|
3155
|
-
// Otherwise try to get stored fingerprint by locator.
|
|
3156
|
-
fingerprintToVerify = this.keyStore[keyMetadata.locator];
|
|
3157
|
-
}
|
|
3158
|
-
if (!fingerprintToVerify) {
|
|
3159
|
-
throw new Error("Either fingerprint or a valid locator must be provided for verification.");
|
|
3160
|
-
}
|
|
3161
|
-
// Verify the key size.
|
|
3162
|
-
if (fingerprintToVerify.size !== computedFingerprint.size) {
|
|
3163
|
-
throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
|
|
3164
|
-
}
|
|
3165
|
-
// Verify the key checksum.
|
|
3166
|
-
if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
|
|
3167
|
-
throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
|
|
3459
|
+
throw new Error("Invalid verifying key. Error: " + inner.message);
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3168
3462
|
}
|
|
3169
3463
|
}
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
/**
|
|
3173
|
-
* Error thrown when a key locator is invalid for filesystem use.
|
|
3174
|
-
* Used to prevent path traversal and other filesystem injection when deriving paths from locators.
|
|
3175
|
-
*
|
|
3176
|
-
* @extends Error
|
|
3177
|
-
*/
|
|
3178
|
-
class InvalidLocatorError extends Error {
|
|
3179
|
-
locator;
|
|
3180
|
-
reason;
|
|
3181
|
-
/**
|
|
3182
|
-
* @param message - Human-readable description of the validation failure.
|
|
3183
|
-
* @param locator - The invalid locator string that failed validation.
|
|
3184
|
-
* @param reason - Machine-readable reason code for the failure.
|
|
3185
|
-
*/
|
|
3186
|
-
constructor(message, locator, reason) {
|
|
3187
|
-
super(message);
|
|
3188
|
-
this.locator = locator;
|
|
3189
|
-
this.reason = reason;
|
|
3190
|
-
this.name = "InvalidLocatorError";
|
|
3191
|
-
Object.setPrototypeOf(this, InvalidLocatorError.prototype);
|
|
3464
|
+
unBondPublicKeys() {
|
|
3465
|
+
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
|
|
3192
3466
|
}
|
|
3193
3467
|
}
|
|
3194
3468
|
|
|
@@ -3945,12 +4219,12 @@ class NetworkRecordProvider {
|
|
|
3945
4219
|
records = await this.findCreditsRecords([microcredits], searchParameters);
|
|
3946
4220
|
}
|
|
3947
4221
|
catch (e) {
|
|
3948
|
-
|
|
4222
|
+
logger.log("No records found with error:", e);
|
|
3949
4223
|
}
|
|
3950
4224
|
if (records && records.length > 0) {
|
|
3951
4225
|
return records[0];
|
|
3952
4226
|
}
|
|
3953
|
-
|
|
4227
|
+
logger.error("Record not found with error:", records);
|
|
3954
4228
|
throw new Error("Record not found");
|
|
3955
4229
|
}
|
|
3956
4230
|
/**
|
|
@@ -3962,12 +4236,12 @@ class NetworkRecordProvider {
|
|
|
3962
4236
|
records = await this.findRecords(searchParameters);
|
|
3963
4237
|
}
|
|
3964
4238
|
catch (e) {
|
|
3965
|
-
|
|
4239
|
+
logger.log("No records found with error:", e);
|
|
3966
4240
|
}
|
|
3967
4241
|
if (records && records.length > 0) {
|
|
3968
4242
|
return records[0];
|
|
3969
4243
|
}
|
|
3970
|
-
|
|
4244
|
+
logger.error("Record not found with error:", records);
|
|
3971
4245
|
throw new Error("Record not found");
|
|
3972
4246
|
}
|
|
3973
4247
|
/**
|
|
@@ -4062,7 +4336,7 @@ class BlockHeightSearch {
|
|
|
4062
4336
|
* const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" });
|
|
4063
4337
|
* recordScanner.setAccount(account);
|
|
4064
4338
|
* recordScanner.setApiKey("example-api-key");
|
|
4065
|
-
* const result = await recordScanner.
|
|
4339
|
+
* const result = await recordScanner.registerEncrypted(viewKey, 0);
|
|
4066
4340
|
* if (result.ok) { const uuid = result.data.uuid; }
|
|
4067
4341
|
*
|
|
4068
4342
|
* const filter = {
|
|
@@ -4317,40 +4591,6 @@ class RecordScanner {
|
|
|
4317
4591
|
}
|
|
4318
4592
|
throw err;
|
|
4319
4593
|
}
|
|
4320
|
-
/**
|
|
4321
|
-
* Register the account with the record scanning service (unencrypted POST /register). Does not throw if a valid error response from the record scanner is received; returns a result object instead.
|
|
4322
|
-
*
|
|
4323
|
-
* @param {ViewKey} viewKey The view key to register.
|
|
4324
|
-
* @param {number} startBlock The block height to start scanning from.
|
|
4325
|
-
* @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
|
|
4326
|
-
*/
|
|
4327
|
-
async register(viewKey, startBlock) {
|
|
4328
|
-
try {
|
|
4329
|
-
const request = {
|
|
4330
|
-
view_key: viewKey.to_string(),
|
|
4331
|
-
start: startBlock,
|
|
4332
|
-
};
|
|
4333
|
-
const response = await this.request(new Request(`${this.url}/register`, {
|
|
4334
|
-
method: "POST",
|
|
4335
|
-
headers: { "Content-Type": "application/json" },
|
|
4336
|
-
body: JSON.stringify(request),
|
|
4337
|
-
}));
|
|
4338
|
-
const data = await response.json();
|
|
4339
|
-
// If the uuid is not set, set it on the scanner.
|
|
4340
|
-
if (!this.uuid) {
|
|
4341
|
-
this.uuid = data.uuid;
|
|
4342
|
-
}
|
|
4343
|
-
// Add the view key to the local scanner's view keys if configured to do so.
|
|
4344
|
-
if (this.cacheViewKeysOnRegister) {
|
|
4345
|
-
this.addViewKey(viewKey);
|
|
4346
|
-
}
|
|
4347
|
-
return { ok: true, data };
|
|
4348
|
-
}
|
|
4349
|
-
catch (err) {
|
|
4350
|
-
console.error(`Failed to register view key: ${err}`);
|
|
4351
|
-
return this.handleRequestError(err);
|
|
4352
|
-
}
|
|
4353
|
-
}
|
|
4354
4594
|
/**
|
|
4355
4595
|
* Fetches an ephemeral public key from the record scanning service for use with registerEncrypted.
|
|
4356
4596
|
* Follows the same pattern as the delegated proving service /pubkey endpoint.
|
|
@@ -4592,7 +4832,7 @@ class RecordScanner {
|
|
|
4592
4832
|
throw new Error("Record not found");
|
|
4593
4833
|
}
|
|
4594
4834
|
catch (error) {
|
|
4595
|
-
|
|
4835
|
+
logger.error(`Failed to find record: ${error}`);
|
|
4596
4836
|
throw error;
|
|
4597
4837
|
}
|
|
4598
4838
|
}
|
|
@@ -4753,7 +4993,7 @@ class RecordScanner {
|
|
|
4753
4993
|
return record;
|
|
4754
4994
|
}
|
|
4755
4995
|
catch (error) {
|
|
4756
|
-
|
|
4996
|
+
logger.error(`Failed to find credits record: ${error}`);
|
|
4757
4997
|
throw error;
|
|
4758
4998
|
}
|
|
4759
4999
|
}
|
|
@@ -4802,7 +5042,7 @@ class RecordScanner {
|
|
|
4802
5042
|
});
|
|
4803
5043
|
}
|
|
4804
5044
|
catch (error) {
|
|
4805
|
-
|
|
5045
|
+
logger.error(`Failed to find credits records: ${error}`);
|
|
4806
5046
|
throw error;
|
|
4807
5047
|
}
|
|
4808
5048
|
}
|
|
@@ -4840,7 +5080,7 @@ class RecordScanner {
|
|
|
4840
5080
|
return response;
|
|
4841
5081
|
}
|
|
4842
5082
|
catch (error) {
|
|
4843
|
-
|
|
5083
|
+
logger.error(`Failed to make request to ${req.url}: ${error}`);
|
|
4844
5084
|
throw error;
|
|
4845
5085
|
}
|
|
4846
5086
|
}
|
|
@@ -5197,17 +5437,19 @@ class ProgramManager {
|
|
|
5197
5437
|
networkClient;
|
|
5198
5438
|
recordProvider;
|
|
5199
5439
|
inclusionKeysLoaded = false;
|
|
5440
|
+
_keyStore;
|
|
5200
5441
|
/** Create a new instance of the ProgramManager
|
|
5201
5442
|
*
|
|
5202
5443
|
* @param { string | undefined } host A host uri running the official Aleo API
|
|
5203
5444
|
* @param { FunctionKeyProvider | undefined } keyProvider A key provider that implements {@link FunctionKeyProvider} interface
|
|
5204
5445
|
* @param { RecordProvider | undefined } recordProvider A record provider that implements {@link RecordProvider} interface
|
|
5205
5446
|
*/
|
|
5206
|
-
constructor(host, keyProvider, recordProvider, networkClientOptions) {
|
|
5447
|
+
constructor(host, keyProvider, recordProvider, networkClientOptions, keyStore) {
|
|
5207
5448
|
this.host = host ? host : "https://api.provable.com/v2";
|
|
5208
5449
|
this.networkClient = new AleoNetworkClient(this.host, networkClientOptions);
|
|
5209
5450
|
this.keyProvider = keyProvider ? keyProvider : new AleoKeyProvider({ transport: networkClientOptions?.transport });
|
|
5210
5451
|
this.recordProvider = recordProvider;
|
|
5452
|
+
this._keyStore = keyStore;
|
|
5211
5453
|
}
|
|
5212
5454
|
/**
|
|
5213
5455
|
* Pre-load the inclusion prover for offline execution. Required when the
|
|
@@ -5285,6 +5527,330 @@ class ProgramManager {
|
|
|
5285
5527
|
setRecordProvider(recordProvider) {
|
|
5286
5528
|
this.recordProvider = recordProvider;
|
|
5287
5529
|
}
|
|
5530
|
+
/**
|
|
5531
|
+
* Set the key store for automatic key caching across executions.
|
|
5532
|
+
*
|
|
5533
|
+
* @param {KeyStore} keyStore
|
|
5534
|
+
*/
|
|
5535
|
+
setKeyStore(keyStore) {
|
|
5536
|
+
this._keyStore = keyStore;
|
|
5537
|
+
}
|
|
5538
|
+
/**
|
|
5539
|
+
* Build a ProgramImportsBuilder from a program and its imports.
|
|
5540
|
+
* Fetches imports from the network if not provided, resolves transitive
|
|
5541
|
+
* dependencies, and optionally pre-loads cached keys from the KeyStore.
|
|
5542
|
+
*
|
|
5543
|
+
* @param loadKeys When true (default), loads cached proving/verifying keys
|
|
5544
|
+
* from the KeyStore into the builder. Set to false for authorization and
|
|
5545
|
+
* proving request paths where keys are not synthesized.
|
|
5546
|
+
*/
|
|
5547
|
+
async buildProgramImports(program, imports, loadKeys, entryFunction) {
|
|
5548
|
+
const builder = new mainnet_js.ProgramImports();
|
|
5549
|
+
const programSource = typeof program === "string" ? program : program.toString();
|
|
5550
|
+
const programObj = mainnet_js.Program.fromString(programSource);
|
|
5551
|
+
const importNames = programObj.getImports();
|
|
5552
|
+
if (importNames.length === 0 && (!imports || Object.keys(imports).length === 0)) {
|
|
5553
|
+
return { builder, importEditions: new Map() };
|
|
5554
|
+
}
|
|
5555
|
+
let resolvedImports = {};
|
|
5556
|
+
if (imports) {
|
|
5557
|
+
resolvedImports = { ...imports };
|
|
5558
|
+
}
|
|
5559
|
+
if (importNames.length > 0 && !imports) {
|
|
5560
|
+
try {
|
|
5561
|
+
resolvedImports = await this.networkClient.getProgramImports(programSource);
|
|
5562
|
+
}
|
|
5563
|
+
catch (e) {
|
|
5564
|
+
console.warn(`Failed to resolve program imports from network: ${e}.`);
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
// Build a map of which functions each import actually calls,
|
|
5568
|
+
// so we only load keys for functions in the call chain.
|
|
5569
|
+
const calledFunctions = ProgramManager.callGraphToMap(programObj.getCallGraph(entryFunction));
|
|
5570
|
+
// Phase 1: Collect all programs via BFS, discovering transitive imports.
|
|
5571
|
+
// The initial getProgramImports call above already resolves the full
|
|
5572
|
+
// transitive closure via recursive DFS. The BFS loop here only needs
|
|
5573
|
+
// to handle user-provided imports whose transitive deps may not yet be
|
|
5574
|
+
// known. For each unknown import we issue a single getProgram() call
|
|
5575
|
+
// (not a full recursive getProgramImports) and fetch siblings in
|
|
5576
|
+
// parallel to minimize round-trips.
|
|
5577
|
+
const collected = new Map();
|
|
5578
|
+
const collectQueue = Object.entries(resolvedImports).map(([name, src]) => [name, typeof src === "string" ? src : src.toString()]);
|
|
5579
|
+
while (collectQueue.length > 0) {
|
|
5580
|
+
const [name, source] = collectQueue.shift();
|
|
5581
|
+
if (collected.has(name))
|
|
5582
|
+
continue;
|
|
5583
|
+
collected.set(name, source);
|
|
5584
|
+
// Discover transitive imports for collection (call-graph tracing
|
|
5585
|
+
// happens in a separate pass after topological sorting).
|
|
5586
|
+
try {
|
|
5587
|
+
const subImports = ProgramManager.getImportNames(source);
|
|
5588
|
+
if (subImports.length > 0) {
|
|
5589
|
+
// Fetch only unknown transitive imports — the source for
|
|
5590
|
+
// programs already in collected/resolvedImports is known, so
|
|
5591
|
+
// we skip them. Fetches within a BFS level run in parallel.
|
|
5592
|
+
const unknownImports = subImports.filter((id) => !collected.has(id) && !(id in resolvedImports));
|
|
5593
|
+
if (unknownImports.length > 0) {
|
|
5594
|
+
const fetched = await Promise.all(unknownImports.map(async (id) => {
|
|
5595
|
+
try {
|
|
5596
|
+
const src = await this.networkClient.getProgram(id);
|
|
5597
|
+
return [id, src];
|
|
5598
|
+
}
|
|
5599
|
+
catch {
|
|
5600
|
+
return null;
|
|
5601
|
+
}
|
|
5602
|
+
}));
|
|
5603
|
+
for (const entry of fetched) {
|
|
5604
|
+
if (entry && !collected.has(entry[0])) {
|
|
5605
|
+
collectQueue.push(entry);
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5611
|
+
catch (e) {
|
|
5612
|
+
console.warn(`Failed to resolve transitive imports for ${name}: ${e}`);
|
|
5613
|
+
}
|
|
5614
|
+
}
|
|
5615
|
+
// Phase 2: Add programs in topological order (leaves first).
|
|
5616
|
+
// A simple DFS post-order ensures dependencies are added before
|
|
5617
|
+
// dependents, regardless of the order collected entries arrived.
|
|
5618
|
+
const sorted = [];
|
|
5619
|
+
const visited = new Set();
|
|
5620
|
+
const visit = (name) => {
|
|
5621
|
+
if (visited.has(name))
|
|
5622
|
+
return;
|
|
5623
|
+
visited.add(name);
|
|
5624
|
+
const source = collected.get(name);
|
|
5625
|
+
if (!source)
|
|
5626
|
+
return;
|
|
5627
|
+
for (const dep of ProgramManager.getImportNames(source)) {
|
|
5628
|
+
if (collected.has(dep))
|
|
5629
|
+
visit(dep);
|
|
5630
|
+
}
|
|
5631
|
+
sorted.push([name, source]);
|
|
5632
|
+
};
|
|
5633
|
+
for (const [name] of collected)
|
|
5634
|
+
visit(name);
|
|
5635
|
+
// When scoped, trace call graphs in reverse topological order
|
|
5636
|
+
// (dependents before leaves) so parent calls propagate before
|
|
5637
|
+
// children are traced. sorted is leaves-first, so reverse it.
|
|
5638
|
+
if (entryFunction) {
|
|
5639
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
5640
|
+
const [name, source] = sorted[i];
|
|
5641
|
+
const functionsNeeded = calledFunctions.get(name);
|
|
5642
|
+
if (!functionsNeeded)
|
|
5643
|
+
continue;
|
|
5644
|
+
try {
|
|
5645
|
+
const importProgram = mainnet_js.Program.fromString(source);
|
|
5646
|
+
for (const fn of functionsNeeded) {
|
|
5647
|
+
const reachable = ProgramManager.callGraphToMap(importProgram.getCallGraph(fn));
|
|
5648
|
+
for (const [p, fns] of reachable) {
|
|
5649
|
+
if (!calledFunctions.has(p)) {
|
|
5650
|
+
calledFunctions.set(p, new Set(fns));
|
|
5651
|
+
}
|
|
5652
|
+
else {
|
|
5653
|
+
for (const f of fns)
|
|
5654
|
+
calledFunctions.get(p).add(f);
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
}
|
|
5659
|
+
catch (e) {
|
|
5660
|
+
console.warn(`Failed to trace call graph for ${name}: ${e}`);
|
|
5661
|
+
}
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
// Resolve editions for all imports in parallel (each import may have
|
|
5665
|
+
// a different edition than the top-level program).
|
|
5666
|
+
const importEditions = new Map();
|
|
5667
|
+
await Promise.all(sorted.map(async ([name]) => {
|
|
5668
|
+
importEditions.set(name, await this.resolveEditionAndAmendment(name));
|
|
5669
|
+
}));
|
|
5670
|
+
for (const [name, source] of sorted) {
|
|
5671
|
+
if (builder.contains(name))
|
|
5672
|
+
continue;
|
|
5673
|
+
const { edition: importEdition, amendment: importAmendment } = importEditions.get(name);
|
|
5674
|
+
try {
|
|
5675
|
+
builder.addProgram(name, source, importEdition);
|
|
5676
|
+
}
|
|
5677
|
+
catch (e) {
|
|
5678
|
+
console.warn(`Failed to add import ${name} to builder: ${e}`);
|
|
5679
|
+
continue;
|
|
5680
|
+
}
|
|
5681
|
+
if (loadKeys) {
|
|
5682
|
+
const calledFns = calledFunctions.get(name);
|
|
5683
|
+
await this.loadKeysFromStore(builder, name, calledFns ? [...calledFns] : [], importEdition, importAmendment);
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5686
|
+
return { builder, importEditions };
|
|
5687
|
+
}
|
|
5688
|
+
/**
|
|
5689
|
+
* Extract `import program_name.aleo;` names from program source via regex.
|
|
5690
|
+
* Avoids a WASM round-trip compared to Program.fromString + getImports.
|
|
5691
|
+
*/
|
|
5692
|
+
static getImportNames(programSource) {
|
|
5693
|
+
const result = [];
|
|
5694
|
+
const importPattern = /^\s*import\s+([a-zA-Z_][a-zA-Z0-9_]*\.aleo)\s*;/gm;
|
|
5695
|
+
let match;
|
|
5696
|
+
while ((match = importPattern.exec(programSource)) !== null) {
|
|
5697
|
+
result.push(match[1]);
|
|
5698
|
+
}
|
|
5699
|
+
return result;
|
|
5700
|
+
}
|
|
5701
|
+
/**
|
|
5702
|
+
* Convert the JS object returned by Program.getCallGraph() into a
|
|
5703
|
+
* Map<string, Set<string>> for use in buildProgramImports.
|
|
5704
|
+
*/
|
|
5705
|
+
static callGraphToMap(callGraph) {
|
|
5706
|
+
const result = new Map();
|
|
5707
|
+
for (const [programName, functions] of Object.entries(callGraph)) {
|
|
5708
|
+
result.set(programName, new Set(functions));
|
|
5709
|
+
}
|
|
5710
|
+
return result;
|
|
5711
|
+
}
|
|
5712
|
+
/**
|
|
5713
|
+
* Resolve the active KeyStore, preferring the directly-set _keyStore
|
|
5714
|
+
* over the KeyProvider's keyStore().
|
|
5715
|
+
*/
|
|
5716
|
+
async resolveKeyStore() {
|
|
5717
|
+
if (this._keyStore)
|
|
5718
|
+
return this._keyStore;
|
|
5719
|
+
try {
|
|
5720
|
+
return await this.keyProvider?.keyStore?.();
|
|
5721
|
+
}
|
|
5722
|
+
catch {
|
|
5723
|
+
return undefined;
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
/**
|
|
5727
|
+
* Resolve the edition and amendment count for a program from the network.
|
|
5728
|
+
* Returns `{ edition, amendment }` or falls back to `{ edition: 1, amendment: 0 }`.
|
|
5729
|
+
*/
|
|
5730
|
+
async resolveEditionAndAmendment(programName, fallbackEdition) {
|
|
5731
|
+
try {
|
|
5732
|
+
const info = await this.networkClient.getProgramAmendmentCount(programName);
|
|
5733
|
+
return { edition: info.edition, amendment: info.amendment_count };
|
|
5734
|
+
}
|
|
5735
|
+
catch (e) {
|
|
5736
|
+
console.warn(`Error finding edition/amendment for ${programName}. Network response: '${e.message}'. Defaulting to edition ${fallbackEdition ?? 1}, amendment 0.`);
|
|
5737
|
+
return { edition: fallbackEdition ?? 1, amendment: 0 };
|
|
5738
|
+
}
|
|
5739
|
+
}
|
|
5740
|
+
/**
|
|
5741
|
+
* Load cached proving/verifying keys from the KeyStore into a ProgramImportsBuilder.
|
|
5742
|
+
* Only loads keys for the specified functions — returns immediately if
|
|
5743
|
+
* functionNames is empty or undefined.
|
|
5744
|
+
* Resolves edition and amendment from the network for accurate key locator
|
|
5745
|
+
* construction when not explicitly provided.
|
|
5746
|
+
*/
|
|
5747
|
+
async loadKeysFromStore(builder, programName, functionNames, edition, amendment) {
|
|
5748
|
+
if (!functionNames || functionNames.length === 0)
|
|
5749
|
+
return;
|
|
5750
|
+
const keyStore = await this.resolveKeyStore();
|
|
5751
|
+
if (!keyStore)
|
|
5752
|
+
return;
|
|
5753
|
+
// Resolve edition and amendment from the network if not fully provided.
|
|
5754
|
+
if (edition === undefined || amendment === undefined) {
|
|
5755
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
5756
|
+
edition = edition ?? resolved.edition;
|
|
5757
|
+
amendment = amendment ?? resolved.amendment;
|
|
5758
|
+
}
|
|
5759
|
+
for (const fnName of functionNames) {
|
|
5760
|
+
const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
|
|
5761
|
+
const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
|
|
5762
|
+
try {
|
|
5763
|
+
const pk = await keyStore.getProvingKey(pkLocator);
|
|
5764
|
+
if (pk)
|
|
5765
|
+
builder.addProvingKey(programName, fnName, pk);
|
|
5766
|
+
const vk = await keyStore.getVerifyingKey(vkLocator);
|
|
5767
|
+
if (vk)
|
|
5768
|
+
builder.addVerifyingKey(programName, fnName, vk);
|
|
5769
|
+
}
|
|
5770
|
+
catch (e) {
|
|
5771
|
+
console.debug(`Failed to load keys for ${programName}/${fnName}: ${e}`);
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
}
|
|
5775
|
+
/**
|
|
5776
|
+
* Persist newly synthesized keys from the returned ProgramImportsBuilder
|
|
5777
|
+
* into the KeyStore. Only writes keys that are not already in the store,
|
|
5778
|
+
* avoiding unnecessary writes of large proving keys.
|
|
5779
|
+
* Fetches each program's current edition and amendment count from the network
|
|
5780
|
+
* for accurate key locator construction.
|
|
5781
|
+
*/
|
|
5782
|
+
async persistExtractedKeys(builder, importEditions) {
|
|
5783
|
+
const keyStore = await this.resolveKeyStore();
|
|
5784
|
+
if (!keyStore)
|
|
5785
|
+
return;
|
|
5786
|
+
const programNames = Array.from(builder.programNames());
|
|
5787
|
+
// Reuse editions resolved during buildProgramImports when available,
|
|
5788
|
+
// falling back to parallel network resolution for any missing programs.
|
|
5789
|
+
const editionMap = importEditions ?? new Map();
|
|
5790
|
+
const missing = programNames.filter((name) => !editionMap.has(name));
|
|
5791
|
+
if (missing.length > 0) {
|
|
5792
|
+
await Promise.all(missing.map(async (name) => {
|
|
5793
|
+
editionMap.set(name, await this.resolveEditionAndAmendment(name));
|
|
5794
|
+
}));
|
|
5795
|
+
}
|
|
5796
|
+
for (const programName of programNames) {
|
|
5797
|
+
const { edition, amendment } = editionMap.get(programName);
|
|
5798
|
+
let fns;
|
|
5799
|
+
try {
|
|
5800
|
+
fns = Array.from(builder.functionKeysAvailable(programName));
|
|
5801
|
+
}
|
|
5802
|
+
catch (e) {
|
|
5803
|
+
console.debug(`Failed to query keys for ${programName}: ${e}`);
|
|
5804
|
+
continue;
|
|
5805
|
+
}
|
|
5806
|
+
for (const fnName of fns) {
|
|
5807
|
+
try {
|
|
5808
|
+
const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
|
|
5809
|
+
const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
|
|
5810
|
+
// Skip keys already in the store.
|
|
5811
|
+
if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
|
|
5812
|
+
continue;
|
|
5813
|
+
}
|
|
5814
|
+
const pk = builder.getProvingKey(programName, fnName);
|
|
5815
|
+
const vk = builder.getVerifyingKey(programName, fnName);
|
|
5816
|
+
if (pk && vk) {
|
|
5817
|
+
await keyStore.setKeys(pkLocator, vkLocator, [pk, vk]);
|
|
5818
|
+
}
|
|
5819
|
+
}
|
|
5820
|
+
catch (e) {
|
|
5821
|
+
console.debug(`Failed to persist key for ${programName}/${fnName}: ${e}`);
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
/**
|
|
5827
|
+
* Resolve top-level function keys, checking the KeyStore first and
|
|
5828
|
+
* falling back to the KeyProvider.
|
|
5829
|
+
*/
|
|
5830
|
+
async resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment) {
|
|
5831
|
+
const keyStore = await this.resolveKeyStore();
|
|
5832
|
+
if (keyStore) {
|
|
5833
|
+
try {
|
|
5834
|
+
const pkLocator = provingKeyLocator(programName, functionName, edition, amendment);
|
|
5835
|
+
const vkLocator = verifyingKeyLocator(programName, functionName, edition, amendment);
|
|
5836
|
+
if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
|
|
5837
|
+
const pk = await keyStore.getProvingKey(pkLocator);
|
|
5838
|
+
const vk = await keyStore.getVerifyingKey(vkLocator);
|
|
5839
|
+
if (pk && vk)
|
|
5840
|
+
return [pk, vk];
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
5843
|
+
catch {
|
|
5844
|
+
// Fall through to KeyProvider
|
|
5845
|
+
}
|
|
5846
|
+
}
|
|
5847
|
+
try {
|
|
5848
|
+
return (await this.keyProvider.functionKeys(keySearchParams));
|
|
5849
|
+
}
|
|
5850
|
+
catch {
|
|
5851
|
+
return undefined;
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5288
5854
|
/**
|
|
5289
5855
|
* Set a header in the `AleoNetworkClient`s header map
|
|
5290
5856
|
*
|
|
@@ -5338,7 +5904,7 @@ class ProgramManager {
|
|
|
5338
5904
|
return;
|
|
5339
5905
|
}
|
|
5340
5906
|
catch {
|
|
5341
|
-
|
|
5907
|
+
logger.log("Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly");
|
|
5342
5908
|
}
|
|
5343
5909
|
}
|
|
5344
5910
|
/**
|
|
@@ -5413,7 +5979,7 @@ class ProgramManager {
|
|
|
5413
5979
|
}
|
|
5414
5980
|
catch (e) {
|
|
5415
5981
|
// Program does not exist on the network, deployment can proceed
|
|
5416
|
-
|
|
5982
|
+
logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
|
|
5417
5983
|
}
|
|
5418
5984
|
if (typeof programSource === "string") {
|
|
5419
5985
|
throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
|
|
@@ -5437,7 +6003,7 @@ class ProgramManager {
|
|
|
5437
6003
|
let fee = priorityFee;
|
|
5438
6004
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
5439
6005
|
if (!feeRecord) {
|
|
5440
|
-
|
|
6006
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
5441
6007
|
const programString = programObject.toString();
|
|
5442
6008
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
5443
6009
|
const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
|
|
@@ -5528,7 +6094,7 @@ class ProgramManager {
|
|
|
5528
6094
|
}
|
|
5529
6095
|
catch (e) {
|
|
5530
6096
|
// Program does not exist on the network, deployment can proceed
|
|
5531
|
-
|
|
6097
|
+
logger.log(`Program ${programObject.id()} does not exist on the network...`);
|
|
5532
6098
|
}
|
|
5533
6099
|
}
|
|
5534
6100
|
catch (e) {
|
|
@@ -5549,7 +6115,7 @@ class ProgramManager {
|
|
|
5549
6115
|
let fee = priorityFee;
|
|
5550
6116
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
5551
6117
|
if (!feeRecord) {
|
|
5552
|
-
|
|
6118
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
5553
6119
|
const programString = programObject.toString();
|
|
5554
6120
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
5555
6121
|
const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
|
|
@@ -5719,15 +6285,9 @@ class ProgramManager {
|
|
|
5719
6285
|
if (programName === undefined) {
|
|
5720
6286
|
programName = programObject.id();
|
|
5721
6287
|
}
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
}
|
|
5726
|
-
catch (e) {
|
|
5727
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
5728
|
-
edition = 0;
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
6288
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6289
|
+
edition = edition ?? resolved.edition;
|
|
6290
|
+
const { amendment } = resolved;
|
|
5731
6291
|
// Get the private key from the account if it is not provided in the parameters
|
|
5732
6292
|
let executionPrivateKey = privateKey;
|
|
5733
6293
|
if (typeof privateKey === "undefined" &&
|
|
@@ -5748,23 +6308,19 @@ class ProgramManager {
|
|
|
5748
6308
|
logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
|
|
5749
6309
|
}
|
|
5750
6310
|
const [feeProvingKey, feeVerifyingKey] = feeKeys;
|
|
5751
|
-
//
|
|
6311
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
6312
|
+
const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, functionName);
|
|
6313
|
+
// Include the top-level program's already-resolved edition so
|
|
6314
|
+
// persistExtractedKeys doesn't make a redundant network call for it.
|
|
6315
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6316
|
+
// If the function proving and verifying keys are not provided, attempt to find them
|
|
5752
6317
|
if (!provingKey || !verifyingKey) {
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
catch (e) {
|
|
5757
|
-
console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
|
|
6318
|
+
const keys = await this.resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment);
|
|
6319
|
+
if (keys) {
|
|
6320
|
+
[provingKey, verifyingKey] = keys;
|
|
5758
6321
|
}
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
const numberOfImports = programObject.getImports().length;
|
|
5762
|
-
if (numberOfImports > 0 && !imports) {
|
|
5763
|
-
try {
|
|
5764
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
5765
|
-
}
|
|
5766
|
-
catch (e) {
|
|
5767
|
-
logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
|
|
6322
|
+
else {
|
|
6323
|
+
console.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
5768
6324
|
}
|
|
5769
6325
|
}
|
|
5770
6326
|
// Get the fee record from the account if it is not provided in the parameters
|
|
@@ -5789,17 +6345,23 @@ class ProgramManager {
|
|
|
5789
6345
|
}
|
|
5790
6346
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
5791
6347
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
5792
|
-
// Build the query: user-provided OfflineQuery for truly offline execution,
|
|
5793
|
-
// CallbackQuery when a custom transport is configured, or undefined to
|
|
5794
|
-
// let WASM use its internal SnapshotQuery with the default fetch.
|
|
5795
6348
|
const query = offlineQuery
|
|
5796
6349
|
? mainnet_js.QueryOption.offlineQuery(offlineQuery)
|
|
5797
6350
|
: this.networkClient.hasCustomTransport
|
|
5798
6351
|
? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
5799
6352
|
: undefined;
|
|
5800
6353
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
5801
|
-
//
|
|
5802
|
-
|
|
6354
|
+
const result = await mainnet_js.ProgramManager.buildExecutionTransaction(executionPrivateKey, program, functionName, preparedInputs, priorityFee, feeRecord, this.host, undefined, // imports (legacy Object path — builder handles resolution)
|
|
6355
|
+
provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, query, edition, programImportsBuilder?.clone());
|
|
6356
|
+
// The clone passed to WASM shares state with the original via
|
|
6357
|
+
// Rc<RefCell<>> — synthesized keys are already visible.
|
|
6358
|
+
try {
|
|
6359
|
+
await this.persistExtractedKeys(programImportsBuilder, importEditions);
|
|
6360
|
+
}
|
|
6361
|
+
catch (e) {
|
|
6362
|
+
console.debug(`Failed to persist extracted keys: ${e}`);
|
|
6363
|
+
}
|
|
6364
|
+
return result;
|
|
5803
6365
|
}
|
|
5804
6366
|
/**
|
|
5805
6367
|
* Builds an execution transaction for submission to the Aleo network from an Authorization and Fee Authorization.
|
|
@@ -5877,6 +6439,10 @@ class ProgramManager {
|
|
|
5877
6439
|
const feeAuthorization = options.feeAuthorization;
|
|
5878
6440
|
const keySearchParams = options.keySearchParams;
|
|
5879
6441
|
const offlineQuery = options.offlineQuery;
|
|
6442
|
+
let edition = options.edition;
|
|
6443
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6444
|
+
edition = edition ?? resolved.edition;
|
|
6445
|
+
const { amendment } = resolved;
|
|
5880
6446
|
let provingKey = options.provingKey;
|
|
5881
6447
|
let verifyingKey = options.verifyingKey;
|
|
5882
6448
|
let program = options.program;
|
|
@@ -5905,24 +6471,17 @@ class ProgramManager {
|
|
|
5905
6471
|
logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
|
|
5906
6472
|
}
|
|
5907
6473
|
const [feeProvingKey, feeVerifyingKey] = feeKeys;
|
|
5908
|
-
//
|
|
6474
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
6475
|
+
const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, authorization.functionName());
|
|
6476
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6477
|
+
// If the function proving and verifying keys are not provided, attempt to find them.
|
|
5909
6478
|
if (!provingKey || !verifyingKey) {
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
catch (e) {
|
|
5914
|
-
console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
|
|
5915
|
-
}
|
|
5916
|
-
}
|
|
5917
|
-
// Resolve the program imports if they exist.
|
|
5918
|
-
console.log("Resolving program imports");
|
|
5919
|
-
const numberOfImports = mainnet_js.Program.fromString(program).getImports().length;
|
|
5920
|
-
if (numberOfImports > 0 && !imports) {
|
|
5921
|
-
try {
|
|
5922
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6479
|
+
const keys = await this.resolveTopLevelKeys(programName, authorization.functionName(), keySearchParams, edition, amendment);
|
|
6480
|
+
if (keys) {
|
|
6481
|
+
[provingKey, verifyingKey] = keys;
|
|
5923
6482
|
}
|
|
5924
|
-
|
|
5925
|
-
|
|
6483
|
+
else {
|
|
6484
|
+
console.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
5926
6485
|
}
|
|
5927
6486
|
}
|
|
5928
6487
|
const query = offlineQuery
|
|
@@ -5931,9 +6490,18 @@ class ProgramManager {
|
|
|
5931
6490
|
? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
5932
6491
|
: undefined;
|
|
5933
6492
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
5934
|
-
// Build an execution transaction from the authorization.
|
|
5935
|
-
|
|
5936
|
-
|
|
6493
|
+
// Build an execution transaction from the authorization. The
|
|
6494
|
+
// ProgramImportsBuilder is consumed by value (ownership transfers to WASM)
|
|
6495
|
+
// and returned enriched with any keys synthesized during execution.
|
|
6496
|
+
const result = await mainnet_js.ProgramManager.executeAuthorization(authorization, feeAuthorization, program, provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, undefined, // imports (legacy Object path — builder handles resolution)
|
|
6497
|
+
this.host, query, programImportsBuilder?.clone(), edition);
|
|
6498
|
+
try {
|
|
6499
|
+
await this.persistExtractedKeys(programImportsBuilder, importEditions);
|
|
6500
|
+
}
|
|
6501
|
+
catch (e) {
|
|
6502
|
+
console.debug(`Failed to persist extracted keys: ${e}`);
|
|
6503
|
+
}
|
|
6504
|
+
return result;
|
|
5937
6505
|
}
|
|
5938
6506
|
/**
|
|
5939
6507
|
* Builds a SnarkVM `Authorization` for a specific function.
|
|
@@ -5996,29 +6564,17 @@ class ProgramManager {
|
|
|
5996
6564
|
if (typeof executionPrivateKey === "undefined") {
|
|
5997
6565
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
5998
6566
|
}
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6002
|
-
}
|
|
6003
|
-
catch (e) {
|
|
6004
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6005
|
-
edition = 0;
|
|
6006
|
-
}
|
|
6007
|
-
}
|
|
6008
|
-
// Resolve the program imports if they exist.
|
|
6009
|
-
const numberOfImports = mainnet_js.Program.fromString(program).getImports().length;
|
|
6010
|
-
if (numberOfImports > 0 && !imports) {
|
|
6011
|
-
try {
|
|
6012
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6013
|
-
}
|
|
6014
|
-
catch (e) {
|
|
6015
|
-
logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
|
|
6016
|
-
}
|
|
6017
|
-
}
|
|
6567
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6568
|
+
edition = edition ?? resolved.edition;
|
|
6018
6569
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6019
6570
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6571
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6572
|
+
// authorizations don't synthesize keys).
|
|
6573
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6574
|
+
const hasImports = !builder.isEmpty();
|
|
6020
6575
|
// Build and return an `Authorization` for the desired function.
|
|
6021
|
-
|
|
6576
|
+
const authorization = await mainnet_js.ProgramManager.authorize(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
|
|
6577
|
+
return authorization;
|
|
6022
6578
|
}
|
|
6023
6579
|
/**
|
|
6024
6580
|
* Builds a SnarkVM `Authorization` for a specific function without building a circuit first. This should be used when fast authorization generation is needed and the invoker is confident inputs are coorect.
|
|
@@ -6081,29 +6637,17 @@ class ProgramManager {
|
|
|
6081
6637
|
if (typeof executionPrivateKey === "undefined") {
|
|
6082
6638
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
6083
6639
|
}
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
if (numberOfImports > 0 && !imports) {
|
|
6087
|
-
try {
|
|
6088
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6089
|
-
}
|
|
6090
|
-
catch (e) {
|
|
6091
|
-
logAndThrow(`Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`);
|
|
6092
|
-
}
|
|
6093
|
-
}
|
|
6094
|
-
if (edition == undefined) {
|
|
6095
|
-
try {
|
|
6096
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6097
|
-
}
|
|
6098
|
-
catch (e) {
|
|
6099
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6100
|
-
edition = 0;
|
|
6101
|
-
}
|
|
6102
|
-
}
|
|
6640
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6641
|
+
edition = edition ?? resolved.edition;
|
|
6103
6642
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6104
6643
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6644
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6645
|
+
// authorizations don't synthesize keys).
|
|
6646
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6647
|
+
const hasImports = !builder.isEmpty();
|
|
6105
6648
|
// Build and return an `Authorization` for the desired function.
|
|
6106
|
-
|
|
6649
|
+
const authorization = await mainnet_js.ProgramManager.buildAuthorizationUnchecked(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
|
|
6650
|
+
return authorization;
|
|
6107
6651
|
}
|
|
6108
6652
|
/**
|
|
6109
6653
|
* Builds a `ProvingRequest` for submission to a prover for execution. If building a proving request with an ExecutionRequest, a private key must be explicitly provided.
|
|
@@ -6166,15 +6710,8 @@ class ProgramManager {
|
|
|
6166
6710
|
if (programName === undefined) {
|
|
6167
6711
|
programName = mainnet_js.Program.fromString(program).id();
|
|
6168
6712
|
}
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6172
|
-
}
|
|
6173
|
-
catch (e) {
|
|
6174
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6175
|
-
edition = 0;
|
|
6176
|
-
}
|
|
6177
|
-
}
|
|
6713
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6714
|
+
edition = edition ?? resolved.edition;
|
|
6178
6715
|
// Get the private key from the account if it is not provided in the parameters.
|
|
6179
6716
|
let executionPrivateKey = privateKey;
|
|
6180
6717
|
if (typeof privateKey === "undefined" &&
|
|
@@ -6212,8 +6749,30 @@ class ProgramManager {
|
|
|
6212
6749
|
catch (e) {
|
|
6213
6750
|
logAndThrow(`Error finding fee record. Record finder response: '${e.message}'. Please ensure you're connected to a valid Aleo network and a record with enough balance exists.`);
|
|
6214
6751
|
}
|
|
6752
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6753
|
+
// proving requests don't synthesize keys).
|
|
6754
|
+
// Note: imports is kept for the Rust-side fee estimation fallback
|
|
6755
|
+
// (estimate_fee_for_authorization uses the legacy imports Object to avoid
|
|
6756
|
+
// a RefCell double-borrow — see proving_request.rs).
|
|
6757
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6758
|
+
const hasImports = !builder.isEmpty();
|
|
6759
|
+
// Normalize imports to the full merged set from the builder so the
|
|
6760
|
+
// Rust fee estimator sees all transitive imports, not just the
|
|
6761
|
+
// caller's partial set. Use programNames + getProgram to avoid
|
|
6762
|
+
// serializing key bytes (toObject would serialize all proving/
|
|
6763
|
+
// verifying keys, which can be tens of MB).
|
|
6764
|
+
if (hasImports) {
|
|
6765
|
+
const merged = {};
|
|
6766
|
+
for (const name of Array.from(builder.programNames())) {
|
|
6767
|
+
const src = builder.getProgram(name);
|
|
6768
|
+
if (src)
|
|
6769
|
+
merged[name] = src;
|
|
6770
|
+
}
|
|
6771
|
+
imports = merged;
|
|
6772
|
+
}
|
|
6215
6773
|
if (options.executionRequest instanceof mainnet_js.ExecutionRequest) {
|
|
6216
|
-
return await mainnet_js.ProgramManager.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports,
|
|
6774
|
+
return await mainnet_js.ProgramManager.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports, // kept for fee estimation in Rust
|
|
6775
|
+
executionPrivateKey, hasImports ? builder?.clone() : undefined);
|
|
6217
6776
|
}
|
|
6218
6777
|
else {
|
|
6219
6778
|
// Ensure the private key exists.
|
|
@@ -6227,7 +6786,8 @@ class ProgramManager {
|
|
|
6227
6786
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6228
6787
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6229
6788
|
// Build and return the `ProvingRequest`.
|
|
6230
|
-
return await mainnet_js.ProgramManager.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports,
|
|
6789
|
+
return await mainnet_js.ProgramManager.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports, // kept for fee estimation in Rust
|
|
6790
|
+
broadcast, unchecked, edition, useFeeMaster, hasImports ? builder?.clone() : undefined);
|
|
6231
6791
|
}
|
|
6232
6792
|
}
|
|
6233
6793
|
/**
|
|
@@ -6400,6 +6960,10 @@ class ProgramManager {
|
|
|
6400
6960
|
* assert(result === ["10u32"]);
|
|
6401
6961
|
*/
|
|
6402
6962
|
async run(program, function_name, inputs, proveExecution, imports, keySearchParams, provingKey, verifyingKey, privateKey, offlineQuery, edition) {
|
|
6963
|
+
const programName = mainnet_js.Program.fromString(program).id();
|
|
6964
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6965
|
+
edition = edition ?? resolved.edition;
|
|
6966
|
+
const { amendment } = resolved;
|
|
6403
6967
|
// Get the private key from the account if it is not provided in the parameters
|
|
6404
6968
|
let executionPrivateKey = privateKey;
|
|
6405
6969
|
if (typeof privateKey === "undefined" &&
|
|
@@ -6409,13 +6973,16 @@ class ProgramManager {
|
|
|
6409
6973
|
if (typeof executionPrivateKey === "undefined") {
|
|
6410
6974
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
6411
6975
|
}
|
|
6412
|
-
//
|
|
6976
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
6977
|
+
const { builder, importEditions } = await this.buildProgramImports(program, imports, true, function_name);
|
|
6978
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6413
6979
|
if (!provingKey || !verifyingKey) {
|
|
6414
|
-
|
|
6415
|
-
|
|
6980
|
+
const keys = await this.resolveTopLevelKeys(programName, function_name, keySearchParams, edition, amendment);
|
|
6981
|
+
if (keys) {
|
|
6982
|
+
[provingKey, verifyingKey] = keys;
|
|
6416
6983
|
}
|
|
6417
|
-
|
|
6418
|
-
console.log(
|
|
6984
|
+
else {
|
|
6985
|
+
console.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
6419
6986
|
}
|
|
6420
6987
|
}
|
|
6421
6988
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
@@ -6426,11 +6993,17 @@ class ProgramManager {
|
|
|
6426
6993
|
? mainnet_js.QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
6427
6994
|
: undefined;
|
|
6428
6995
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6996
|
+
const result = await mainnet_js.ProgramManager.executeFunctionOffline(executionPrivateKey, program, function_name, preparedInputs, proveExecution, false, undefined, // imports (legacy Object path — builder handles resolution)
|
|
6997
|
+
provingKey, verifyingKey, this.host, query, edition, builder?.clone());
|
|
6998
|
+
// The clone passed to WASM shares state with the original via
|
|
6999
|
+
// Rc<RefCell<>> — synthesized keys are already visible.
|
|
7000
|
+
try {
|
|
7001
|
+
await this.persistExtractedKeys(builder, importEditions);
|
|
7002
|
+
}
|
|
7003
|
+
catch (e) {
|
|
7004
|
+
console.debug(`Failed to persist extracted keys: ${e}`);
|
|
7005
|
+
}
|
|
7006
|
+
return result;
|
|
6434
7007
|
}
|
|
6435
7008
|
/**
|
|
6436
7009
|
* Join two credits records into a single credits record
|
|
@@ -7419,7 +7992,7 @@ class ProgramManager {
|
|
|
7419
7992
|
return mainnet_js.verifyFunctionExecution(execution, verifyingKey, program, function_id, imports, importedVerifyingKeys, blockHeight);
|
|
7420
7993
|
}
|
|
7421
7994
|
catch (e) {
|
|
7422
|
-
|
|
7995
|
+
logger.warn(`The execution was not found in the response, cannot verify the execution: ${e}`);
|
|
7423
7996
|
return false;
|
|
7424
7997
|
}
|
|
7425
7998
|
}
|
|
@@ -7512,12 +8085,8 @@ class ProgramManager {
|
|
|
7512
8085
|
throw new Error("Authorization must be provided if estimating fee for Authorization.");
|
|
7513
8086
|
}
|
|
7514
8087
|
const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
|
|
7515
|
-
const programImports = imports
|
|
7516
|
-
|
|
7517
|
-
if (Object.keys(programImports)) {
|
|
7518
|
-
return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
|
|
7519
|
-
}
|
|
7520
|
-
return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, imports, edition);
|
|
8088
|
+
const programImports = imports ?? await this.networkClient.getProgramImports(programSource);
|
|
8089
|
+
return mainnet_js.ProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
|
|
7521
8090
|
}
|
|
7522
8091
|
/**
|
|
7523
8092
|
* Estimate the execution fee for an Aleo function.
|
|
@@ -7552,10 +8121,7 @@ class ProgramManager {
|
|
|
7552
8121
|
}
|
|
7553
8122
|
const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
|
|
7554
8123
|
const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource);
|
|
7555
|
-
|
|
7556
|
-
return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition);
|
|
7557
|
-
}
|
|
7558
|
-
return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, imports, edition);
|
|
8124
|
+
return mainnet_js.ProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition);
|
|
7559
8125
|
}
|
|
7560
8126
|
// Internal utility function for getting a credits.aleo record
|
|
7561
8127
|
async getCreditsRecord(amount, nonces, record, params) {
|
|
@@ -7663,15 +8229,8 @@ class ProgramManager {
|
|
|
7663
8229
|
if (programName === undefined) {
|
|
7664
8230
|
programName = programObject.id();
|
|
7665
8231
|
}
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
7669
|
-
}
|
|
7670
|
-
catch (e) {
|
|
7671
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
7672
|
-
edition = 0;
|
|
7673
|
-
}
|
|
7674
|
-
}
|
|
8232
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
8233
|
+
edition = edition ?? resolved.edition;
|
|
7675
8234
|
// Get the private key from the account if it is not provided in the parameters.
|
|
7676
8235
|
let executionPrivateKey = privateKey;
|
|
7677
8236
|
if (typeof privateKey === "undefined" &&
|
|
@@ -7687,7 +8246,7 @@ class ProgramManager {
|
|
|
7687
8246
|
let fee = priorityFee;
|
|
7688
8247
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7689
8248
|
if (!feeRecord) {
|
|
7690
|
-
|
|
8249
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7691
8250
|
const programString = programObject.toString();
|
|
7692
8251
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7693
8252
|
const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
|
|
@@ -7776,7 +8335,7 @@ class ProgramManager {
|
|
|
7776
8335
|
}
|
|
7777
8336
|
catch (e) {
|
|
7778
8337
|
// Program does not exist on the network, deployment can proceed
|
|
7779
|
-
|
|
8338
|
+
logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
|
|
7780
8339
|
}
|
|
7781
8340
|
if (typeof programSource === "string") {
|
|
7782
8341
|
throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
|
|
@@ -7800,7 +8359,7 @@ class ProgramManager {
|
|
|
7800
8359
|
let fee = priorityFee;
|
|
7801
8360
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7802
8361
|
if (!feeRecord) {
|
|
7803
|
-
|
|
8362
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7804
8363
|
const programString = programObject.toString();
|
|
7805
8364
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7806
8365
|
const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
|
|
@@ -7901,7 +8460,7 @@ class ProgramManager {
|
|
|
7901
8460
|
let fee = priorityFee;
|
|
7902
8461
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7903
8462
|
if (!feeRecord) {
|
|
7904
|
-
|
|
8463
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7905
8464
|
const programString = programObject.toString();
|
|
7906
8465
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7907
8466
|
const baseFee = Number(mainnet_js.ProgramManager.estimateDeploymentFee(programString, imports));
|
|
@@ -8275,7 +8834,7 @@ function serializeRequestSignInputToBytes(input) {
|
|
|
8275
8834
|
|
|
8276
8835
|
// @TODO: This function is no longer needed, remove it.
|
|
8277
8836
|
async function initializeWasm() {
|
|
8278
|
-
|
|
8837
|
+
logger.warn("initializeWasm is deprecated, you no longer need to use it");
|
|
8279
8838
|
}
|
|
8280
8839
|
|
|
8281
8840
|
Object.defineProperty(exports, "Address", {
|
|
@@ -8406,6 +8965,10 @@ Object.defineProperty(exports, "Program", {
|
|
|
8406
8965
|
enumerable: true,
|
|
8407
8966
|
get: function () { return mainnet_js.Program; }
|
|
8408
8967
|
});
|
|
8968
|
+
Object.defineProperty(exports, "ProgramImportsBuilder", {
|
|
8969
|
+
enumerable: true,
|
|
8970
|
+
get: function () { return mainnet_js.ProgramImports; }
|
|
8971
|
+
});
|
|
8409
8972
|
Object.defineProperty(exports, "ProgramManagerBase", {
|
|
8410
8973
|
enumerable: true,
|
|
8411
8974
|
get: function () { return mainnet_js.ProgramManager; }
|
|
@@ -8422,6 +8985,10 @@ Object.defineProperty(exports, "ProvingRequest", {
|
|
|
8422
8985
|
enumerable: true,
|
|
8423
8986
|
get: function () { return mainnet_js.ProvingRequest; }
|
|
8424
8987
|
});
|
|
8988
|
+
Object.defineProperty(exports, "QueryOption", {
|
|
8989
|
+
enumerable: true,
|
|
8990
|
+
get: function () { return mainnet_js.QueryOption; }
|
|
8991
|
+
});
|
|
8425
8992
|
Object.defineProperty(exports, "RecordCiphertext", {
|
|
8426
8993
|
enumerable: true,
|
|
8427
8994
|
get: function () { return mainnet_js.RecordCiphertext; }
|
|
@@ -8510,6 +9077,7 @@ exports.BlockHeightSearch = BlockHeightSearch;
|
|
|
8510
9077
|
exports.CREDITS_PROGRAM_KEYS = CREDITS_PROGRAM_KEYS;
|
|
8511
9078
|
exports.ChecksumMismatchError = KeyVerificationError;
|
|
8512
9079
|
exports.DecryptionNotEnabledError = DecryptionNotEnabledError;
|
|
9080
|
+
exports.IndexedDBKeyStore = IndexedDBKeyStore;
|
|
8513
9081
|
exports.InvalidLocatorError = InvalidLocatorError;
|
|
8514
9082
|
exports.KEY_STORE = KEY_STORE;
|
|
8515
9083
|
exports.KeyVerificationError = KeyVerificationError;
|
|
@@ -8538,6 +9106,7 @@ exports.encryptAuthorization = encryptAuthorization;
|
|
|
8538
9106
|
exports.encryptProvingRequest = encryptProvingRequest;
|
|
8539
9107
|
exports.encryptRegistrationRequest = encryptRegistrationRequest;
|
|
8540
9108
|
exports.encryptViewKey = encryptViewKey;
|
|
9109
|
+
exports.getLogLevel = getLogLevel;
|
|
8541
9110
|
exports.initializeWasm = initializeWasm;
|
|
8542
9111
|
exports.inputsToFields = inputsToFields;
|
|
8543
9112
|
exports.isInputIdStrategy = isInputIdStrategy;
|
|
@@ -8548,6 +9117,7 @@ exports.isViewKeyStrategy = isViewKeyStrategy;
|
|
|
8548
9117
|
exports.logAndThrow = logAndThrow;
|
|
8549
9118
|
exports.programChecksum = programChecksum;
|
|
8550
9119
|
exports.provingKeyLocator = provingKeyLocator;
|
|
9120
|
+
exports.setLogLevel = setLogLevel;
|
|
8551
9121
|
exports.sha256Hex = sha256Hex;
|
|
8552
9122
|
exports.toAddress = toAddress;
|
|
8553
9123
|
exports.toField = toField;
|