@provablehq/sdk 0.10.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mainnet/browser.cjs +1027 -418
- 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 +1021 -427
- package/dist/mainnet/browser.js.map +1 -1
- package/dist/mainnet/keys/keystore/indexeddb.d.cts +60 -0
- package/dist/mainnet/keys/keystore/indexeddb.d.ts +60 -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 +22 -2
- package/dist/mainnet/network-client.d.ts +22 -2
- package/dist/mainnet/node.cjs +15 -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 +13 -10
- package/dist/mainnet/record-scanner.d.ts +13 -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 +34 -0
- package/dist/mainnet/utils/logger.d.ts +34 -0
- package/dist/mainnet/wasm-Bnb_v1_O.js +3 -0
- package/dist/mainnet/wasm-Bnb_v1_O.js.map +1 -0
- package/dist/mainnet/wasm.cjs +251 -0
- package/dist/mainnet/wasm.cjs.map +1 -0
- package/dist/mainnet/wasm.d.cts +1 -1
- package/dist/mainnet/wasm.d.ts +1 -1
- package/dist/testnet/browser.cjs +1027 -418
- 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 +1021 -427
- package/dist/testnet/browser.js.map +1 -1
- package/dist/testnet/keys/keystore/indexeddb.d.cts +60 -0
- package/dist/testnet/keys/keystore/indexeddb.d.ts +60 -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 +22 -2
- package/dist/testnet/network-client.d.ts +22 -2
- package/dist/testnet/node.cjs +15 -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 +13 -10
- package/dist/testnet/record-scanner.d.ts +13 -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 +34 -0
- package/dist/testnet/utils/logger.d.ts +34 -0
- package/dist/testnet/wasm-BCrMb35a.js +3 -0
- package/dist/testnet/wasm-BCrMb35a.js.map +1 -0
- package/dist/testnet/wasm.cjs +251 -0
- package/dist/testnet/wasm.cjs.map +1 -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.js
CHANGED
|
@@ -1,9 +1,434 @@
|
|
|
1
1
|
import 'core-js/proposals/json-parse-with-source.js';
|
|
2
|
-
import { ViewKey, ComputeKey, Address, PrivateKeyCiphertext, PrivateKey, RecordCiphertext, EncryptionToolkit, Group, Metadata,
|
|
3
|
-
export { Address, Authorization, BHP1024, BHP256, BHP512, BHP768, Boolean, Ciphertext, ComputeKey, DynamicRecord, EncryptionToolkit, ExecutionRequest, ExecutionResponse, Field, Execution as FunctionExecution, GraphKey, Group, I128, I16, I32, I64, I8, OfflineQuery, Pedersen128, Pedersen64, Plaintext, Poseidon2, Poseidon4, Poseidon8, PrivateKey, PrivateKeyCiphertext, Program, ProgramManager as ProgramManagerBase, Proof, ProvingKey, ProvingRequest, RecordCiphertext, RecordPlaintext, Scalar, Signature, Transaction, Transition, U128, U16, U32, U64, U8, Value, VerifyingKey, ViewKey, getOrInitConsensusVersionTestHeights, initThreadPool, snarkVerify, snarkVerifyBatch, stringToField, verifyFunctionExecution } from '@provablehq/wasm/mainnet.js';
|
|
2
|
+
import { ProvingKey, VerifyingKey, ViewKey, ComputeKey, Address, PrivateKeyCiphertext, PrivateKey, RecordCiphertext, EncryptionToolkit, Group, Metadata, Program, Plaintext, Transaction, ProvingRequest, RecordPlaintext, Field, Poseidon4, ProgramManager as ProgramManager$1, CallbackQuery, ProgramImports, QueryOption, ExecutionRequest, stringToField, verifyFunctionExecution, Value, Proof, Signature } from '@provablehq/wasm/mainnet.js';
|
|
3
|
+
export { Address, Authorization, BHP1024, BHP256, BHP512, BHP768, Boolean, Ciphertext, ComputeKey, DynamicRecord, EncryptionToolkit, ExecutionRequest, ExecutionResponse, Field, Execution as FunctionExecution, GraphKey, Group, I128, I16, I32, I64, I8, OfflineQuery, Pedersen128, Pedersen64, Plaintext, Poseidon2, Poseidon4, Poseidon8, PrivateKey, PrivateKeyCiphertext, Program, ProgramImports as ProgramImportsBuilder, ProgramManager as ProgramManagerBase, Proof, ProvingKey, ProvingRequest, QueryOption, RecordCiphertext, RecordPlaintext, Scalar, Signature, Transaction, Transition, U128, U16, U32, U64, U8, Value, VerifyingKey, ViewKey, getOrInitConsensusVersionTestHeights, initThreadPool, setWasmLogLevel, snarkVerify, snarkVerifyBatch, stringToField, verifyFunctionExecution } from '@provablehq/wasm/mainnet.js';
|
|
4
4
|
import { cryptoBoxSeal } from '@serenity-kit/noble-sodium';
|
|
5
5
|
import { base64, bech32m } from '@scure/base';
|
|
6
6
|
|
|
7
|
+
const LEVEL_PRIORITY = {
|
|
8
|
+
silent: 0,
|
|
9
|
+
error: 1,
|
|
10
|
+
warn: 2,
|
|
11
|
+
info: 3,
|
|
12
|
+
debug: 4,
|
|
13
|
+
};
|
|
14
|
+
let currentLevel = "info";
|
|
15
|
+
/**
|
|
16
|
+
* Set the SDK log level. Levels are hierarchical:
|
|
17
|
+
* - "silent" — suppress all SDK logging
|
|
18
|
+
* - "error" — only errors
|
|
19
|
+
* - "warn" — errors + warnings
|
|
20
|
+
* - "info" — errors + warnings + info (default)
|
|
21
|
+
* - "debug" — everything including debug traces
|
|
22
|
+
*
|
|
23
|
+
* This controls both TS-side and WASM-side logging. WASM log calls
|
|
24
|
+
* (e.g., "Loading program", "Executing program") are silenced when
|
|
25
|
+
* the level is below "info".
|
|
26
|
+
*/
|
|
27
|
+
function setLogLevel(level) {
|
|
28
|
+
currentLevel = level;
|
|
29
|
+
import('./wasm-Bnb_v1_O.js')
|
|
30
|
+
.then(({ setWasmLogLevel }) => setWasmLogLevel(LEVEL_PRIORITY[level]))
|
|
31
|
+
.catch(() => { }); // WASM not loaded yet — will use default level (info)
|
|
32
|
+
}
|
|
33
|
+
/** Returns the current SDK log level. */
|
|
34
|
+
function getLogLevel() {
|
|
35
|
+
return currentLevel;
|
|
36
|
+
}
|
|
37
|
+
function shouldLog(level) {
|
|
38
|
+
return LEVEL_PRIORITY[level] <= LEVEL_PRIORITY[currentLevel];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* SDK logger object following console.log/warn/error/debug syntax.
|
|
42
|
+
* Respects the current log level set via setLogLevel().
|
|
43
|
+
*/
|
|
44
|
+
const logger = {
|
|
45
|
+
log(...args) {
|
|
46
|
+
if (shouldLog("info"))
|
|
47
|
+
console.log(...args);
|
|
48
|
+
},
|
|
49
|
+
warn(...args) {
|
|
50
|
+
if (shouldLog("warn"))
|
|
51
|
+
console.warn(...args);
|
|
52
|
+
},
|
|
53
|
+
error(...args) {
|
|
54
|
+
if (shouldLog("error"))
|
|
55
|
+
console.error(...args);
|
|
56
|
+
},
|
|
57
|
+
debug(...args) {
|
|
58
|
+
if (shouldLog("debug"))
|
|
59
|
+
console.debug(...args);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Error thrown when a key locator is invalid for filesystem use.
|
|
65
|
+
* Used to prevent path traversal and other filesystem injection when deriving paths from locators.
|
|
66
|
+
*
|
|
67
|
+
* @extends Error
|
|
68
|
+
*/
|
|
69
|
+
class InvalidLocatorError extends Error {
|
|
70
|
+
locator;
|
|
71
|
+
reason;
|
|
72
|
+
/**
|
|
73
|
+
* @param message - Human-readable description of the validation failure.
|
|
74
|
+
* @param locator - The invalid locator string that failed validation.
|
|
75
|
+
* @param reason - Machine-readable reason code for the failure.
|
|
76
|
+
*/
|
|
77
|
+
constructor(message, locator, reason) {
|
|
78
|
+
super(message);
|
|
79
|
+
this.locator = locator;
|
|
80
|
+
this.reason = reason;
|
|
81
|
+
this.name = "InvalidLocatorError";
|
|
82
|
+
Object.setPrototypeOf(this, InvalidLocatorError.prototype);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Error thrown when there is a mismatch between expected and actual key metadata.
|
|
88
|
+
* This can occur during verification of either the checksum or size of a key.
|
|
89
|
+
*
|
|
90
|
+
* @extends Error
|
|
91
|
+
*/
|
|
92
|
+
class KeyVerificationError extends Error {
|
|
93
|
+
locator;
|
|
94
|
+
field;
|
|
95
|
+
expected;
|
|
96
|
+
actual;
|
|
97
|
+
/**
|
|
98
|
+
* Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
|
|
99
|
+
*
|
|
100
|
+
* @param {string} locator - The key locator where the mismatch occurred.
|
|
101
|
+
* @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
|
|
102
|
+
* @param {string} expected - The expected value of the field.
|
|
103
|
+
* @param {string} actual - The actual value encountered.
|
|
104
|
+
*/
|
|
105
|
+
constructor(locator, field, expected, actual) {
|
|
106
|
+
super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
|
|
107
|
+
this.locator = locator;
|
|
108
|
+
this.field = field;
|
|
109
|
+
this.expected = expected;
|
|
110
|
+
this.actual = actual;
|
|
111
|
+
this.name = "ChecksumMismatchError";
|
|
112
|
+
Object.setPrototypeOf(this, KeyVerificationError.prototype);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Computes the SHA-256 checksum of a given set of bytes.
|
|
117
|
+
*
|
|
118
|
+
* @param {Uint8Array} bytes - The bytes to compute the checksum of.
|
|
119
|
+
*/
|
|
120
|
+
async function sha256Hex(bytes) {
|
|
121
|
+
const hash = await crypto.subtle.digest("SHA-256", bytes);
|
|
122
|
+
return Array.from(new Uint8Array(hash))
|
|
123
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
124
|
+
.join("");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
|
|
129
|
+
* Provides functionality to compute and verify cryptographic checksums of keys, storing them
|
|
130
|
+
* in memory for subsequent verification. This implementation is primarily used for testing
|
|
131
|
+
* and development purposes where persistence is not required.
|
|
132
|
+
*
|
|
133
|
+
* Key features:
|
|
134
|
+
* - Computes SHA-256 checksums and sizes for key bytes.
|
|
135
|
+
* - Stores key fingerprints in memory using string locators.
|
|
136
|
+
* - Verifies key bytes against stored or provided fingerprints.
|
|
137
|
+
*
|
|
138
|
+
* @implements {KeyVerifier}
|
|
139
|
+
*/
|
|
140
|
+
class MemKeyVerifier {
|
|
141
|
+
keyStore = {};
|
|
142
|
+
/**
|
|
143
|
+
* Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
|
|
144
|
+
*
|
|
145
|
+
* @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
|
|
146
|
+
* @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
|
|
147
|
+
* @returns {Promise<KeyFingerprint>} Computed key metadata.
|
|
148
|
+
*/
|
|
149
|
+
async computeKeyMetadata(keyMetadata) {
|
|
150
|
+
// Compute the metadata from the key bytes
|
|
151
|
+
const computedFingerprint = {
|
|
152
|
+
checksum: await sha256Hex(keyMetadata.keyBytes),
|
|
153
|
+
size: keyMetadata.keyBytes.length
|
|
154
|
+
};
|
|
155
|
+
// If a KeyFingerprint is provided, verify it matches computed values.
|
|
156
|
+
if (keyMetadata.fingerprint) {
|
|
157
|
+
if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
|
|
158
|
+
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
|
|
159
|
+
}
|
|
160
|
+
if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
|
|
161
|
+
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// If locator is provided, store only the fingerprint
|
|
165
|
+
if (keyMetadata.locator) {
|
|
166
|
+
this.keyStore[keyMetadata.locator] = computedFingerprint;
|
|
167
|
+
}
|
|
168
|
+
return computedFingerprint;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
|
|
172
|
+
* 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
|
|
173
|
+
* 2. If a locator is provided, attempts to verify against stored fingerprint.
|
|
174
|
+
* 3. If neither is available, throws an error.
|
|
175
|
+
*
|
|
176
|
+
* @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
|
|
177
|
+
* @throws {Error} When neither fingerprint nor valid locator is provided for verification.
|
|
178
|
+
* @throws {KeyVerificationError} When size or checksum verification fails.
|
|
179
|
+
* @returns {Promise<void>} Promise that resolves when verification succeeds.
|
|
180
|
+
*/
|
|
181
|
+
async verifyKeyBytes(keyMetadata) {
|
|
182
|
+
if (!keyMetadata.keyBytes) {
|
|
183
|
+
throw new Error("Key bytes must be provided for verification.");
|
|
184
|
+
}
|
|
185
|
+
// Compute the fingerprint for the provided bytes.
|
|
186
|
+
const computedFingerprint = await this.computeKeyMetadata({
|
|
187
|
+
keyBytes: keyMetadata.keyBytes
|
|
188
|
+
});
|
|
189
|
+
// Determine which fingerprint to verify against.
|
|
190
|
+
let fingerprintToVerify;
|
|
191
|
+
if (keyMetadata.fingerprint) {
|
|
192
|
+
// If a key fingerprint is provided, use it.
|
|
193
|
+
fingerprintToVerify = keyMetadata.fingerprint;
|
|
194
|
+
}
|
|
195
|
+
else if (keyMetadata.locator) {
|
|
196
|
+
// Otherwise try to get stored fingerprint by locator.
|
|
197
|
+
fingerprintToVerify = this.keyStore[keyMetadata.locator];
|
|
198
|
+
}
|
|
199
|
+
if (!fingerprintToVerify) {
|
|
200
|
+
throw new Error("Either fingerprint or a valid locator must be provided for verification.");
|
|
201
|
+
}
|
|
202
|
+
// Verify the key size.
|
|
203
|
+
if (fingerprintToVerify.size !== computedFingerprint.size) {
|
|
204
|
+
throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
|
|
205
|
+
}
|
|
206
|
+
// Verify the key checksum.
|
|
207
|
+
if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
|
|
208
|
+
throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Browser-compatible {@link KeyStore} backed by IndexedDB.
|
|
215
|
+
*
|
|
216
|
+
* This is the browser counterpart to {@link LocalFileKeyStore} (which requires Node.js `fs`).
|
|
217
|
+
* It persists proving and verifying keys across page reloads and browser sessions using the
|
|
218
|
+
* IndexedDB API available in all modern browsers and Web Workers.
|
|
219
|
+
*
|
|
220
|
+
* **Environment**: Browser / Web Worker only. Instantiating this class is safe in any
|
|
221
|
+
* environment, but the first IndexedDB operation will reject with a clear error when
|
|
222
|
+
* `globalThis.indexedDB` is not available (e.g., Node.js, SSR contexts). Guard your
|
|
223
|
+
* imports accordingly if you bundle for server-side rendering.
|
|
224
|
+
*
|
|
225
|
+
* **Security**: Keys are stored as plaintext `Uint8Array` in IndexedDB. Any script
|
|
226
|
+
* running on the same origin — including scripts injected via XSS — can read the stored
|
|
227
|
+
* proving and verifying keys. Do **not** use this keystore for data that must remain
|
|
228
|
+
* confidential under an XSS-capable adversary; encrypt sensitive material at the
|
|
229
|
+
* application layer before persisting, or use an in-memory store.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* import { IndexedDBKeyStore, ProgramManager } from "@provablehq/sdk";
|
|
234
|
+
*
|
|
235
|
+
* const keyStore = new IndexedDBKeyStore();
|
|
236
|
+
* const pm = new ProgramManager();
|
|
237
|
+
* pm.setKeyStore(keyStore);
|
|
238
|
+
* // Keys synthesized during execution are now cached in IndexedDB
|
|
239
|
+
* // and reloaded automatically on subsequent runs.
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
class IndexedDBKeyStore {
|
|
243
|
+
dbName;
|
|
244
|
+
storeName = "keys";
|
|
245
|
+
keyVerifier = new MemKeyVerifier();
|
|
246
|
+
dbPromise = null;
|
|
247
|
+
/**
|
|
248
|
+
* @param dbName IndexedDB database name. Defaults to `"aleo-keystore"`.
|
|
249
|
+
*/
|
|
250
|
+
constructor(dbName = "aleo-keystore") {
|
|
251
|
+
this.dbName = dbName;
|
|
252
|
+
}
|
|
253
|
+
// -------------------------------------------------------
|
|
254
|
+
// IndexedDB helpers
|
|
255
|
+
// -------------------------------------------------------
|
|
256
|
+
/** Opens (or creates) the database, returning a cached promise. */
|
|
257
|
+
openDB() {
|
|
258
|
+
if (this.dbPromise)
|
|
259
|
+
return this.dbPromise;
|
|
260
|
+
// Env check sits outside the Promise constructor so a failure doesn't
|
|
261
|
+
// poison the cache; a later call (e.g. after a polyfill loads) can retry.
|
|
262
|
+
if (typeof indexedDB === "undefined") {
|
|
263
|
+
return Promise.reject(new Error("IndexedDBKeyStore requires a browser or Web Worker environment: " +
|
|
264
|
+
"`indexedDB` is not defined. If you are in Node.js or an SSR " +
|
|
265
|
+
"context, use LocalFileKeyStore or an in-memory key provider instead."));
|
|
266
|
+
}
|
|
267
|
+
this.dbPromise = new Promise((resolve, reject) => {
|
|
268
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
269
|
+
request.onupgradeneeded = () => {
|
|
270
|
+
const db = request.result;
|
|
271
|
+
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
272
|
+
db.createObjectStore(this.storeName, { keyPath: "locator" });
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
request.onsuccess = () => resolve(request.result);
|
|
276
|
+
request.onerror = () => {
|
|
277
|
+
this.dbPromise = null;
|
|
278
|
+
reject(request.error);
|
|
279
|
+
};
|
|
280
|
+
request.onblocked = () => {
|
|
281
|
+
this.dbPromise = null;
|
|
282
|
+
reject(new DOMException("Database open blocked", "AbortError"));
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
return this.dbPromise;
|
|
286
|
+
}
|
|
287
|
+
/** Runs a single read-write transaction and returns the request result. */
|
|
288
|
+
async tx(mode, fn) {
|
|
289
|
+
const db = await this.openDB();
|
|
290
|
+
return new Promise((resolve, reject) => {
|
|
291
|
+
const txn = db.transaction(this.storeName, mode);
|
|
292
|
+
const store = txn.objectStore(this.storeName);
|
|
293
|
+
const req = fn(store);
|
|
294
|
+
let result;
|
|
295
|
+
req.onsuccess = () => { result = req.result; };
|
|
296
|
+
txn.oncomplete = () => resolve(result);
|
|
297
|
+
txn.onerror = () => reject(txn.error);
|
|
298
|
+
txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
// -------------------------------------------------------
|
|
302
|
+
// Locator serialization (mirrors LocalFileKeyStore)
|
|
303
|
+
// -------------------------------------------------------
|
|
304
|
+
validateComponent(value, label) {
|
|
305
|
+
if (value === "" || value === ".") {
|
|
306
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not be empty or "." (got "${value}")`, value, "reserved_name");
|
|
307
|
+
}
|
|
308
|
+
if (value.includes("..")) {
|
|
309
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not contain ".." (got "${value}")`, value, "path_traversal");
|
|
310
|
+
}
|
|
311
|
+
if (value.includes("/") || value.includes("\\") || value.includes("\0")) {
|
|
312
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must not contain path separators or null bytes (got "${value}")`, value, "path_separator");
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
validateNonNegative(value, label) {
|
|
316
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
317
|
+
throw new InvalidLocatorError(`KeyLocator ${label} must be a non-negative integer (got ${value})`, String(value), "negative_value");
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
serializeLocator(locator) {
|
|
321
|
+
this.validateComponent(locator.program, "program");
|
|
322
|
+
this.validateComponent(locator.functionName, "functionName");
|
|
323
|
+
this.validateComponent(locator.network, "network");
|
|
324
|
+
this.validateNonNegative(locator.edition, "edition");
|
|
325
|
+
this.validateNonNegative(locator.amendment, "amendment");
|
|
326
|
+
const base = `${locator.program}.${locator.functionName}.e${locator.edition}.a${locator.amendment}.${locator.network}.${locator.keyType}`;
|
|
327
|
+
if (locator.keyType === "translation") {
|
|
328
|
+
this.validateComponent(locator.recordName, "recordName");
|
|
329
|
+
this.validateNonNegative(locator.recordInputPosition, "recordInputPosition");
|
|
330
|
+
return `${base}.${locator.recordName}.${locator.recordInputPosition}`;
|
|
331
|
+
}
|
|
332
|
+
return base;
|
|
333
|
+
}
|
|
334
|
+
checksumToFingerprint(checksum, keyBytes) {
|
|
335
|
+
if (!checksum)
|
|
336
|
+
return undefined;
|
|
337
|
+
return { checksum, size: keyBytes.length };
|
|
338
|
+
}
|
|
339
|
+
// -------------------------------------------------------
|
|
340
|
+
// KeyStore interface
|
|
341
|
+
// -------------------------------------------------------
|
|
342
|
+
async getKeyBytes(locator) {
|
|
343
|
+
const key = this.serializeLocator(locator);
|
|
344
|
+
const record = await this.tx("readonly", (store) => store.get(key));
|
|
345
|
+
if (!record)
|
|
346
|
+
return null;
|
|
347
|
+
const fingerprint = this.checksumToFingerprint(locator.checksum, record.bytes) ?? record.metadata;
|
|
348
|
+
if (fingerprint) {
|
|
349
|
+
await this.keyVerifier.verifyKeyBytes({
|
|
350
|
+
keyBytes: record.bytes,
|
|
351
|
+
locator: key,
|
|
352
|
+
fingerprint,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return record.bytes;
|
|
356
|
+
}
|
|
357
|
+
async getProvingKey(locator) {
|
|
358
|
+
const bytes = await this.getKeyBytes(locator);
|
|
359
|
+
if (!bytes)
|
|
360
|
+
return null;
|
|
361
|
+
return ProvingKey.fromBytes(bytes);
|
|
362
|
+
}
|
|
363
|
+
async getVerifyingKey(locator) {
|
|
364
|
+
const bytes = await this.getKeyBytes(locator);
|
|
365
|
+
if (!bytes)
|
|
366
|
+
return null;
|
|
367
|
+
return VerifyingKey.fromBytes(bytes);
|
|
368
|
+
}
|
|
369
|
+
async setKeys(proverLocator, verifierLocator, keys) {
|
|
370
|
+
const proverKey = this.serializeLocator(proverLocator);
|
|
371
|
+
const verifierKey = this.serializeLocator(verifierLocator);
|
|
372
|
+
const [provingKey, verifyingKey] = keys;
|
|
373
|
+
const [provingKeyBytes, verifyingKeyBytes] = [
|
|
374
|
+
provingKey.toBytes(),
|
|
375
|
+
verifyingKey.toBytes(),
|
|
376
|
+
];
|
|
377
|
+
const [proverFingerprint, verifierFingerprint] = await Promise.all([
|
|
378
|
+
this.keyVerifier.computeKeyMetadata({
|
|
379
|
+
keyBytes: provingKeyBytes,
|
|
380
|
+
locator: proverKey,
|
|
381
|
+
fingerprint: this.checksumToFingerprint(proverLocator.checksum, provingKeyBytes),
|
|
382
|
+
}),
|
|
383
|
+
this.keyVerifier.computeKeyMetadata({
|
|
384
|
+
keyBytes: verifyingKeyBytes,
|
|
385
|
+
locator: verifierKey,
|
|
386
|
+
fingerprint: this.checksumToFingerprint(verifierLocator.checksum, verifyingKeyBytes),
|
|
387
|
+
}),
|
|
388
|
+
]);
|
|
389
|
+
const proverRecord = { locator: proverKey, bytes: provingKeyBytes, metadata: proverFingerprint };
|
|
390
|
+
const verifierRecord = { locator: verifierKey, bytes: verifyingKeyBytes, metadata: verifierFingerprint };
|
|
391
|
+
// Write both in a single transaction for atomicity.
|
|
392
|
+
const db = await this.openDB();
|
|
393
|
+
await new Promise((resolve, reject) => {
|
|
394
|
+
const txn = db.transaction(this.storeName, "readwrite");
|
|
395
|
+
const store = txn.objectStore(this.storeName);
|
|
396
|
+
store.put(proverRecord);
|
|
397
|
+
store.put(verifierRecord);
|
|
398
|
+
txn.oncomplete = () => resolve();
|
|
399
|
+
txn.onerror = () => reject(txn.error);
|
|
400
|
+
txn.onabort = () => reject(txn.error ?? new DOMException("Transaction aborted", "AbortError"));
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
async setKeyBytes(keyBytes, locator) {
|
|
404
|
+
const key = this.serializeLocator(locator);
|
|
405
|
+
const computedMetadata = await this.keyVerifier.computeKeyMetadata({
|
|
406
|
+
keyBytes,
|
|
407
|
+
locator: key,
|
|
408
|
+
fingerprint: this.checksumToFingerprint(locator.checksum, keyBytes),
|
|
409
|
+
});
|
|
410
|
+
const record = { locator: key, bytes: keyBytes, metadata: computedMetadata };
|
|
411
|
+
await this.tx("readwrite", (store) => store.put(record));
|
|
412
|
+
}
|
|
413
|
+
async getKeyMetadata(locator) {
|
|
414
|
+
const key = this.serializeLocator(locator);
|
|
415
|
+
const record = await this.tx("readonly", (store) => store.get(key));
|
|
416
|
+
return record?.metadata ?? null;
|
|
417
|
+
}
|
|
418
|
+
async has(locator) {
|
|
419
|
+
const key = this.serializeLocator(locator);
|
|
420
|
+
const count = await this.tx("readonly", (store) => store.count(IDBKeyRange.only(key)));
|
|
421
|
+
return count > 0;
|
|
422
|
+
}
|
|
423
|
+
async delete(locator) {
|
|
424
|
+
const key = this.serializeLocator(locator);
|
|
425
|
+
await this.tx("readwrite", (store) => store.delete(key));
|
|
426
|
+
}
|
|
427
|
+
async clear() {
|
|
428
|
+
await this.tx("readwrite", (store) => store.clear());
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
7
432
|
/**
|
|
8
433
|
* Encrypt an authorization with a cryptobox X25519 public key (libsodium-compatible wire format).
|
|
9
434
|
*
|
|
@@ -163,7 +588,7 @@ class Account {
|
|
|
163
588
|
this._privateKey = this.privateKeyFromParams(params);
|
|
164
589
|
}
|
|
165
590
|
catch (e) {
|
|
166
|
-
|
|
591
|
+
logger.error("Wrong parameter", e);
|
|
167
592
|
throw new Error("Wrong Parameter");
|
|
168
593
|
}
|
|
169
594
|
this._viewKey = ViewKey.from_private_key(this._privateKey);
|
|
@@ -634,7 +1059,7 @@ function environment() {
|
|
|
634
1059
|
}
|
|
635
1060
|
}
|
|
636
1061
|
function logAndThrow(message) {
|
|
637
|
-
|
|
1062
|
+
logger.error(message);
|
|
638
1063
|
throw new Error(message);
|
|
639
1064
|
}
|
|
640
1065
|
/** Default transport — wraps global fetch to avoid illegal-invocation errors in browsers. */
|
|
@@ -695,7 +1120,7 @@ async function retryWithBackoff(fn, { maxAttempts = 5, baseDelay = 100, jitter,
|
|
|
695
1120
|
const jitterAmount = jitter ?? baseDelay;
|
|
696
1121
|
const actualJitter = Math.floor(Math.random() * jitterAmount);
|
|
697
1122
|
const delay = baseDelay * 2 ** (attempt - 1) + actualJitter;
|
|
698
|
-
|
|
1123
|
+
logger.warn(`Retry ${attempt}/${maxAttempts} failed. Retrying in ${delay}ms...`);
|
|
699
1124
|
await new Promise((res) => setTimeout(res, delay));
|
|
700
1125
|
}
|
|
701
1126
|
}
|
|
@@ -864,7 +1289,7 @@ class AleoNetworkClient {
|
|
|
864
1289
|
else {
|
|
865
1290
|
this.headers = {
|
|
866
1291
|
// This is replaced by the actual version by a Rollup plugin
|
|
867
|
-
"X-Aleo-SDK-Version": "0.
|
|
1292
|
+
"X-Aleo-SDK-Version": "0.11.0",
|
|
868
1293
|
"X-Aleo-environment": environment(),
|
|
869
1294
|
};
|
|
870
1295
|
}
|
|
@@ -880,7 +1305,7 @@ class AleoNetworkClient {
|
|
|
880
1305
|
else {
|
|
881
1306
|
this.headers = {
|
|
882
1307
|
// This is replaced by the actual version by a Rollup plugin
|
|
883
|
-
"X-Aleo-SDK-Version": "0.
|
|
1308
|
+
"X-Aleo-SDK-Version": "0.11.0",
|
|
884
1309
|
"X-Aleo-environment": environment(),
|
|
885
1310
|
};
|
|
886
1311
|
}
|
|
@@ -1196,7 +1621,7 @@ class AleoNetworkClient {
|
|
|
1196
1621
|
continue;
|
|
1197
1622
|
}
|
|
1198
1623
|
catch (error) {
|
|
1199
|
-
|
|
1624
|
+
logger.log("Found unspent record!");
|
|
1200
1625
|
}
|
|
1201
1626
|
}
|
|
1202
1627
|
// Add the record to the list of records if the user did not specify amounts.
|
|
@@ -1256,14 +1681,14 @@ class AleoNetworkClient {
|
|
|
1256
1681
|
}
|
|
1257
1682
|
catch (error) {
|
|
1258
1683
|
// If there is an error fetching blocks, log it and keep searching
|
|
1259
|
-
|
|
1684
|
+
logger.warn("Error fetching blocks in range: " +
|
|
1260
1685
|
start.toString() +
|
|
1261
1686
|
"-" +
|
|
1262
1687
|
end.toString());
|
|
1263
|
-
|
|
1688
|
+
logger.warn("Error: ", error);
|
|
1264
1689
|
failures += 1;
|
|
1265
1690
|
if (failures > 10) {
|
|
1266
|
-
|
|
1691
|
+
logger.warn("10 failures fetching records reached. Returning records fetched so far");
|
|
1267
1692
|
return records;
|
|
1268
1693
|
}
|
|
1269
1694
|
}
|
|
@@ -1713,6 +2138,30 @@ class AleoNetworkClient {
|
|
|
1713
2138
|
this.ctx = {};
|
|
1714
2139
|
}
|
|
1715
2140
|
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Returns the current edition and amendment count for a program.
|
|
2143
|
+
*
|
|
2144
|
+
* @param {string} programId - The program ID (e.g. "hello_hello.aleo")
|
|
2145
|
+
* @returns {{ program_id: string, edition: number, amendment_count: number }}
|
|
2146
|
+
*
|
|
2147
|
+
* @example
|
|
2148
|
+
* const networkClient = new AleoNetworkClient("https://api.provable.com/v2");
|
|
2149
|
+
* const info = await networkClient.getProgramAmendmentCount("hello_hello.aleo");
|
|
2150
|
+
* console.log(info.edition, info.amendment_count);
|
|
2151
|
+
*/
|
|
2152
|
+
async getProgramAmendmentCount(programId) {
|
|
2153
|
+
try {
|
|
2154
|
+
this.ctx = { "X-ALEO-METHOD": "getProgramAmendmentCount" };
|
|
2155
|
+
const raw = await this.fetchRaw("/programs/" + programId + "/amendment_count");
|
|
2156
|
+
return JSON.parse(raw);
|
|
2157
|
+
}
|
|
2158
|
+
catch (error) {
|
|
2159
|
+
throw new Error(`Error fetching amendment count for ${programId}: ${error}`);
|
|
2160
|
+
}
|
|
2161
|
+
finally {
|
|
2162
|
+
this.ctx = {};
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
1716
2165
|
/**
|
|
1717
2166
|
* Returns a program object from a program ID or program source code.
|
|
1718
2167
|
*
|
|
@@ -2303,7 +2752,7 @@ class AleoNetworkClient {
|
|
|
2303
2752
|
};
|
|
2304
2753
|
}
|
|
2305
2754
|
/**
|
|
2306
|
-
* Parses a /prove or /prove/
|
|
2755
|
+
* Parses a /prove/authorization or /prove/request response. Returns a result object (never throws for 200/400/500/503).
|
|
2307
2756
|
*/
|
|
2308
2757
|
async handleProvingResponse(response) {
|
|
2309
2758
|
// Get the proving response text.
|
|
@@ -2364,14 +2813,12 @@ class AleoNetworkClient {
|
|
|
2364
2813
|
async submitProvingRequestSafe(options) {
|
|
2365
2814
|
// Attempt to get the Prover URI first from the options, then from any configured globally, or third try the main configured host.
|
|
2366
2815
|
const proverUri = (options.url ?? this.proverUri) ?? this.host;
|
|
2367
|
-
const provingRequestString = options.provingRequest instanceof ProvingRequest
|
|
2368
|
-
? options.provingRequest.toString()
|
|
2369
|
-
: options.provingRequest;
|
|
2370
2816
|
// Try to get JWT data to access the Provable API.
|
|
2371
2817
|
const apiKey = options.apiKey ?? this.apiKey;
|
|
2372
2818
|
const consumerId = options.consumerId ?? this.consumerId;
|
|
2373
2819
|
let jwtData = options.jwtData ?? this.jwtData;
|
|
2374
|
-
// Check to see if the JWT needs refreshing.
|
|
2820
|
+
// Check to see if the JWT needs refreshing. Runs before parsing the
|
|
2821
|
+
// proving request so JWT refresh errors propagate as they always have.
|
|
2375
2822
|
const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES;
|
|
2376
2823
|
if (!jwtData || isExpired) {
|
|
2377
2824
|
if (apiKey && consumerId) {
|
|
@@ -2380,7 +2827,7 @@ class AleoNetworkClient {
|
|
|
2380
2827
|
options.jwtData = jwtData;
|
|
2381
2828
|
}
|
|
2382
2829
|
else {
|
|
2383
|
-
|
|
2830
|
+
logger.warn('JWT or both apiKey and consumerId are required when using the Provable API');
|
|
2384
2831
|
}
|
|
2385
2832
|
}
|
|
2386
2833
|
// Create the necessary headers to hit the provable api.
|
|
@@ -2392,59 +2839,58 @@ class AleoNetworkClient {
|
|
|
2392
2839
|
if (jwtData?.jwt) {
|
|
2393
2840
|
headers["Authorization"] = jwtData.jwt;
|
|
2394
2841
|
}
|
|
2395
|
-
//
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
credentials: "include",
|
|
2403
|
-
}, this.transport);
|
|
2404
|
-
// Encrypt the provingRequest.
|
|
2405
|
-
const pubkey = parseJSON(await pubKeyResponse.text());
|
|
2406
|
-
const ciphertext = encryptProvingRequest(pubkey.public_key, ProvingRequest.fromString(provingRequestString));
|
|
2407
|
-
// Form the expected query a DPS service expects (including the key_id).
|
|
2408
|
-
const payload = {
|
|
2409
|
-
key_id: pubkey.key_id,
|
|
2410
|
-
ciphertext: ciphertext,
|
|
2411
|
-
};
|
|
2412
|
-
// We're in node, attempt to set the cookie manually.
|
|
2413
|
-
const cookie = isNode() ? pubKeyResponse.headers.get("set-cookie") : undefined;
|
|
2414
|
-
// Send the encrypted proving request to the DPS service.
|
|
2415
|
-
const res = await this.transport(`${proverUri}/prove/encrypted`, {
|
|
2416
|
-
method: "POST",
|
|
2417
|
-
body: JSON.stringify(payload),
|
|
2418
|
-
headers: {
|
|
2419
|
-
...headers,
|
|
2420
|
-
...(cookie ? { Cookie: cookie } : {})
|
|
2421
|
-
},
|
|
2422
|
-
credentials: "include",
|
|
2423
|
-
});
|
|
2424
|
-
// Properly handle the proving response.
|
|
2425
|
-
return this.handleProvingResponse(res);
|
|
2426
|
-
}
|
|
2427
|
-
// If encrypted usage is not specified use the unencrypted endpoint.
|
|
2428
|
-
const proveEndpoint = proverUri.endsWith("/prove")
|
|
2429
|
-
? proverUri
|
|
2430
|
-
: proverUri + "/prove";
|
|
2431
|
-
const res = await this.transport(proveEndpoint, {
|
|
2432
|
-
method: "POST",
|
|
2433
|
-
body: provingRequestString,
|
|
2842
|
+
// Send the proving request encrypted (libsodium-compatible sealed box).
|
|
2843
|
+
// Used by both `/prove/authorization` (Authorization variant) and
|
|
2844
|
+
// `/prove/request` (Request variant). The legacy plaintext `/prove`
|
|
2845
|
+
// route is no longer used.
|
|
2846
|
+
const sendEncrypted = async (endpoint, provingRequestObj) => {
|
|
2847
|
+
// Get an ephemeral public key from the DPS.
|
|
2848
|
+
const pubKeyResponse = await get(proverUri + "/pubkey", {
|
|
2434
2849
|
headers,
|
|
2850
|
+
credentials: "include",
|
|
2851
|
+
}, this.transport);
|
|
2852
|
+
// Encrypt the provingRequest using the ephemeral pubkey.
|
|
2853
|
+
const pubkey = parseJSON(await pubKeyResponse.text());
|
|
2854
|
+
const ciphertext = encryptProvingRequest(pubkey.public_key, provingRequestObj);
|
|
2855
|
+
const payload = {
|
|
2856
|
+
key_id: pubkey.key_id,
|
|
2857
|
+
ciphertext: ciphertext,
|
|
2858
|
+
};
|
|
2859
|
+
// We're in node, attempt to set the cookie manually.
|
|
2860
|
+
const cookie = isNode() ? pubKeyResponse.headers.get("set-cookie") : undefined;
|
|
2861
|
+
const res = await this.transport(`${proverUri}${endpoint}`, {
|
|
2862
|
+
method: "POST",
|
|
2863
|
+
body: JSON.stringify(payload),
|
|
2864
|
+
headers: {
|
|
2865
|
+
...headers,
|
|
2866
|
+
...(cookie ? { Cookie: cookie } : {})
|
|
2867
|
+
},
|
|
2868
|
+
credentials: "include",
|
|
2435
2869
|
});
|
|
2436
|
-
// Properly handle the proving response.
|
|
2437
2870
|
return this.handleProvingResponse(res);
|
|
2438
2871
|
};
|
|
2872
|
+
// Parse the proving request once, up front, so the variant the SDK
|
|
2873
|
+
// routes on matches the bytes it will eventually send (a Request-
|
|
2874
|
+
// variant string must hit /prove/request, not /prove/authorization).
|
|
2875
|
+
// A malformed input string throws synchronously — the safe-API
|
|
2876
|
+
// contract only promises to return `{ ok: false, ... }` for HTTP
|
|
2877
|
+
// failures (400/500/503); a parse error is a caller-side bug and
|
|
2878
|
+
// surfacing it as a fake 500 would mislead callers debugging it.
|
|
2879
|
+
// `options.dpsPrivacy` is ignored; the legacy plaintext `/prove`
|
|
2880
|
+
// route is deprecated.
|
|
2881
|
+
const provingRequestObj = options.provingRequest instanceof ProvingRequest
|
|
2882
|
+
? options.provingRequest
|
|
2883
|
+
: ProvingRequest.fromString(options.provingRequest);
|
|
2884
|
+
const endpoint = provingRequestObj.kind() === "request"
|
|
2885
|
+
? "/prove/request"
|
|
2886
|
+
: "/prove/authorization";
|
|
2439
2887
|
try {
|
|
2440
|
-
// Run the request with retries.
|
|
2441
2888
|
return await retryWithBackoff(async () => {
|
|
2442
|
-
|
|
2443
|
-
const result = await runRequest();
|
|
2889
|
+
const result = await sendEncrypted(endpoint, provingRequestObj);
|
|
2444
2890
|
if (result.ok) {
|
|
2445
2891
|
return result;
|
|
2446
2892
|
}
|
|
2447
|
-
//
|
|
2893
|
+
// Retry on 500/503; surface 400 verbatim.
|
|
2448
2894
|
if (result.status === 500 || result.status === 503) {
|
|
2449
2895
|
const err = new Error(result.error.message);
|
|
2450
2896
|
err.status = result.status;
|
|
@@ -2454,7 +2900,7 @@ class AleoNetworkClient {
|
|
|
2454
2900
|
});
|
|
2455
2901
|
}
|
|
2456
2902
|
catch (err) {
|
|
2457
|
-
//
|
|
2903
|
+
// HTTP failure inside the retry loop — convert to a result object.
|
|
2458
2904
|
const e = err;
|
|
2459
2905
|
return {
|
|
2460
2906
|
ok: false,
|
|
@@ -2510,10 +2956,10 @@ class AleoNetworkClient {
|
|
|
2510
2956
|
let text = "";
|
|
2511
2957
|
try {
|
|
2512
2958
|
text = await res.text();
|
|
2513
|
-
|
|
2959
|
+
logger.warn("Response text from server:", text);
|
|
2514
2960
|
}
|
|
2515
2961
|
catch (err) {
|
|
2516
|
-
|
|
2962
|
+
logger.warn("Failed to read response text:", err);
|
|
2517
2963
|
}
|
|
2518
2964
|
// If the transaction ID is malformed (e.g. invalid checksum, wrong length),
|
|
2519
2965
|
// the API returns a 4XX with "Invalid URL" — we treat this as a fatal error and stop polling.
|
|
@@ -2524,7 +2970,7 @@ class AleoNetworkClient {
|
|
|
2524
2970
|
return reject(new Error(`Malformed transaction ID: ${text}`));
|
|
2525
2971
|
}
|
|
2526
2972
|
// Log and continue polling for 404s or 5XX errors in case a tx doesn't exist yet
|
|
2527
|
-
|
|
2973
|
+
logger.warn("Non-OK response (retrying):", res.status, text);
|
|
2528
2974
|
return;
|
|
2529
2975
|
}
|
|
2530
2976
|
const data = parseJSON(await res.text());
|
|
@@ -2538,7 +2984,7 @@ class AleoNetworkClient {
|
|
|
2538
2984
|
}
|
|
2539
2985
|
}
|
|
2540
2986
|
catch (err) {
|
|
2541
|
-
|
|
2987
|
+
logger.error("Polling error:", err);
|
|
2542
2988
|
}
|
|
2543
2989
|
}, checkInterval);
|
|
2544
2990
|
});
|
|
@@ -2546,7 +2992,7 @@ class AleoNetworkClient {
|
|
|
2546
2992
|
}
|
|
2547
2993
|
|
|
2548
2994
|
/**
|
|
2549
|
-
* Error thrown when a record scanner request fails (e.g. /register
|
|
2995
|
+
* Error thrown when a record scanner request fails (e.g. /register/encrypted).
|
|
2550
2996
|
* Includes HTTP status so callers can handle 422 vs 500 etc.
|
|
2551
2997
|
*/
|
|
2552
2998
|
class RecordScannerRequestError extends Error {
|
|
@@ -2706,7 +3152,7 @@ class AleoKeyProvider {
|
|
|
2706
3152
|
* @returns {FunctionKeyPair} Proving and verifying keys for the specified program
|
|
2707
3153
|
*/
|
|
2708
3154
|
getKeys(keyId) {
|
|
2709
|
-
|
|
3155
|
+
logger.debug(`Checking if key exists in cache. KeyId: ${keyId}`);
|
|
2710
3156
|
if (this.cache.has(keyId)) {
|
|
2711
3157
|
const [provingKeyBytes, verifyingKeyBytes] = (this.cache.get(keyId));
|
|
2712
3158
|
return [
|
|
@@ -2807,9 +3253,9 @@ class AleoKeyProvider {
|
|
|
2807
3253
|
];
|
|
2808
3254
|
}
|
|
2809
3255
|
else {
|
|
2810
|
-
|
|
3256
|
+
logger.debug("Fetching proving keys from url " + proverUrl);
|
|
2811
3257
|
const provingKey = (ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
|
|
2812
|
-
|
|
3258
|
+
logger.debug("Fetching verifying keys " + verifierUrl);
|
|
2813
3259
|
const verifyingKey = (await this.getVerifyingKey(verifierUrl));
|
|
2814
3260
|
this.cache.set(cacheKey, [
|
|
2815
3261
|
provingKey.toBytes(),
|
|
@@ -2849,7 +3295,7 @@ class AleoKeyProvider {
|
|
|
2849
3295
|
return ProvingKey.fromBytes(value[0]);
|
|
2850
3296
|
}
|
|
2851
3297
|
else {
|
|
2852
|
-
|
|
3298
|
+
logger.debug("Fetching proving keys from url " + proverUrl);
|
|
2853
3299
|
const provingKey = (ProvingKey.fromBytes(await this.fetchBytes(proverUrl)));
|
|
2854
3300
|
return provingKey;
|
|
2855
3301
|
}
|
|
@@ -3016,178 +3462,28 @@ class AleoKeyProvider {
|
|
|
3016
3462
|
return CREDITS_PROGRAM_KEYS.transfer_public_as_signer.verifyingKey();
|
|
3017
3463
|
case CREDITS_PROGRAM_KEYS.transfer_public_to_private.verifier:
|
|
3018
3464
|
return CREDITS_PROGRAM_KEYS.transfer_public_to_private.verifyingKey();
|
|
3019
|
-
case CREDITS_PROGRAM_KEYS.unbond_public.verifier:
|
|
3020
|
-
return CREDITS_PROGRAM_KEYS.unbond_public.verifyingKey();
|
|
3021
|
-
default:
|
|
3022
|
-
try {
|
|
3023
|
-
/// Try to fetch the verifying key from the network as a string
|
|
3024
|
-
const response = await get(verifierUri, undefined, this.transport);
|
|
3025
|
-
const text = await response.text();
|
|
3026
|
-
return VerifyingKey.fromString(text);
|
|
3027
|
-
}
|
|
3028
|
-
catch (e) {
|
|
3029
|
-
/// If that fails, try to fetch the verifying key from the network as bytes
|
|
3030
|
-
try {
|
|
3031
|
-
return (VerifyingKey.fromBytes(await this.fetchBytes(verifierUri)));
|
|
3032
|
-
}
|
|
3033
|
-
catch (inner) {
|
|
3034
|
-
throw new Error("Invalid verifying key. Error: " + inner.message);
|
|
3035
|
-
}
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
}
|
|
3039
|
-
unBondPublicKeys() {
|
|
3040
|
-
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
|
|
3041
|
-
}
|
|
3042
|
-
}
|
|
3043
|
-
|
|
3044
|
-
/**
|
|
3045
|
-
* Error thrown when there is a mismatch between expected and actual key metadata.
|
|
3046
|
-
* This can occur during verification of either the checksum or size of a key.
|
|
3047
|
-
*
|
|
3048
|
-
* @extends Error
|
|
3049
|
-
*/
|
|
3050
|
-
class KeyVerificationError extends Error {
|
|
3051
|
-
locator;
|
|
3052
|
-
field;
|
|
3053
|
-
expected;
|
|
3054
|
-
actual;
|
|
3055
|
-
/**
|
|
3056
|
-
* Creates a new KeyVerificationError instance (error.name is "ChecksumMismatchError").
|
|
3057
|
-
*
|
|
3058
|
-
* @param {string} locator - The key locator where the mismatch occurred.
|
|
3059
|
-
* @param {"checksum" | "size"} field - The field that failed verification (either "checksum" or "size").
|
|
3060
|
-
* @param {string} expected - The expected value of the field.
|
|
3061
|
-
* @param {string} actual - The actual value encountered.
|
|
3062
|
-
*/
|
|
3063
|
-
constructor(locator, field, expected, actual) {
|
|
3064
|
-
super(`Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`);
|
|
3065
|
-
this.locator = locator;
|
|
3066
|
-
this.field = field;
|
|
3067
|
-
this.expected = expected;
|
|
3068
|
-
this.actual = actual;
|
|
3069
|
-
this.name = "ChecksumMismatchError";
|
|
3070
|
-
Object.setPrototypeOf(this, KeyVerificationError.prototype);
|
|
3071
|
-
}
|
|
3072
|
-
}
|
|
3073
|
-
/**
|
|
3074
|
-
* Computes the SHA-256 checksum of a given set of bytes.
|
|
3075
|
-
*
|
|
3076
|
-
* @param {Uint8Array} bytes - The bytes to compute the checksum of.
|
|
3077
|
-
*/
|
|
3078
|
-
async function sha256Hex(bytes) {
|
|
3079
|
-
const hash = await crypto.subtle.digest("SHA-256", bytes);
|
|
3080
|
-
return Array.from(new Uint8Array(hash))
|
|
3081
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
3082
|
-
.join("");
|
|
3083
|
-
}
|
|
3084
|
-
|
|
3085
|
-
/**
|
|
3086
|
-
* In-memory implementation of KeyVerifier that stores and verifies key fingerprints.
|
|
3087
|
-
* Provides functionality to compute and verify cryptographic checksums of keys, storing them
|
|
3088
|
-
* in memory for subsequent verification. This implementation is primarily used for testing
|
|
3089
|
-
* and development purposes where persistence is not required.
|
|
3090
|
-
*
|
|
3091
|
-
* Key features:
|
|
3092
|
-
* - Computes SHA-256 checksums and sizes for key bytes.
|
|
3093
|
-
* - Stores key fingerprints in memory using string locators.
|
|
3094
|
-
* - Verifies key bytes against stored or provided fingerprints.
|
|
3095
|
-
*
|
|
3096
|
-
* @implements {KeyVerifier}
|
|
3097
|
-
*/
|
|
3098
|
-
class MemKeyVerifier {
|
|
3099
|
-
keyStore = {};
|
|
3100
|
-
/**
|
|
3101
|
-
* Computes and optionally stores key metadata. If a keyFingerprint is provided, this function will verify the computed checksum against it before storing it.
|
|
3102
|
-
*
|
|
3103
|
-
* @param {KeyMetadata} keyMetadata - Object containing key bytes and optional verification data.
|
|
3104
|
-
* @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values.
|
|
3105
|
-
* @returns {Promise<KeyFingerprint>} Computed key metadata.
|
|
3106
|
-
*/
|
|
3107
|
-
async computeKeyMetadata(keyMetadata) {
|
|
3108
|
-
// Compute the metadata from the key bytes
|
|
3109
|
-
const computedFingerprint = {
|
|
3110
|
-
checksum: await sha256Hex(keyMetadata.keyBytes),
|
|
3111
|
-
size: keyMetadata.keyBytes.length
|
|
3112
|
-
};
|
|
3113
|
-
// If a KeyFingerprint is provided, verify it matches computed values.
|
|
3114
|
-
if (keyMetadata.fingerprint) {
|
|
3115
|
-
if (keyMetadata.fingerprint.size !== computedFingerprint.size) {
|
|
3116
|
-
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), String(computedFingerprint.size));
|
|
3117
|
-
}
|
|
3118
|
-
if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) {
|
|
3119
|
-
throw new KeyVerificationError(keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, computedFingerprint.checksum);
|
|
3120
|
-
}
|
|
3121
|
-
}
|
|
3122
|
-
// If locator is provided, store only the fingerprint
|
|
3123
|
-
if (keyMetadata.locator) {
|
|
3124
|
-
this.keyStore[keyMetadata.locator] = computedFingerprint;
|
|
3125
|
-
}
|
|
3126
|
-
return computedFingerprint;
|
|
3127
|
-
}
|
|
3128
|
-
/**
|
|
3129
|
-
* Verifies key bytes against stored or provided metadata. Follows a priority verification scheme:
|
|
3130
|
-
* 1. If KeyFingerprint is provided in the metadata, this method verifies against that first.
|
|
3131
|
-
* 2. If a locator is provided, attempts to verify against stored fingerprint.
|
|
3132
|
-
* 3. If neither is available, throws an error.
|
|
3133
|
-
*
|
|
3134
|
-
* @param {KeyMetadata} keyMetadata - Object containing the key bytes and optional verification metadata.
|
|
3135
|
-
* @throws {Error} When neither fingerprint nor valid locator is provided for verification.
|
|
3136
|
-
* @throws {KeyVerificationError} When size or checksum verification fails.
|
|
3137
|
-
* @returns {Promise<void>} Promise that resolves when verification succeeds.
|
|
3138
|
-
*/
|
|
3139
|
-
async verifyKeyBytes(keyMetadata) {
|
|
3140
|
-
if (!keyMetadata.keyBytes) {
|
|
3141
|
-
throw new Error("Key bytes must be provided for verification.");
|
|
3142
|
-
}
|
|
3143
|
-
// Compute the fingerprint for the provided bytes.
|
|
3144
|
-
const computedFingerprint = await this.computeKeyMetadata({
|
|
3145
|
-
keyBytes: keyMetadata.keyBytes
|
|
3146
|
-
});
|
|
3147
|
-
// Determine which fingerprint to verify against.
|
|
3148
|
-
let fingerprintToVerify;
|
|
3149
|
-
if (keyMetadata.fingerprint) {
|
|
3150
|
-
// If a key fingerprint is provided, use it.
|
|
3151
|
-
fingerprintToVerify = keyMetadata.fingerprint;
|
|
3152
|
-
}
|
|
3153
|
-
else if (keyMetadata.locator) {
|
|
3154
|
-
// Otherwise try to get stored fingerprint by locator.
|
|
3155
|
-
fingerprintToVerify = this.keyStore[keyMetadata.locator];
|
|
3156
|
-
}
|
|
3157
|
-
if (!fingerprintToVerify) {
|
|
3158
|
-
throw new Error("Either fingerprint or a valid locator must be provided for verification.");
|
|
3159
|
-
}
|
|
3160
|
-
// Verify the key size.
|
|
3161
|
-
if (fingerprintToVerify.size !== computedFingerprint.size) {
|
|
3162
|
-
throw new KeyVerificationError(keyMetadata.locator || "", "size", String(fingerprintToVerify.size), String(computedFingerprint.size));
|
|
3163
|
-
}
|
|
3164
|
-
// Verify the key checksum.
|
|
3165
|
-
if (fingerprintToVerify.checksum !== computedFingerprint.checksum) {
|
|
3166
|
-
throw new KeyVerificationError(keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, computedFingerprint.checksum);
|
|
3167
|
-
}
|
|
3168
|
-
}
|
|
3169
|
-
}
|
|
3170
|
-
|
|
3171
|
-
/**
|
|
3172
|
-
* Error thrown when a key locator is invalid for filesystem use.
|
|
3173
|
-
* Used to prevent path traversal and other filesystem injection when deriving paths from locators.
|
|
3174
|
-
*
|
|
3175
|
-
* @extends Error
|
|
3176
|
-
*/
|
|
3177
|
-
class InvalidLocatorError extends Error {
|
|
3178
|
-
locator;
|
|
3179
|
-
reason;
|
|
3180
|
-
/**
|
|
3181
|
-
* @param message - Human-readable description of the validation failure.
|
|
3182
|
-
* @param locator - The invalid locator string that failed validation.
|
|
3183
|
-
* @param reason - Machine-readable reason code for the failure.
|
|
3184
|
-
*/
|
|
3185
|
-
constructor(message, locator, reason) {
|
|
3186
|
-
super(message);
|
|
3187
|
-
this.locator = locator;
|
|
3188
|
-
this.reason = reason;
|
|
3189
|
-
this.name = "InvalidLocatorError";
|
|
3190
|
-
Object.setPrototypeOf(this, InvalidLocatorError.prototype);
|
|
3465
|
+
case CREDITS_PROGRAM_KEYS.unbond_public.verifier:
|
|
3466
|
+
return CREDITS_PROGRAM_KEYS.unbond_public.verifyingKey();
|
|
3467
|
+
default:
|
|
3468
|
+
try {
|
|
3469
|
+
/// Try to fetch the verifying key from the network as a string
|
|
3470
|
+
const response = await get(verifierUri, undefined, this.transport);
|
|
3471
|
+
const text = await response.text();
|
|
3472
|
+
return VerifyingKey.fromString(text);
|
|
3473
|
+
}
|
|
3474
|
+
catch (e) {
|
|
3475
|
+
/// If that fails, try to fetch the verifying key from the network as bytes
|
|
3476
|
+
try {
|
|
3477
|
+
return (VerifyingKey.fromBytes(await this.fetchBytes(verifierUri)));
|
|
3478
|
+
}
|
|
3479
|
+
catch (inner) {
|
|
3480
|
+
throw new Error("Invalid verifying key. Error: " + inner.message);
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
unBondPublicKeys() {
|
|
3486
|
+
return this.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public);
|
|
3191
3487
|
}
|
|
3192
3488
|
}
|
|
3193
3489
|
|
|
@@ -3944,12 +4240,12 @@ class NetworkRecordProvider {
|
|
|
3944
4240
|
records = await this.findCreditsRecords([microcredits], searchParameters);
|
|
3945
4241
|
}
|
|
3946
4242
|
catch (e) {
|
|
3947
|
-
|
|
4243
|
+
logger.log("No records found with error:", e);
|
|
3948
4244
|
}
|
|
3949
4245
|
if (records && records.length > 0) {
|
|
3950
4246
|
return records[0];
|
|
3951
4247
|
}
|
|
3952
|
-
|
|
4248
|
+
logger.error("Record not found with error:", records);
|
|
3953
4249
|
throw new Error("Record not found");
|
|
3954
4250
|
}
|
|
3955
4251
|
/**
|
|
@@ -3961,12 +4257,12 @@ class NetworkRecordProvider {
|
|
|
3961
4257
|
records = await this.findRecords(searchParameters);
|
|
3962
4258
|
}
|
|
3963
4259
|
catch (e) {
|
|
3964
|
-
|
|
4260
|
+
logger.log("No records found with error:", e);
|
|
3965
4261
|
}
|
|
3966
4262
|
if (records && records.length > 0) {
|
|
3967
4263
|
return records[0];
|
|
3968
4264
|
}
|
|
3969
|
-
|
|
4265
|
+
logger.error("Record not found with error:", records);
|
|
3970
4266
|
throw new Error("Record not found");
|
|
3971
4267
|
}
|
|
3972
4268
|
/**
|
|
@@ -4061,7 +4357,7 @@ class BlockHeightSearch {
|
|
|
4061
4357
|
* const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" });
|
|
4062
4358
|
* recordScanner.setAccount(account);
|
|
4063
4359
|
* recordScanner.setApiKey("example-api-key");
|
|
4064
|
-
* const result = await recordScanner.
|
|
4360
|
+
* const result = await recordScanner.registerEncrypted(viewKey, 0);
|
|
4065
4361
|
* if (result.ok) { const uuid = result.data.uuid; }
|
|
4066
4362
|
*
|
|
4067
4363
|
* const filter = {
|
|
@@ -4316,40 +4612,6 @@ class RecordScanner {
|
|
|
4316
4612
|
}
|
|
4317
4613
|
throw err;
|
|
4318
4614
|
}
|
|
4319
|
-
/**
|
|
4320
|
-
* 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.
|
|
4321
|
-
*
|
|
4322
|
-
* @param {ViewKey} viewKey The view key to register.
|
|
4323
|
-
* @param {number} startBlock The block height to start scanning from.
|
|
4324
|
-
* @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
|
|
4325
|
-
*/
|
|
4326
|
-
async register(viewKey, startBlock) {
|
|
4327
|
-
try {
|
|
4328
|
-
const request = {
|
|
4329
|
-
view_key: viewKey.to_string(),
|
|
4330
|
-
start: startBlock,
|
|
4331
|
-
};
|
|
4332
|
-
const response = await this.request(new Request(`${this.url}/register`, {
|
|
4333
|
-
method: "POST",
|
|
4334
|
-
headers: { "Content-Type": "application/json" },
|
|
4335
|
-
body: JSON.stringify(request),
|
|
4336
|
-
}));
|
|
4337
|
-
const data = await response.json();
|
|
4338
|
-
// If the uuid is not set, set it on the scanner.
|
|
4339
|
-
if (!this.uuid) {
|
|
4340
|
-
this.uuid = data.uuid;
|
|
4341
|
-
}
|
|
4342
|
-
// Add the view key to the local scanner's view keys if configured to do so.
|
|
4343
|
-
if (this.cacheViewKeysOnRegister) {
|
|
4344
|
-
this.addViewKey(viewKey);
|
|
4345
|
-
}
|
|
4346
|
-
return { ok: true, data };
|
|
4347
|
-
}
|
|
4348
|
-
catch (err) {
|
|
4349
|
-
console.error(`Failed to register view key: ${err}`);
|
|
4350
|
-
return this.handleRequestError(err);
|
|
4351
|
-
}
|
|
4352
|
-
}
|
|
4353
4615
|
/**
|
|
4354
4616
|
* Fetches an ephemeral public key from the record scanning service for use with registerEncrypted.
|
|
4355
4617
|
* Follows the same pattern as the delegated proving service /pubkey endpoint.
|
|
@@ -4360,6 +4622,19 @@ class RecordScanner {
|
|
|
4360
4622
|
const response = await this.request(new Request(`${this.url}/pubkey`, { method: "GET" }));
|
|
4361
4623
|
return parseJSON(await response.text());
|
|
4362
4624
|
}
|
|
4625
|
+
/**
|
|
4626
|
+
* Registers the account with the record scanning service using the encrypted flow.
|
|
4627
|
+
* Alias of {@link registerEncrypted} — preserved so existing callers using the
|
|
4628
|
+
* previous unencrypted `register(viewKey, startBlock)` API continue to work
|
|
4629
|
+
* unchanged while transparently using the encrypted endpoint.
|
|
4630
|
+
*
|
|
4631
|
+
* @param {ViewKey} viewKey The view key to register.
|
|
4632
|
+
* @param {number} startBlock The block height to start scanning from.
|
|
4633
|
+
* @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
|
|
4634
|
+
*/
|
|
4635
|
+
async register(viewKey, startBlock) {
|
|
4636
|
+
return this.registerEncrypted(viewKey, startBlock);
|
|
4637
|
+
}
|
|
4363
4638
|
/**
|
|
4364
4639
|
* Registers the account with the record scanning service using the encrypted flow: 1. fetches an ephemeral public key from /pubkey - 2. encrypts the registration request (view key + start block) - 3. POSTs to /register/encrypted. Does not HTTP error on a proper error response from the record scanner; returns a result object instead.
|
|
4365
4640
|
*
|
|
@@ -4591,7 +4866,7 @@ class RecordScanner {
|
|
|
4591
4866
|
throw new Error("Record not found");
|
|
4592
4867
|
}
|
|
4593
4868
|
catch (error) {
|
|
4594
|
-
|
|
4869
|
+
logger.error(`Failed to find record: ${error}`);
|
|
4595
4870
|
throw error;
|
|
4596
4871
|
}
|
|
4597
4872
|
}
|
|
@@ -4752,7 +5027,7 @@ class RecordScanner {
|
|
|
4752
5027
|
return record;
|
|
4753
5028
|
}
|
|
4754
5029
|
catch (error) {
|
|
4755
|
-
|
|
5030
|
+
logger.error(`Failed to find credits record: ${error}`);
|
|
4756
5031
|
throw error;
|
|
4757
5032
|
}
|
|
4758
5033
|
}
|
|
@@ -4801,7 +5076,7 @@ class RecordScanner {
|
|
|
4801
5076
|
});
|
|
4802
5077
|
}
|
|
4803
5078
|
catch (error) {
|
|
4804
|
-
|
|
5079
|
+
logger.error(`Failed to find credits records: ${error}`);
|
|
4805
5080
|
throw error;
|
|
4806
5081
|
}
|
|
4807
5082
|
}
|
|
@@ -4839,7 +5114,7 @@ class RecordScanner {
|
|
|
4839
5114
|
return response;
|
|
4840
5115
|
}
|
|
4841
5116
|
catch (error) {
|
|
4842
|
-
|
|
5117
|
+
logger.error(`Failed to make request to ${req.url}: ${error}`);
|
|
4843
5118
|
throw error;
|
|
4844
5119
|
}
|
|
4845
5120
|
}
|
|
@@ -5196,17 +5471,19 @@ class ProgramManager {
|
|
|
5196
5471
|
networkClient;
|
|
5197
5472
|
recordProvider;
|
|
5198
5473
|
inclusionKeysLoaded = false;
|
|
5474
|
+
_keyStore;
|
|
5199
5475
|
/** Create a new instance of the ProgramManager
|
|
5200
5476
|
*
|
|
5201
5477
|
* @param { string | undefined } host A host uri running the official Aleo API
|
|
5202
5478
|
* @param { FunctionKeyProvider | undefined } keyProvider A key provider that implements {@link FunctionKeyProvider} interface
|
|
5203
5479
|
* @param { RecordProvider | undefined } recordProvider A record provider that implements {@link RecordProvider} interface
|
|
5204
5480
|
*/
|
|
5205
|
-
constructor(host, keyProvider, recordProvider, networkClientOptions) {
|
|
5481
|
+
constructor(host, keyProvider, recordProvider, networkClientOptions, keyStore) {
|
|
5206
5482
|
this.host = host ? host : "https://api.provable.com/v2";
|
|
5207
5483
|
this.networkClient = new AleoNetworkClient(this.host, networkClientOptions);
|
|
5208
5484
|
this.keyProvider = keyProvider ? keyProvider : new AleoKeyProvider({ transport: networkClientOptions?.transport });
|
|
5209
5485
|
this.recordProvider = recordProvider;
|
|
5486
|
+
this._keyStore = keyStore;
|
|
5210
5487
|
}
|
|
5211
5488
|
/**
|
|
5212
5489
|
* Pre-load the inclusion prover for offline execution. Required when the
|
|
@@ -5284,6 +5561,330 @@ class ProgramManager {
|
|
|
5284
5561
|
setRecordProvider(recordProvider) {
|
|
5285
5562
|
this.recordProvider = recordProvider;
|
|
5286
5563
|
}
|
|
5564
|
+
/**
|
|
5565
|
+
* Set the key store for automatic key caching across executions.
|
|
5566
|
+
*
|
|
5567
|
+
* @param {KeyStore} keyStore
|
|
5568
|
+
*/
|
|
5569
|
+
setKeyStore(keyStore) {
|
|
5570
|
+
this._keyStore = keyStore;
|
|
5571
|
+
}
|
|
5572
|
+
/**
|
|
5573
|
+
* Build a ProgramImportsBuilder from a program and its imports.
|
|
5574
|
+
* Fetches imports from the network if not provided, resolves transitive
|
|
5575
|
+
* dependencies, and optionally pre-loads cached keys from the KeyStore.
|
|
5576
|
+
*
|
|
5577
|
+
* @param loadKeys When true (default), loads cached proving/verifying keys
|
|
5578
|
+
* from the KeyStore into the builder. Set to false for authorization and
|
|
5579
|
+
* proving request paths where keys are not synthesized.
|
|
5580
|
+
*/
|
|
5581
|
+
async buildProgramImports(program, imports, loadKeys, entryFunction) {
|
|
5582
|
+
const builder = new ProgramImports();
|
|
5583
|
+
const programSource = typeof program === "string" ? program : program.toString();
|
|
5584
|
+
const programObj = Program.fromString(programSource);
|
|
5585
|
+
const importNames = programObj.getImports();
|
|
5586
|
+
if (importNames.length === 0 && (!imports || Object.keys(imports).length === 0)) {
|
|
5587
|
+
return { builder, importEditions: new Map() };
|
|
5588
|
+
}
|
|
5589
|
+
let resolvedImports = {};
|
|
5590
|
+
if (imports) {
|
|
5591
|
+
resolvedImports = { ...imports };
|
|
5592
|
+
}
|
|
5593
|
+
if (importNames.length > 0 && !imports) {
|
|
5594
|
+
try {
|
|
5595
|
+
resolvedImports = await this.networkClient.getProgramImports(programSource);
|
|
5596
|
+
}
|
|
5597
|
+
catch (e) {
|
|
5598
|
+
logger.warn(`Failed to resolve program imports from network: ${e}.`);
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
// Build a map of which functions each import actually calls,
|
|
5602
|
+
// so we only load keys for functions in the call chain.
|
|
5603
|
+
const calledFunctions = ProgramManager.callGraphToMap(programObj.getCallGraph(entryFunction));
|
|
5604
|
+
// Phase 1: Collect all programs via BFS, discovering transitive imports.
|
|
5605
|
+
// The initial getProgramImports call above already resolves the full
|
|
5606
|
+
// transitive closure via recursive DFS. The BFS loop here only needs
|
|
5607
|
+
// to handle user-provided imports whose transitive deps may not yet be
|
|
5608
|
+
// known. For each unknown import we issue a single getProgram() call
|
|
5609
|
+
// (not a full recursive getProgramImports) and fetch siblings in
|
|
5610
|
+
// parallel to minimize round-trips.
|
|
5611
|
+
const collected = new Map();
|
|
5612
|
+
const collectQueue = Object.entries(resolvedImports).map(([name, src]) => [name, typeof src === "string" ? src : src.toString()]);
|
|
5613
|
+
while (collectQueue.length > 0) {
|
|
5614
|
+
const [name, source] = collectQueue.shift();
|
|
5615
|
+
if (collected.has(name))
|
|
5616
|
+
continue;
|
|
5617
|
+
collected.set(name, source);
|
|
5618
|
+
// Discover transitive imports for collection (call-graph tracing
|
|
5619
|
+
// happens in a separate pass after topological sorting).
|
|
5620
|
+
try {
|
|
5621
|
+
const subImports = ProgramManager.getImportNames(source);
|
|
5622
|
+
if (subImports.length > 0) {
|
|
5623
|
+
// Fetch only unknown transitive imports — the source for
|
|
5624
|
+
// programs already in collected/resolvedImports is known, so
|
|
5625
|
+
// we skip them. Fetches within a BFS level run in parallel.
|
|
5626
|
+
const unknownImports = subImports.filter((id) => !collected.has(id) && !(id in resolvedImports));
|
|
5627
|
+
if (unknownImports.length > 0) {
|
|
5628
|
+
const fetched = await Promise.all(unknownImports.map(async (id) => {
|
|
5629
|
+
try {
|
|
5630
|
+
const src = await this.networkClient.getProgram(id);
|
|
5631
|
+
return [id, src];
|
|
5632
|
+
}
|
|
5633
|
+
catch {
|
|
5634
|
+
return null;
|
|
5635
|
+
}
|
|
5636
|
+
}));
|
|
5637
|
+
for (const entry of fetched) {
|
|
5638
|
+
if (entry && !collected.has(entry[0])) {
|
|
5639
|
+
collectQueue.push(entry);
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
}
|
|
5645
|
+
catch (e) {
|
|
5646
|
+
logger.warn(`Failed to resolve transitive imports for ${name}: ${e}`);
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
// Phase 2: Add programs in topological order (leaves first).
|
|
5650
|
+
// A simple DFS post-order ensures dependencies are added before
|
|
5651
|
+
// dependents, regardless of the order collected entries arrived.
|
|
5652
|
+
const sorted = [];
|
|
5653
|
+
const visited = new Set();
|
|
5654
|
+
const visit = (name) => {
|
|
5655
|
+
if (visited.has(name))
|
|
5656
|
+
return;
|
|
5657
|
+
visited.add(name);
|
|
5658
|
+
const source = collected.get(name);
|
|
5659
|
+
if (!source)
|
|
5660
|
+
return;
|
|
5661
|
+
for (const dep of ProgramManager.getImportNames(source)) {
|
|
5662
|
+
if (collected.has(dep))
|
|
5663
|
+
visit(dep);
|
|
5664
|
+
}
|
|
5665
|
+
sorted.push([name, source]);
|
|
5666
|
+
};
|
|
5667
|
+
for (const [name] of collected)
|
|
5668
|
+
visit(name);
|
|
5669
|
+
// When scoped, trace call graphs in reverse topological order
|
|
5670
|
+
// (dependents before leaves) so parent calls propagate before
|
|
5671
|
+
// children are traced. sorted is leaves-first, so reverse it.
|
|
5672
|
+
if (entryFunction) {
|
|
5673
|
+
for (let i = sorted.length - 1; i >= 0; i--) {
|
|
5674
|
+
const [name, source] = sorted[i];
|
|
5675
|
+
const functionsNeeded = calledFunctions.get(name);
|
|
5676
|
+
if (!functionsNeeded)
|
|
5677
|
+
continue;
|
|
5678
|
+
try {
|
|
5679
|
+
const importProgram = Program.fromString(source);
|
|
5680
|
+
for (const fn of functionsNeeded) {
|
|
5681
|
+
const reachable = ProgramManager.callGraphToMap(importProgram.getCallGraph(fn));
|
|
5682
|
+
for (const [p, fns] of reachable) {
|
|
5683
|
+
if (!calledFunctions.has(p)) {
|
|
5684
|
+
calledFunctions.set(p, new Set(fns));
|
|
5685
|
+
}
|
|
5686
|
+
else {
|
|
5687
|
+
for (const f of fns)
|
|
5688
|
+
calledFunctions.get(p).add(f);
|
|
5689
|
+
}
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
catch (e) {
|
|
5694
|
+
logger.warn(`Failed to trace call graph for ${name}: ${e}`);
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
5698
|
+
// Resolve editions for all imports in parallel (each import may have
|
|
5699
|
+
// a different edition than the top-level program).
|
|
5700
|
+
const importEditions = new Map();
|
|
5701
|
+
await Promise.all(sorted.map(async ([name]) => {
|
|
5702
|
+
importEditions.set(name, await this.resolveEditionAndAmendment(name));
|
|
5703
|
+
}));
|
|
5704
|
+
for (const [name, source] of sorted) {
|
|
5705
|
+
if (builder.contains(name))
|
|
5706
|
+
continue;
|
|
5707
|
+
const { edition: importEdition, amendment: importAmendment } = importEditions.get(name);
|
|
5708
|
+
try {
|
|
5709
|
+
builder.addProgram(name, source, importEdition);
|
|
5710
|
+
}
|
|
5711
|
+
catch (e) {
|
|
5712
|
+
logger.warn(`Failed to add import ${name} to builder: ${e}`);
|
|
5713
|
+
continue;
|
|
5714
|
+
}
|
|
5715
|
+
if (loadKeys) {
|
|
5716
|
+
const calledFns = calledFunctions.get(name);
|
|
5717
|
+
await this.loadKeysFromStore(builder, name, calledFns ? [...calledFns] : [], importEdition, importAmendment);
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
return { builder, importEditions };
|
|
5721
|
+
}
|
|
5722
|
+
/**
|
|
5723
|
+
* Extract `import program_name.aleo;` names from program source via regex.
|
|
5724
|
+
* Avoids a WASM round-trip compared to Program.fromString + getImports.
|
|
5725
|
+
*/
|
|
5726
|
+
static getImportNames(programSource) {
|
|
5727
|
+
const result = [];
|
|
5728
|
+
const importPattern = /^\s*import\s+([a-zA-Z_][a-zA-Z0-9_]*\.aleo)\s*;/gm;
|
|
5729
|
+
let match;
|
|
5730
|
+
while ((match = importPattern.exec(programSource)) !== null) {
|
|
5731
|
+
result.push(match[1]);
|
|
5732
|
+
}
|
|
5733
|
+
return result;
|
|
5734
|
+
}
|
|
5735
|
+
/**
|
|
5736
|
+
* Convert the JS object returned by Program.getCallGraph() into a
|
|
5737
|
+
* Map<string, Set<string>> for use in buildProgramImports.
|
|
5738
|
+
*/
|
|
5739
|
+
static callGraphToMap(callGraph) {
|
|
5740
|
+
const result = new Map();
|
|
5741
|
+
for (const [programName, functions] of Object.entries(callGraph)) {
|
|
5742
|
+
result.set(programName, new Set(functions));
|
|
5743
|
+
}
|
|
5744
|
+
return result;
|
|
5745
|
+
}
|
|
5746
|
+
/**
|
|
5747
|
+
* Resolve the active KeyStore, preferring the directly-set _keyStore
|
|
5748
|
+
* over the KeyProvider's keyStore().
|
|
5749
|
+
*/
|
|
5750
|
+
async resolveKeyStore() {
|
|
5751
|
+
if (this._keyStore)
|
|
5752
|
+
return this._keyStore;
|
|
5753
|
+
try {
|
|
5754
|
+
return await this.keyProvider?.keyStore?.();
|
|
5755
|
+
}
|
|
5756
|
+
catch {
|
|
5757
|
+
return undefined;
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
/**
|
|
5761
|
+
* Resolve the edition and amendment count for a program from the network.
|
|
5762
|
+
* Returns `{ edition, amendment }` or falls back to `{ edition: 1, amendment: 0 }`.
|
|
5763
|
+
*/
|
|
5764
|
+
async resolveEditionAndAmendment(programName, fallbackEdition) {
|
|
5765
|
+
try {
|
|
5766
|
+
const info = await this.networkClient.getProgramAmendmentCount(programName);
|
|
5767
|
+
return { edition: info.edition, amendment: info.amendment_count };
|
|
5768
|
+
}
|
|
5769
|
+
catch (e) {
|
|
5770
|
+
logger.warn(`Error finding edition/amendment for ${programName}. Network response: '${e.message}'. Defaulting to edition ${fallbackEdition ?? 1}, amendment 0.`);
|
|
5771
|
+
return { edition: fallbackEdition ?? 1, amendment: 0 };
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
/**
|
|
5775
|
+
* Load cached proving/verifying keys from the KeyStore into a ProgramImportsBuilder.
|
|
5776
|
+
* Only loads keys for the specified functions — returns immediately if
|
|
5777
|
+
* functionNames is empty or undefined.
|
|
5778
|
+
* Resolves edition and amendment from the network for accurate key locator
|
|
5779
|
+
* construction when not explicitly provided.
|
|
5780
|
+
*/
|
|
5781
|
+
async loadKeysFromStore(builder, programName, functionNames, edition, amendment) {
|
|
5782
|
+
if (!functionNames || functionNames.length === 0)
|
|
5783
|
+
return;
|
|
5784
|
+
const keyStore = await this.resolveKeyStore();
|
|
5785
|
+
if (!keyStore)
|
|
5786
|
+
return;
|
|
5787
|
+
// Resolve edition and amendment from the network if not fully provided.
|
|
5788
|
+
if (edition === undefined || amendment === undefined) {
|
|
5789
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
5790
|
+
edition = edition ?? resolved.edition;
|
|
5791
|
+
amendment = amendment ?? resolved.amendment;
|
|
5792
|
+
}
|
|
5793
|
+
for (const fnName of functionNames) {
|
|
5794
|
+
const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
|
|
5795
|
+
const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
|
|
5796
|
+
try {
|
|
5797
|
+
const pk = await keyStore.getProvingKey(pkLocator);
|
|
5798
|
+
if (pk)
|
|
5799
|
+
builder.addProvingKey(programName, fnName, pk);
|
|
5800
|
+
const vk = await keyStore.getVerifyingKey(vkLocator);
|
|
5801
|
+
if (vk)
|
|
5802
|
+
builder.addVerifyingKey(programName, fnName, vk);
|
|
5803
|
+
}
|
|
5804
|
+
catch (e) {
|
|
5805
|
+
logger.debug(`Failed to load keys for ${programName}/${fnName}: ${e}`);
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
}
|
|
5809
|
+
/**
|
|
5810
|
+
* Persist newly synthesized keys from the returned ProgramImportsBuilder
|
|
5811
|
+
* into the KeyStore. Only writes keys that are not already in the store,
|
|
5812
|
+
* avoiding unnecessary writes of large proving keys.
|
|
5813
|
+
* Fetches each program's current edition and amendment count from the network
|
|
5814
|
+
* for accurate key locator construction.
|
|
5815
|
+
*/
|
|
5816
|
+
async persistExtractedKeys(builder, importEditions) {
|
|
5817
|
+
const keyStore = await this.resolveKeyStore();
|
|
5818
|
+
if (!keyStore)
|
|
5819
|
+
return;
|
|
5820
|
+
const programNames = Array.from(builder.programNames());
|
|
5821
|
+
// Reuse editions resolved during buildProgramImports when available,
|
|
5822
|
+
// falling back to parallel network resolution for any missing programs.
|
|
5823
|
+
const editionMap = importEditions ?? new Map();
|
|
5824
|
+
const missing = programNames.filter((name) => !editionMap.has(name));
|
|
5825
|
+
if (missing.length > 0) {
|
|
5826
|
+
await Promise.all(missing.map(async (name) => {
|
|
5827
|
+
editionMap.set(name, await this.resolveEditionAndAmendment(name));
|
|
5828
|
+
}));
|
|
5829
|
+
}
|
|
5830
|
+
for (const programName of programNames) {
|
|
5831
|
+
const { edition, amendment } = editionMap.get(programName);
|
|
5832
|
+
let fns;
|
|
5833
|
+
try {
|
|
5834
|
+
fns = Array.from(builder.functionKeysAvailable(programName));
|
|
5835
|
+
}
|
|
5836
|
+
catch (e) {
|
|
5837
|
+
logger.debug(`Failed to query keys for ${programName}: ${e}`);
|
|
5838
|
+
continue;
|
|
5839
|
+
}
|
|
5840
|
+
for (const fnName of fns) {
|
|
5841
|
+
try {
|
|
5842
|
+
const pkLocator = provingKeyLocator(programName, fnName, edition, amendment);
|
|
5843
|
+
const vkLocator = verifyingKeyLocator(programName, fnName, edition, amendment);
|
|
5844
|
+
// Skip keys already in the store.
|
|
5845
|
+
if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
|
|
5846
|
+
continue;
|
|
5847
|
+
}
|
|
5848
|
+
const pk = builder.getProvingKey(programName, fnName);
|
|
5849
|
+
const vk = builder.getVerifyingKey(programName, fnName);
|
|
5850
|
+
if (pk && vk) {
|
|
5851
|
+
await keyStore.setKeys(pkLocator, vkLocator, [pk, vk]);
|
|
5852
|
+
}
|
|
5853
|
+
}
|
|
5854
|
+
catch (e) {
|
|
5855
|
+
logger.debug(`Failed to persist key for ${programName}/${fnName}: ${e}`);
|
|
5856
|
+
}
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
/**
|
|
5861
|
+
* Resolve top-level function keys, checking the KeyStore first and
|
|
5862
|
+
* falling back to the KeyProvider.
|
|
5863
|
+
*/
|
|
5864
|
+
async resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment) {
|
|
5865
|
+
const keyStore = await this.resolveKeyStore();
|
|
5866
|
+
if (keyStore) {
|
|
5867
|
+
try {
|
|
5868
|
+
const pkLocator = provingKeyLocator(programName, functionName, edition, amendment);
|
|
5869
|
+
const vkLocator = verifyingKeyLocator(programName, functionName, edition, amendment);
|
|
5870
|
+
if (await keyStore.has(pkLocator) && await keyStore.has(vkLocator)) {
|
|
5871
|
+
const pk = await keyStore.getProvingKey(pkLocator);
|
|
5872
|
+
const vk = await keyStore.getVerifyingKey(vkLocator);
|
|
5873
|
+
if (pk && vk)
|
|
5874
|
+
return [pk, vk];
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
catch {
|
|
5878
|
+
// Fall through to KeyProvider
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
try {
|
|
5882
|
+
return (await this.keyProvider.functionKeys(keySearchParams));
|
|
5883
|
+
}
|
|
5884
|
+
catch {
|
|
5885
|
+
return undefined;
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5287
5888
|
/**
|
|
5288
5889
|
* Set a header in the `AleoNetworkClient`s header map
|
|
5289
5890
|
*
|
|
@@ -5337,7 +5938,7 @@ class ProgramManager {
|
|
|
5337
5938
|
return;
|
|
5338
5939
|
}
|
|
5339
5940
|
catch {
|
|
5340
|
-
|
|
5941
|
+
logger.log("Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly");
|
|
5341
5942
|
}
|
|
5342
5943
|
}
|
|
5343
5944
|
/**
|
|
@@ -5412,7 +6013,7 @@ class ProgramManager {
|
|
|
5412
6013
|
}
|
|
5413
6014
|
catch (e) {
|
|
5414
6015
|
// Program does not exist on the network, deployment can proceed
|
|
5415
|
-
|
|
6016
|
+
logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
|
|
5416
6017
|
}
|
|
5417
6018
|
if (typeof programSource === "string") {
|
|
5418
6019
|
throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
|
|
@@ -5436,7 +6037,7 @@ class ProgramManager {
|
|
|
5436
6037
|
let fee = priorityFee;
|
|
5437
6038
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
5438
6039
|
if (!feeRecord) {
|
|
5439
|
-
|
|
6040
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
5440
6041
|
const programString = programObject.toString();
|
|
5441
6042
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
5442
6043
|
const baseFee = Number(ProgramManager$1.estimateDeploymentFee(programString, imports));
|
|
@@ -5527,7 +6128,7 @@ class ProgramManager {
|
|
|
5527
6128
|
}
|
|
5528
6129
|
catch (e) {
|
|
5529
6130
|
// Program does not exist on the network, deployment can proceed
|
|
5530
|
-
|
|
6131
|
+
logger.log(`Program ${programObject.id()} does not exist on the network...`);
|
|
5531
6132
|
}
|
|
5532
6133
|
}
|
|
5533
6134
|
catch (e) {
|
|
@@ -5548,7 +6149,7 @@ class ProgramManager {
|
|
|
5548
6149
|
let fee = priorityFee;
|
|
5549
6150
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
5550
6151
|
if (!feeRecord) {
|
|
5551
|
-
|
|
6152
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
5552
6153
|
const programString = programObject.toString();
|
|
5553
6154
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
5554
6155
|
const baseFee = Number(ProgramManager$1.estimateDeploymentFee(programString, imports));
|
|
@@ -5718,15 +6319,9 @@ class ProgramManager {
|
|
|
5718
6319
|
if (programName === undefined) {
|
|
5719
6320
|
programName = programObject.id();
|
|
5720
6321
|
}
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
}
|
|
5725
|
-
catch (e) {
|
|
5726
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
5727
|
-
edition = 0;
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
6322
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6323
|
+
edition = edition ?? resolved.edition;
|
|
6324
|
+
const { amendment } = resolved;
|
|
5730
6325
|
// Get the private key from the account if it is not provided in the parameters
|
|
5731
6326
|
let executionPrivateKey = privateKey;
|
|
5732
6327
|
if (typeof privateKey === "undefined" &&
|
|
@@ -5747,23 +6342,19 @@ class ProgramManager {
|
|
|
5747
6342
|
logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
|
|
5748
6343
|
}
|
|
5749
6344
|
const [feeProvingKey, feeVerifyingKey] = feeKeys;
|
|
5750
|
-
//
|
|
6345
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
6346
|
+
const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, functionName);
|
|
6347
|
+
// Include the top-level program's already-resolved edition so
|
|
6348
|
+
// persistExtractedKeys doesn't make a redundant network call for it.
|
|
6349
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6350
|
+
// If the function proving and verifying keys are not provided, attempt to find them
|
|
5751
6351
|
if (!provingKey || !verifyingKey) {
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
catch (e) {
|
|
5756
|
-
console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
|
|
5757
|
-
}
|
|
5758
|
-
}
|
|
5759
|
-
// Resolve the program imports if they exist
|
|
5760
|
-
const numberOfImports = programObject.getImports().length;
|
|
5761
|
-
if (numberOfImports > 0 && !imports) {
|
|
5762
|
-
try {
|
|
5763
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6352
|
+
const keys = await this.resolveTopLevelKeys(programName, functionName, keySearchParams, edition, amendment);
|
|
6353
|
+
if (keys) {
|
|
6354
|
+
[provingKey, verifyingKey] = keys;
|
|
5764
6355
|
}
|
|
5765
|
-
|
|
5766
|
-
|
|
6356
|
+
else {
|
|
6357
|
+
logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
5767
6358
|
}
|
|
5768
6359
|
}
|
|
5769
6360
|
// Get the fee record from the account if it is not provided in the parameters
|
|
@@ -5788,17 +6379,23 @@ class ProgramManager {
|
|
|
5788
6379
|
}
|
|
5789
6380
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
5790
6381
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
5791
|
-
// Build the query: user-provided OfflineQuery for truly offline execution,
|
|
5792
|
-
// CallbackQuery when a custom transport is configured, or undefined to
|
|
5793
|
-
// let WASM use its internal SnapshotQuery with the default fetch.
|
|
5794
6382
|
const query = offlineQuery
|
|
5795
6383
|
? QueryOption.offlineQuery(offlineQuery)
|
|
5796
6384
|
: this.networkClient.hasCustomTransport
|
|
5797
6385
|
? QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
5798
6386
|
: undefined;
|
|
5799
6387
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
5800
|
-
//
|
|
5801
|
-
|
|
6388
|
+
const result = await ProgramManager$1.buildExecutionTransaction(executionPrivateKey, program, functionName, preparedInputs, priorityFee, feeRecord, this.host, undefined, // imports (legacy Object path — builder handles resolution)
|
|
6389
|
+
provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, query, edition, programImportsBuilder?.clone());
|
|
6390
|
+
// The clone passed to WASM shares state with the original via
|
|
6391
|
+
// Rc<RefCell<>> — synthesized keys are already visible.
|
|
6392
|
+
try {
|
|
6393
|
+
await this.persistExtractedKeys(programImportsBuilder, importEditions);
|
|
6394
|
+
}
|
|
6395
|
+
catch (e) {
|
|
6396
|
+
logger.debug(`Failed to persist extracted keys: ${e}`);
|
|
6397
|
+
}
|
|
6398
|
+
return result;
|
|
5802
6399
|
}
|
|
5803
6400
|
/**
|
|
5804
6401
|
* Builds an execution transaction for submission to the Aleo network from an Authorization and Fee Authorization.
|
|
@@ -5876,6 +6473,10 @@ class ProgramManager {
|
|
|
5876
6473
|
const feeAuthorization = options.feeAuthorization;
|
|
5877
6474
|
const keySearchParams = options.keySearchParams;
|
|
5878
6475
|
const offlineQuery = options.offlineQuery;
|
|
6476
|
+
let edition = options.edition;
|
|
6477
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6478
|
+
edition = edition ?? resolved.edition;
|
|
6479
|
+
const { amendment } = resolved;
|
|
5879
6480
|
let provingKey = options.provingKey;
|
|
5880
6481
|
let verifyingKey = options.verifyingKey;
|
|
5881
6482
|
let program = options.program;
|
|
@@ -5904,24 +6505,17 @@ class ProgramManager {
|
|
|
5904
6505
|
logAndThrow(`Error finding fee keys. Key finder response: '${e.message}'. Please ensure your key provider is configured correctly.`);
|
|
5905
6506
|
}
|
|
5906
6507
|
const [feeProvingKey, feeVerifyingKey] = feeKeys;
|
|
5907
|
-
//
|
|
6508
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
6509
|
+
const { builder: programImportsBuilder, importEditions } = await this.buildProgramImports(program, imports, true, authorization.functionName());
|
|
6510
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6511
|
+
// If the function proving and verifying keys are not provided, attempt to find them.
|
|
5908
6512
|
if (!provingKey || !verifyingKey) {
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
catch (e) {
|
|
5913
|
-
console.log(`Function keys not found. Key finder response: '${e}'. The function keys will be synthesized`);
|
|
5914
|
-
}
|
|
5915
|
-
}
|
|
5916
|
-
// Resolve the program imports if they exist.
|
|
5917
|
-
console.log("Resolving program imports");
|
|
5918
|
-
const numberOfImports = Program.fromString(program).getImports().length;
|
|
5919
|
-
if (numberOfImports > 0 && !imports) {
|
|
5920
|
-
try {
|
|
5921
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6513
|
+
const keys = await this.resolveTopLevelKeys(programName, authorization.functionName(), keySearchParams, edition, amendment);
|
|
6514
|
+
if (keys) {
|
|
6515
|
+
[provingKey, verifyingKey] = keys;
|
|
5922
6516
|
}
|
|
5923
|
-
|
|
5924
|
-
|
|
6517
|
+
else {
|
|
6518
|
+
logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
5925
6519
|
}
|
|
5926
6520
|
}
|
|
5927
6521
|
const query = offlineQuery
|
|
@@ -5930,9 +6524,18 @@ class ProgramManager {
|
|
|
5930
6524
|
? QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
5931
6525
|
: undefined;
|
|
5932
6526
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
5933
|
-
// Build an execution transaction from the authorization.
|
|
5934
|
-
|
|
5935
|
-
|
|
6527
|
+
// Build an execution transaction from the authorization. The
|
|
6528
|
+
// ProgramImportsBuilder is consumed by value (ownership transfers to WASM)
|
|
6529
|
+
// and returned enriched with any keys synthesized during execution.
|
|
6530
|
+
const result = await ProgramManager$1.executeAuthorization(authorization, feeAuthorization, program, provingKey, verifyingKey, feeProvingKey, feeVerifyingKey, undefined, // imports (legacy Object path — builder handles resolution)
|
|
6531
|
+
this.host, query, programImportsBuilder?.clone(), edition);
|
|
6532
|
+
try {
|
|
6533
|
+
await this.persistExtractedKeys(programImportsBuilder, importEditions);
|
|
6534
|
+
}
|
|
6535
|
+
catch (e) {
|
|
6536
|
+
logger.debug(`Failed to persist extracted keys: ${e}`);
|
|
6537
|
+
}
|
|
6538
|
+
return result;
|
|
5936
6539
|
}
|
|
5937
6540
|
/**
|
|
5938
6541
|
* Builds a SnarkVM `Authorization` for a specific function.
|
|
@@ -5995,29 +6598,17 @@ class ProgramManager {
|
|
|
5995
6598
|
if (typeof executionPrivateKey === "undefined") {
|
|
5996
6599
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
5997
6600
|
}
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6001
|
-
}
|
|
6002
|
-
catch (e) {
|
|
6003
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6004
|
-
edition = 0;
|
|
6005
|
-
}
|
|
6006
|
-
}
|
|
6007
|
-
// Resolve the program imports if they exist.
|
|
6008
|
-
const numberOfImports = Program.fromString(program).getImports().length;
|
|
6009
|
-
if (numberOfImports > 0 && !imports) {
|
|
6010
|
-
try {
|
|
6011
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6012
|
-
}
|
|
6013
|
-
catch (e) {
|
|
6014
|
-
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.`);
|
|
6015
|
-
}
|
|
6016
|
-
}
|
|
6601
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6602
|
+
edition = edition ?? resolved.edition;
|
|
6017
6603
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6018
6604
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6605
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6606
|
+
// authorizations don't synthesize keys).
|
|
6607
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6608
|
+
const hasImports = !builder.isEmpty();
|
|
6019
6609
|
// Build and return an `Authorization` for the desired function.
|
|
6020
|
-
|
|
6610
|
+
const authorization = await ProgramManager$1.authorize(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
|
|
6611
|
+
return authorization;
|
|
6021
6612
|
}
|
|
6022
6613
|
/**
|
|
6023
6614
|
* 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.
|
|
@@ -6080,29 +6671,17 @@ class ProgramManager {
|
|
|
6080
6671
|
if (typeof executionPrivateKey === "undefined") {
|
|
6081
6672
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
6082
6673
|
}
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
if (numberOfImports > 0 && !imports) {
|
|
6086
|
-
try {
|
|
6087
|
-
imports = (await this.networkClient.getProgramImports(programName));
|
|
6088
|
-
}
|
|
6089
|
-
catch (e) {
|
|
6090
|
-
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.`);
|
|
6091
|
-
}
|
|
6092
|
-
}
|
|
6093
|
-
if (edition == undefined) {
|
|
6094
|
-
try {
|
|
6095
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6096
|
-
}
|
|
6097
|
-
catch (e) {
|
|
6098
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6099
|
-
edition = 0;
|
|
6100
|
-
}
|
|
6101
|
-
}
|
|
6674
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6675
|
+
edition = edition ?? resolved.edition;
|
|
6102
6676
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6103
6677
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6678
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6679
|
+
// authorizations don't synthesize keys).
|
|
6680
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6681
|
+
const hasImports = !builder.isEmpty();
|
|
6104
6682
|
// Build and return an `Authorization` for the desired function.
|
|
6105
|
-
|
|
6683
|
+
const authorization = await ProgramManager$1.buildAuthorizationUnchecked(executionPrivateKey, program, functionName, preparedInputs, hasImports ? undefined : imports, edition, hasImports ? builder?.clone() : undefined);
|
|
6684
|
+
return authorization;
|
|
6106
6685
|
}
|
|
6107
6686
|
/**
|
|
6108
6687
|
* 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.
|
|
@@ -6165,15 +6744,8 @@ class ProgramManager {
|
|
|
6165
6744
|
if (programName === undefined) {
|
|
6166
6745
|
programName = Program.fromString(program).id();
|
|
6167
6746
|
}
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
6171
|
-
}
|
|
6172
|
-
catch (e) {
|
|
6173
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
6174
|
-
edition = 0;
|
|
6175
|
-
}
|
|
6176
|
-
}
|
|
6747
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6748
|
+
edition = edition ?? resolved.edition;
|
|
6177
6749
|
// Get the private key from the account if it is not provided in the parameters.
|
|
6178
6750
|
let executionPrivateKey = privateKey;
|
|
6179
6751
|
if (typeof privateKey === "undefined" &&
|
|
@@ -6211,8 +6783,30 @@ class ProgramManager {
|
|
|
6211
6783
|
catch (e) {
|
|
6212
6784
|
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.`);
|
|
6213
6785
|
}
|
|
6786
|
+
// Build ProgramImportsBuilder for import resolution (no key loading —
|
|
6787
|
+
// proving requests don't synthesize keys).
|
|
6788
|
+
// Note: imports is kept for the Rust-side fee estimation fallback
|
|
6789
|
+
// (estimate_fee_for_authorization uses the legacy imports Object to avoid
|
|
6790
|
+
// a RefCell double-borrow — see proving_request.rs).
|
|
6791
|
+
const { builder } = await this.buildProgramImports(program, imports, false, functionName);
|
|
6792
|
+
const hasImports = !builder.isEmpty();
|
|
6793
|
+
// Normalize imports to the full merged set from the builder so the
|
|
6794
|
+
// Rust fee estimator sees all transitive imports, not just the
|
|
6795
|
+
// caller's partial set. Use programNames + getProgram to avoid
|
|
6796
|
+
// serializing key bytes (toObject would serialize all proving/
|
|
6797
|
+
// verifying keys, which can be tens of MB).
|
|
6798
|
+
if (hasImports) {
|
|
6799
|
+
const merged = {};
|
|
6800
|
+
for (const name of Array.from(builder.programNames())) {
|
|
6801
|
+
const src = builder.getProgram(name);
|
|
6802
|
+
if (src)
|
|
6803
|
+
merged[name] = src;
|
|
6804
|
+
}
|
|
6805
|
+
imports = merged;
|
|
6806
|
+
}
|
|
6214
6807
|
if (options.executionRequest instanceof ExecutionRequest) {
|
|
6215
|
-
return await ProgramManager$1.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports,
|
|
6808
|
+
return await ProgramManager$1.buildProvingRequestFromExecutionRequest(options.executionRequest, program, unchecked, broadcast, edition, imports, // kept for fee estimation in Rust
|
|
6809
|
+
executionPrivateKey, hasImports ? builder?.clone() : undefined);
|
|
6216
6810
|
}
|
|
6217
6811
|
else {
|
|
6218
6812
|
// Ensure the private key exists.
|
|
@@ -6226,7 +6820,8 @@ class ProgramManager {
|
|
|
6226
6820
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
6227
6821
|
const preparedInputs = this.prepareInputs(program, functionName, inputs);
|
|
6228
6822
|
// Build and return the `ProvingRequest`.
|
|
6229
|
-
return await ProgramManager$1.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports,
|
|
6823
|
+
return await ProgramManager$1.buildProvingRequest(executionPrivateKey, program, functionName, preparedInputs, baseFee, priorityFee, feeRecord, imports, // kept for fee estimation in Rust
|
|
6824
|
+
broadcast, unchecked, edition, useFeeMaster, hasImports ? builder?.clone() : undefined);
|
|
6230
6825
|
}
|
|
6231
6826
|
}
|
|
6232
6827
|
/**
|
|
@@ -6399,6 +6994,10 @@ class ProgramManager {
|
|
|
6399
6994
|
* assert(result === ["10u32"]);
|
|
6400
6995
|
*/
|
|
6401
6996
|
async run(program, function_name, inputs, proveExecution, imports, keySearchParams, provingKey, verifyingKey, privateKey, offlineQuery, edition) {
|
|
6997
|
+
const programName = Program.fromString(program).id();
|
|
6998
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
6999
|
+
edition = edition ?? resolved.edition;
|
|
7000
|
+
const { amendment } = resolved;
|
|
6402
7001
|
// Get the private key from the account if it is not provided in the parameters
|
|
6403
7002
|
let executionPrivateKey = privateKey;
|
|
6404
7003
|
if (typeof privateKey === "undefined" &&
|
|
@@ -6408,13 +7007,16 @@ class ProgramManager {
|
|
|
6408
7007
|
if (typeof executionPrivateKey === "undefined") {
|
|
6409
7008
|
throw "No private key provided and no private key set in the ProgramManager";
|
|
6410
7009
|
}
|
|
6411
|
-
//
|
|
7010
|
+
// Build the ProgramImportsBuilder for import resolution and key caching.
|
|
7011
|
+
const { builder, importEditions } = await this.buildProgramImports(program, imports, true, function_name);
|
|
7012
|
+
importEditions.set(programName, { edition: edition, amendment });
|
|
6412
7013
|
if (!provingKey || !verifyingKey) {
|
|
6413
|
-
|
|
6414
|
-
|
|
7014
|
+
const keys = await this.resolveTopLevelKeys(programName, function_name, keySearchParams, edition, amendment);
|
|
7015
|
+
if (keys) {
|
|
7016
|
+
[provingKey, verifyingKey] = keys;
|
|
6415
7017
|
}
|
|
6416
|
-
|
|
6417
|
-
|
|
7018
|
+
else {
|
|
7019
|
+
logger.log("Function keys not found in KeyStore or KeyProvider. The function keys will be synthesized");
|
|
6418
7020
|
}
|
|
6419
7021
|
}
|
|
6420
7022
|
// Auto-convert bare string inputs to field elements where the function expects field type.
|
|
@@ -6425,11 +7027,17 @@ class ProgramManager {
|
|
|
6425
7027
|
? QueryOption.callbackQuery(this.buildCallbackQuery())
|
|
6426
7028
|
: undefined;
|
|
6427
7029
|
await this.ensureInclusionKeys(!!offlineQuery);
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
7030
|
+
const result = await ProgramManager$1.executeFunctionOffline(executionPrivateKey, program, function_name, preparedInputs, proveExecution, false, undefined, // imports (legacy Object path — builder handles resolution)
|
|
7031
|
+
provingKey, verifyingKey, this.host, query, edition, builder?.clone());
|
|
7032
|
+
// The clone passed to WASM shares state with the original via
|
|
7033
|
+
// Rc<RefCell<>> — synthesized keys are already visible.
|
|
7034
|
+
try {
|
|
7035
|
+
await this.persistExtractedKeys(builder, importEditions);
|
|
7036
|
+
}
|
|
7037
|
+
catch (e) {
|
|
7038
|
+
logger.debug(`Failed to persist extracted keys: ${e}`);
|
|
7039
|
+
}
|
|
7040
|
+
return result;
|
|
6433
7041
|
}
|
|
6434
7042
|
/**
|
|
6435
7043
|
* Join two credits records into a single credits record
|
|
@@ -7418,7 +8026,7 @@ class ProgramManager {
|
|
|
7418
8026
|
return verifyFunctionExecution(execution, verifyingKey, program, function_id, imports, importedVerifyingKeys, blockHeight);
|
|
7419
8027
|
}
|
|
7420
8028
|
catch (e) {
|
|
7421
|
-
|
|
8029
|
+
logger.warn(`The execution was not found in the response, cannot verify the execution: ${e}`);
|
|
7422
8030
|
return false;
|
|
7423
8031
|
}
|
|
7424
8032
|
}
|
|
@@ -7511,12 +8119,8 @@ class ProgramManager {
|
|
|
7511
8119
|
throw new Error("Authorization must be provided if estimating fee for Authorization.");
|
|
7512
8120
|
}
|
|
7513
8121
|
const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
|
|
7514
|
-
const programImports = imports
|
|
7515
|
-
|
|
7516
|
-
if (Object.keys(programImports)) {
|
|
7517
|
-
return ProgramManager$1.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
|
|
7518
|
-
}
|
|
7519
|
-
return ProgramManager$1.estimateFeeForAuthorization(authorization, programSource, imports, edition);
|
|
8122
|
+
const programImports = imports ?? await this.networkClient.getProgramImports(programSource);
|
|
8123
|
+
return ProgramManager$1.estimateFeeForAuthorization(authorization, programSource, programImports, edition);
|
|
7520
8124
|
}
|
|
7521
8125
|
/**
|
|
7522
8126
|
* Estimate the execution fee for an Aleo function.
|
|
@@ -7551,10 +8155,7 @@ class ProgramManager {
|
|
|
7551
8155
|
}
|
|
7552
8156
|
const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition);
|
|
7553
8157
|
const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource);
|
|
7554
|
-
|
|
7555
|
-
return ProgramManager$1.estimateExecutionFee(programSource, functionName, programImports, edition);
|
|
7556
|
-
}
|
|
7557
|
-
return ProgramManager$1.estimateExecutionFee(programSource, functionName, imports, edition);
|
|
8158
|
+
return ProgramManager$1.estimateExecutionFee(programSource, functionName, programImports, edition);
|
|
7558
8159
|
}
|
|
7559
8160
|
// Internal utility function for getting a credits.aleo record
|
|
7560
8161
|
async getCreditsRecord(amount, nonces, record, params) {
|
|
@@ -7662,15 +8263,8 @@ class ProgramManager {
|
|
|
7662
8263
|
if (programName === undefined) {
|
|
7663
8264
|
programName = programObject.id();
|
|
7664
8265
|
}
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
edition = await this.networkClient.getLatestProgramEdition(programName);
|
|
7668
|
-
}
|
|
7669
|
-
catch (e) {
|
|
7670
|
-
console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`);
|
|
7671
|
-
edition = 0;
|
|
7672
|
-
}
|
|
7673
|
-
}
|
|
8266
|
+
const resolved = await this.resolveEditionAndAmendment(programName, edition);
|
|
8267
|
+
edition = edition ?? resolved.edition;
|
|
7674
8268
|
// Get the private key from the account if it is not provided in the parameters.
|
|
7675
8269
|
let executionPrivateKey = privateKey;
|
|
7676
8270
|
if (typeof privateKey === "undefined" &&
|
|
@@ -7686,7 +8280,7 @@ class ProgramManager {
|
|
|
7686
8280
|
let fee = priorityFee;
|
|
7687
8281
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7688
8282
|
if (!feeRecord) {
|
|
7689
|
-
|
|
8283
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7690
8284
|
const programString = programObject.toString();
|
|
7691
8285
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7692
8286
|
const baseFee = Number(ProgramManager$1.estimateDeploymentFee(programString, imports));
|
|
@@ -7775,7 +8369,7 @@ class ProgramManager {
|
|
|
7775
8369
|
}
|
|
7776
8370
|
catch (e) {
|
|
7777
8371
|
// Program does not exist on the network, deployment can proceed
|
|
7778
|
-
|
|
8372
|
+
logger.log(`Program ${programObject.id()} does not exist on the network, deploying...`);
|
|
7779
8373
|
}
|
|
7780
8374
|
if (typeof programSource === "string") {
|
|
7781
8375
|
throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`);
|
|
@@ -7799,7 +8393,7 @@ class ProgramManager {
|
|
|
7799
8393
|
let fee = priorityFee;
|
|
7800
8394
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7801
8395
|
if (!feeRecord) {
|
|
7802
|
-
|
|
8396
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7803
8397
|
const programString = programObject.toString();
|
|
7804
8398
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7805
8399
|
const baseFee = Number(ProgramManager$1.estimateDeploymentFee(programString, imports));
|
|
@@ -7900,7 +8494,7 @@ class ProgramManager {
|
|
|
7900
8494
|
let fee = priorityFee;
|
|
7901
8495
|
// If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record.
|
|
7902
8496
|
if (!feeRecord) {
|
|
7903
|
-
|
|
8497
|
+
logger.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.");
|
|
7904
8498
|
const programString = programObject.toString();
|
|
7905
8499
|
const imports = await this.networkClient.getProgramImports(programString);
|
|
7906
8500
|
const baseFee = Number(ProgramManager$1.estimateDeploymentFee(programString, imports));
|
|
@@ -8274,8 +8868,8 @@ function serializeRequestSignInputToBytes(input) {
|
|
|
8274
8868
|
|
|
8275
8869
|
// @TODO: This function is no longer needed, remove it.
|
|
8276
8870
|
async function initializeWasm() {
|
|
8277
|
-
|
|
8871
|
+
logger.warn("initializeWasm is deprecated, you no longer need to use it");
|
|
8278
8872
|
}
|
|
8279
8873
|
|
|
8280
|
-
export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoNetworkClient, BlockHeightSearch, CREDITS_PROGRAM_KEYS, KeyVerificationError as ChecksumMismatchError, DecryptionNotEnabledError, InvalidLocatorError, KEY_STORE, KeyVerificationError, MemKeyVerifier, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, PRIVATE_TO_PUBLIC_TRANSFER, PRIVATE_TRANSFER, PRIVATE_TRANSFER_TYPES, PUBLIC_TO_PRIVATE_TRANSFER, PUBLIC_TRANSFER, PUBLIC_TRANSFER_AS_SIGNER, ProgramManager, RECORD_DOMAIN, RecordNotFoundError, RecordScanner, RecordScannerRequestError, SealanceMerkleTree, UUIDError, VALID_TRANSFER_TYPES, ViewKeyNotStoredError, buildExecutionRequestFromExternallySignedData, computeExternalSigningInputs, encryptAuthorization, encryptProvingRequest, encryptRegistrationRequest, encryptViewKey, initializeWasm, inputsToFields, isInputIdStrategy, isProveApiErrorBody, isProvingResponse, isRecordViewKeyStrategy, isViewKeyStrategy, logAndThrow, programChecksum, provingKeyLocator, sha256Hex, toAddress, toField, toGroup, toSignature, toViewKey, translationKeyLocator, verifyBatchProof, verifyProof, verifyingKeyLocator, zeroizeBytes };
|
|
8874
|
+
export { Account, AleoKeyProvider, AleoKeyProviderParams, AleoNetworkClient, BlockHeightSearch, CREDITS_PROGRAM_KEYS, KeyVerificationError as ChecksumMismatchError, DecryptionNotEnabledError, IndexedDBKeyStore, InvalidLocatorError, KEY_STORE, KeyVerificationError, MemKeyVerifier, NetworkRecordProvider, OfflineKeyProvider, OfflineSearchParams, PRIVATE_TO_PUBLIC_TRANSFER, PRIVATE_TRANSFER, PRIVATE_TRANSFER_TYPES, PUBLIC_TO_PRIVATE_TRANSFER, PUBLIC_TRANSFER, PUBLIC_TRANSFER_AS_SIGNER, ProgramManager, RECORD_DOMAIN, RecordNotFoundError, RecordScanner, RecordScannerRequestError, SealanceMerkleTree, UUIDError, VALID_TRANSFER_TYPES, ViewKeyNotStoredError, buildExecutionRequestFromExternallySignedData, computeExternalSigningInputs, encryptAuthorization, encryptProvingRequest, encryptRegistrationRequest, encryptViewKey, getLogLevel, initializeWasm, inputsToFields, isInputIdStrategy, isProveApiErrorBody, isProvingResponse, isRecordViewKeyStrategy, isViewKeyStrategy, logAndThrow, programChecksum, provingKeyLocator, setLogLevel, sha256Hex, toAddress, toField, toGroup, toSignature, toViewKey, translationKeyLocator, verifyBatchProof, verifyProof, verifyingKeyLocator, zeroizeBytes };
|
|
8281
8875
|
//# sourceMappingURL=browser.js.map
|