@aztec/foundation 0.0.1-commit.993d240 → 0.0.1-commit.9a89641
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/dest/config/env_var.d.ts +2 -2
- package/dest/config/env_var.d.ts.map +1 -1
- package/dest/config/index.d.ts +21 -8
- package/dest/config/index.d.ts.map +1 -1
- package/dest/config/index.js +46 -30
- package/dest/config/network_name.d.ts +2 -2
- package/dest/config/network_name.d.ts.map +1 -1
- package/dest/config/network_name.js +1 -1
- package/dest/crypto/bls/bn254_keystore.d.ts +2 -2
- package/dest/crypto/bls/bn254_keystore.d.ts.map +1 -1
- package/dest/crypto/bls/bn254_keystore.js +22 -17
- package/dest/crypto/keys/index.d.ts +3 -2
- package/dest/crypto/keys/index.d.ts.map +1 -1
- package/dest/crypto/keys/index.js +30 -6
- package/dest/fifo/fifo_frame_reader.d.ts +2 -2
- package/dest/fifo/fifo_frame_reader.d.ts.map +1 -1
- package/dest/fifo/fifo_frame_reader.js +21 -4
- package/dest/json-rpc/client/safe_json_rpc_client.d.ts +1 -1
- package/dest/json-rpc/client/safe_json_rpc_client.d.ts.map +1 -1
- package/dest/json-rpc/client/safe_json_rpc_client.js +3 -2
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.d.ts.map +1 -1
- package/dest/json-rpc/server/safe_json_rpc_server.js +5 -1
- package/dest/log/aws-logger-config.d.ts +8 -0
- package/dest/log/aws-logger-config.d.ts.map +1 -0
- package/dest/log/aws-logger-config.js +55 -0
- package/dest/log/pino-logger.d.ts +1 -1
- package/dest/log/pino-logger.d.ts.map +1 -1
- package/dest/log/pino-logger.js +12 -6
- package/dest/promise/running-promise.d.ts +5 -4
- package/dest/promise/running-promise.d.ts.map +1 -1
- package/dest/promise/running-promise.js +7 -3
- package/dest/retry/index.d.ts +20 -3
- package/dest/retry/index.d.ts.map +1 -1
- package/dest/retry/index.js +23 -2
- package/dest/serialize/buffer_reader.d.ts +1 -1
- package/dest/serialize/buffer_reader.d.ts.map +1 -1
- package/dest/serialize/buffer_reader.js +5 -0
- package/dest/serialize/serialize.d.ts +1 -32
- package/dest/serialize/serialize.d.ts.map +1 -1
- package/dest/serialize/serialize.js +0 -21
- package/dest/string/index.d.ts +3 -1
- package/dest/string/index.d.ts.map +1 -1
- package/dest/string/index.js +12 -0
- package/dest/testing/files/index.d.ts +3 -4
- package/dest/testing/files/index.d.ts.map +1 -1
- package/dest/testing/files/index.js +3 -4
- package/dest/trees/membership_witness.d.ts +1 -5
- package/dest/trees/membership_witness.d.ts.map +1 -1
- package/dest/trees/membership_witness.js +0 -9
- package/dest/trees/sibling_path.d.ts +9 -11
- package/dest/trees/sibling_path.d.ts.map +1 -1
- package/dest/trees/sibling_path.js +13 -20
- package/dest/types/index.d.ts +16 -2
- package/dest/types/index.d.ts.map +1 -1
- package/dest/types/index.js +15 -0
- package/package.json +2 -2
- package/src/config/env_var.ts +26 -5
- package/src/config/index.ts +46 -30
- package/src/config/network_name.ts +2 -2
- package/src/crypto/bls/bn254_keystore.ts +33 -20
- package/src/crypto/keys/index.ts +20 -4
- package/src/fifo/fifo_frame_reader.ts +17 -2
- package/src/json-rpc/client/safe_json_rpc_client.ts +3 -2
- package/src/json-rpc/server/safe_json_rpc_server.ts +5 -1
- package/src/log/aws-logger-config.ts +32 -0
- package/src/log/pino-logger.ts +14 -4
- package/src/promise/running-promise.ts +8 -5
- package/src/retry/index.ts +36 -4
- package/src/serialize/buffer_reader.ts +5 -0
- package/src/serialize/serialize.ts +0 -50
- package/src/string/index.ts +14 -0
- package/src/testing/files/index.ts +3 -4
- package/src/trees/membership_witness.ts +0 -8
- package/src/trees/sibling_path.ts +13 -24
- package/src/types/index.ts +34 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type NetworkNames =
|
|
2
2
|
| 'local'
|
|
3
|
-
| 'staging
|
|
3
|
+
| 'staging'
|
|
4
4
|
| 'testnet'
|
|
5
5
|
| 'mainnet'
|
|
6
6
|
| 'next-net'
|
|
@@ -11,7 +11,7 @@ export function getActiveNetworkName(name?: string): NetworkNames {
|
|
|
11
11
|
const network = name || process.env.NETWORK;
|
|
12
12
|
if (!network || network === '' || network === 'local') {
|
|
13
13
|
return 'local';
|
|
14
|
-
} else if (network === 'staging
|
|
14
|
+
} else if (network === 'staging') {
|
|
15
15
|
return network;
|
|
16
16
|
} else if (network === 'testnet' || network === 'alpha-testnet') {
|
|
17
17
|
return 'testnet';
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { randomBytes } from '@aztec/foundation/crypto/random';
|
|
2
2
|
|
|
3
|
-
import { createCipheriv, createDecipheriv, createHash, pbkdf2Sync, randomUUID } from 'crypto';
|
|
3
|
+
import { createCipheriv, createDecipheriv, createHash, pbkdf2, pbkdf2Sync, randomUUID } from 'crypto';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
5
|
+
import { promisify } from 'util';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -100,24 +101,15 @@ export interface Bn254KeystoreInterface {
|
|
|
100
101
|
version: number;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* Uses PBKDF2 with SHA-256 for key derivation and AES-128-CTR for encryption,
|
|
107
|
-
* following the EIP-2335 specification format.
|
|
108
|
-
*
|
|
109
|
-
* @param password - Password for encrypting the private key
|
|
110
|
-
* @param privateKeyHex - Private key as 0x-prefixed hex string (32 bytes)
|
|
111
|
-
* @param pubkeyHex - Public key as hex string (compressed or uncompressed)
|
|
112
|
-
* @param derivationPath - BIP-44 style derivation path (e.g., "m/12381/3600/0/0/0")
|
|
113
|
-
* @returns BN254 keystore object ready to be serialized to JSON
|
|
114
|
-
* @throws Error if private key is not 32-byte hex
|
|
115
|
-
*/
|
|
116
|
-
export function createBn254Keystore(
|
|
117
|
-
password: string,
|
|
104
|
+
const pbkdf2Async = promisify(pbkdf2);
|
|
105
|
+
|
|
106
|
+
function createBn254KeystoreFromDerivedKey(
|
|
118
107
|
privateKeyHex: string,
|
|
119
108
|
pubkeyHex: string,
|
|
120
109
|
derivationPath: string,
|
|
110
|
+
salt: Buffer,
|
|
111
|
+
iv: Buffer,
|
|
112
|
+
dk: Buffer,
|
|
121
113
|
): Bn254Keystore {
|
|
122
114
|
const ensureHex = (hex: string) => hex.replace(/^0x/i, '');
|
|
123
115
|
const privHex = ensureHex(privateKeyHex);
|
|
@@ -125,11 +117,7 @@ export function createBn254Keystore(
|
|
|
125
117
|
throw new Error('BLS private key must be 32-byte hex');
|
|
126
118
|
}
|
|
127
119
|
|
|
128
|
-
const salt = randomBytes(32);
|
|
129
|
-
const iv = randomBytes(16);
|
|
130
|
-
const dk = pbkdf2Sync(Buffer.from(password.normalize('NFKD'), 'utf8'), salt, 262144, 32, 'sha256');
|
|
131
120
|
const cipherKey = dk.subarray(0, 16);
|
|
132
|
-
|
|
133
121
|
const cipher = createCipheriv('aes-128-ctr', cipherKey, iv);
|
|
134
122
|
const plaintext = Buffer.from(privHex, 'hex');
|
|
135
123
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
@@ -166,6 +154,31 @@ export function createBn254Keystore(
|
|
|
166
154
|
};
|
|
167
155
|
}
|
|
168
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Creates a BN254 keystore object for a BN254 BLS private key.
|
|
159
|
+
*
|
|
160
|
+
* Uses PBKDF2 with SHA-256 for key derivation and AES-128-CTR for encryption,
|
|
161
|
+
* following the EIP-2335 specification format.
|
|
162
|
+
*
|
|
163
|
+
* @param password - Password for encrypting the private key
|
|
164
|
+
* @param privateKeyHex - Private key as 0x-prefixed hex string (32 bytes)
|
|
165
|
+
* @param pubkeyHex - Public key as hex string (compressed or uncompressed)
|
|
166
|
+
* @param derivationPath - BIP-44 style derivation path (e.g., "m/12381/3600/0/0/0")
|
|
167
|
+
* @returns BN254 keystore object ready to be serialized to JSON
|
|
168
|
+
* @throws Error if private key is not 32-byte hex
|
|
169
|
+
*/
|
|
170
|
+
export async function createBn254Keystore(
|
|
171
|
+
password: string,
|
|
172
|
+
privateKeyHex: string,
|
|
173
|
+
pubkeyHex: string,
|
|
174
|
+
derivationPath: string,
|
|
175
|
+
): Promise<Bn254Keystore> {
|
|
176
|
+
const salt = randomBytes(32);
|
|
177
|
+
const iv = randomBytes(16);
|
|
178
|
+
const dk = await pbkdf2Async(Buffer.from(password.normalize('NFKD'), 'utf8'), salt, 262144, 32, 'sha256');
|
|
179
|
+
return createBn254KeystoreFromDerivedKey(privateKeyHex, pubkeyHex, derivationPath, salt, iv, dk);
|
|
180
|
+
}
|
|
181
|
+
|
|
169
182
|
/**
|
|
170
183
|
* Loads and validates a BN254 keystore file.
|
|
171
184
|
*
|
package/src/crypto/keys/index.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
-
import { BarretenbergSync } from '@aztec/bb.js';
|
|
1
|
+
import { BarretenbergSync, CircuitKind } from '@aztec/bb.js';
|
|
2
2
|
|
|
3
3
|
import { Fr } from '../../curves/bn254/field.js';
|
|
4
4
|
|
|
5
|
-
export async function
|
|
5
|
+
export async function vkAsFields(input: Buffer, kind: CircuitKind): Promise<Fr[]> {
|
|
6
6
|
await BarretenbergSync.initSingleton();
|
|
7
7
|
const api = BarretenbergSync.getSingleton();
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
switch (kind) {
|
|
9
|
+
case CircuitKind.App: {
|
|
10
|
+
const response = api.megaAppVkAsFields({ verificationKey: input });
|
|
11
|
+
return response.fields.map(field => Fr.fromBuffer(Buffer.from(field)));
|
|
12
|
+
}
|
|
13
|
+
case CircuitKind.Kernel: {
|
|
14
|
+
const response = api.megaKernelVkAsFields({ verificationKey: input });
|
|
15
|
+
return response.fields.map(field => Fr.fromBuffer(Buffer.from(field)));
|
|
16
|
+
}
|
|
17
|
+
case CircuitKind.HidingKernel: {
|
|
18
|
+
const response = api.megaZKVkAsFields({ verificationKey: input });
|
|
19
|
+
return response.fields.map(field => Fr.fromBuffer(Buffer.from(field)));
|
|
20
|
+
}
|
|
21
|
+
default: {
|
|
22
|
+
const _exhaustive: never = kind;
|
|
23
|
+
throw new Error(`vkAsFields: unhandled CircuitKind ${_exhaustive}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
10
26
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'node:events';
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
|
+
import * as net from 'node:net';
|
|
3
4
|
import type { Readable } from 'node:stream';
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -36,8 +37,22 @@ export class FifoFrameReader extends EventEmitter<FifoFrameReaderEvents> {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** Open a FIFO at the given path and start reading frames. */
|
|
39
|
-
start(fifoPath: string
|
|
40
|
-
|
|
40
|
+
start(fifoPath: string): void {
|
|
41
|
+
// Read the FIFO through a libuv pipe handle (net.Socket) rather than fs.createReadStream.
|
|
42
|
+
// An fs read stream services the pipe with a blocking threadpool read that destroy() cannot
|
|
43
|
+
// cancel: if a writer never closes, that read stays parked and keeps the host process alive
|
|
44
|
+
// (manifesting as Jest "did not exit"). Opening O_NONBLOCK and wrapping the fd in a pipe
|
|
45
|
+
// handle makes reads epoll-based, so stop()'s destroy() releases the handle immediately,
|
|
46
|
+
// regardless of whether the writer is still attached.
|
|
47
|
+
const fd = fs.openSync(fifoPath, fs.constants.O_RDONLY | fs.constants.O_NONBLOCK);
|
|
48
|
+
let socket: net.Socket;
|
|
49
|
+
try {
|
|
50
|
+
socket = new net.Socket({ fd, readable: true, writable: false });
|
|
51
|
+
} catch (err) {
|
|
52
|
+
fs.closeSync(fd);
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
this.startFromStream(socket);
|
|
41
56
|
}
|
|
42
57
|
|
|
43
58
|
/** Start reading frames from an existing readable stream. */
|
|
@@ -14,8 +14,9 @@ const DEFAULT_BATCH_WINDOW_MS = 0;
|
|
|
14
14
|
// the maximum size of a batched request
|
|
15
15
|
const DEFAULT_MAX_BATCH_SIZE = 100;
|
|
16
16
|
|
|
17
|
-
//
|
|
18
|
-
|
|
17
|
+
// 1 mb, matching the JSON-RPC server's default maxBodySizeBytes so the client never assembles a batch the
|
|
18
|
+
// server rejects for exceeding the body limit
|
|
19
|
+
const DEFAULT_MAX_REQUESTY_BODY_SIZE = 1 * 1024 * 1024;
|
|
19
20
|
|
|
20
21
|
export type SafeJsonRpcClientOptions = {
|
|
21
22
|
namespaceMethods?: string | false;
|
|
@@ -224,7 +224,11 @@ export class SafeJsonRpcServer {
|
|
|
224
224
|
result = await this.proxy.call(method, params);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
return
|
|
227
|
+
// Coerce an undefined return value to null so the response always carries a `result` key.
|
|
228
|
+
// JSON.stringify drops undefined-valued keys, which would otherwise produce a JSON-RPC
|
|
229
|
+
// response with neither `result` nor `error` — a spec violation that leaves callers unable
|
|
230
|
+
// to distinguish "not found" from a malformed response.
|
|
231
|
+
return { jsonrpc, id, result: result ?? null };
|
|
228
232
|
} catch (err: any) {
|
|
229
233
|
if (err && err instanceof ZodError) {
|
|
230
234
|
const message = err.issues.map(e => `${e.message} (${e.path.join('.')})`).join('. ') || 'Validation error';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { pino } from 'pino';
|
|
2
|
+
|
|
3
|
+
function getSeverity(label: string): { severityText: string; severityNumber: number } {
|
|
4
|
+
switch (label) {
|
|
5
|
+
case 'trace':
|
|
6
|
+
return { severityText: 'TRACE', severityNumber: 1 };
|
|
7
|
+
case 'debug':
|
|
8
|
+
return { severityText: 'DEBUG', severityNumber: 5 };
|
|
9
|
+
case 'verbose':
|
|
10
|
+
return { severityText: 'VERBOSE', severityNumber: 7 };
|
|
11
|
+
case 'info':
|
|
12
|
+
return { severityText: 'INFO', severityNumber: 9 };
|
|
13
|
+
case 'warn':
|
|
14
|
+
return { severityText: 'WARN', severityNumber: 13 };
|
|
15
|
+
case 'error':
|
|
16
|
+
return { severityText: 'ERROR', severityNumber: 17 };
|
|
17
|
+
case 'fatal':
|
|
18
|
+
return { severityText: 'FATAL', severityNumber: 21 };
|
|
19
|
+
default:
|
|
20
|
+
return { severityText: 'UNSPECIFIED', severityNumber: 0 };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Pino configuration that adds OpenTelemetry severity fields understood by CloudWatch Logs. */
|
|
25
|
+
export const AWSCloudLoggerConfig = {
|
|
26
|
+
messageKey: 'msg',
|
|
27
|
+
formatters: {
|
|
28
|
+
level(label: string, level: number): object {
|
|
29
|
+
return { level, ...getSeverity(label) };
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
} satisfies pino.LoggerOptions;
|
package/src/log/pino-logger.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { inspect } from 'util';
|
|
|
7
7
|
import { compactArray } from '../collection/array.js';
|
|
8
8
|
import type { EnvVar } from '../config/index.js';
|
|
9
9
|
import { parseBooleanEnv } from '../config/parse-env.js';
|
|
10
|
+
import { AWSCloudLoggerConfig } from './aws-logger-config.js';
|
|
10
11
|
import { convertBigintsToStrings } from './bigint-utils.js';
|
|
11
12
|
import { GoogleCloudLoggerConfig } from './gcloud-logger-config.js';
|
|
12
13
|
import { getLogLevelFromFilters, parseLogLevelEnvVar } from './log-filters.js';
|
|
@@ -134,8 +135,17 @@ export const [logLevel, logFilters] = parseLogLevelEnvVar(process.env.LOG_LEVEL,
|
|
|
134
135
|
// Define custom logging levels for pino.
|
|
135
136
|
const customLevels = { verbose: 25 };
|
|
136
137
|
|
|
137
|
-
// Global pino options, tweaked for
|
|
138
|
+
// Global pino options, tweaked for the active cloud logging backend.
|
|
138
139
|
const useGcloudLogging = parseBooleanEnv(process.env['USE_GCLOUD_LOGGING' satisfies EnvVar]);
|
|
140
|
+
const useAwsLogging = parseBooleanEnv(process.env['USE_AWS_LOGGING' satisfies EnvVar]);
|
|
141
|
+
const loggingConfig = useGcloudLogging
|
|
142
|
+
? GoogleCloudLoggerConfig
|
|
143
|
+
: useAwsLogging
|
|
144
|
+
? AWSCloudLoggerConfig
|
|
145
|
+
: {
|
|
146
|
+
formatters: {},
|
|
147
|
+
messageKey: 'msg',
|
|
148
|
+
};
|
|
139
149
|
|
|
140
150
|
const redactedPaths = [
|
|
141
151
|
'validatorPrivateKeys',
|
|
@@ -156,7 +166,7 @@ const redactedPaths = [
|
|
|
156
166
|
|
|
157
167
|
const pinoOpts: pino.LoggerOptions<keyof typeof customLevels> = {
|
|
158
168
|
customLevels,
|
|
159
|
-
messageKey:
|
|
169
|
+
messageKey: loggingConfig.messageKey,
|
|
160
170
|
useOnlyCustomLevels: false,
|
|
161
171
|
level: logLevel,
|
|
162
172
|
redact: {
|
|
@@ -170,8 +180,8 @@ const pinoOpts: pino.LoggerOptions<keyof typeof customLevels> = {
|
|
|
170
180
|
},
|
|
171
181
|
formatters: {
|
|
172
182
|
log: obj => convertBigintsToStrings(obj) as Record<string, unknown>,
|
|
183
|
+
...loggingConfig.formatters,
|
|
173
184
|
},
|
|
174
|
-
...(useGcloudLogging ? GoogleCloudLoggerConfig : {}),
|
|
175
185
|
};
|
|
176
186
|
|
|
177
187
|
export const levels = {
|
|
@@ -241,7 +251,7 @@ const pinoPrettyBaseOpts = {
|
|
|
241
251
|
destination: 2,
|
|
242
252
|
sync: true,
|
|
243
253
|
colorize: useColor,
|
|
244
|
-
ignore: 'module,actor,instanceId,pid,hostname,trace_id,span_id,trace_flags,severity',
|
|
254
|
+
ignore: 'module,actor,instanceId,pid,hostname,trace_id,span_id,trace_flags,severity,severityText,severityNumber',
|
|
245
255
|
customLevels: 'fatal:60,error:50,warn:40,info:30,verbose:25,debug:20,trace:10',
|
|
246
256
|
customColors: 'fatal:bgRed,error:red,warn:yellow,info:green,verbose:magenta,debug:blue,trace:gray',
|
|
247
257
|
minimumLevel: 'trace' as const,
|
|
@@ -22,16 +22,17 @@ export function makeLoggingErrorHandler(
|
|
|
22
22
|
* at a specified polling interval. It allows starting, stopping, and checking the status of the
|
|
23
23
|
* internally managed promise. The class also supports interrupting the polling process when stopped.
|
|
24
24
|
*/
|
|
25
|
-
export class RunningPromise {
|
|
25
|
+
export class RunningPromise<T = void> {
|
|
26
26
|
private running = false;
|
|
27
27
|
private runningPromise = Promise.resolve();
|
|
28
28
|
private interruptibleSleep = new InterruptibleSleep();
|
|
29
29
|
private requested: PromiseWithResolvers<void> | undefined = undefined;
|
|
30
|
+
private requestedArg: T | undefined = undefined;
|
|
30
31
|
|
|
31
32
|
public static readonly EXIT: typeof EXIT = EXIT;
|
|
32
33
|
|
|
33
34
|
constructor(
|
|
34
|
-
private fn: () => void | Promise<void>,
|
|
35
|
+
private fn: (arg?: T) => void | Promise<void>,
|
|
35
36
|
private logger = createLogger('running-promise'),
|
|
36
37
|
private pollingIntervalMS = 10000,
|
|
37
38
|
private handleError: ErrorHandler = makeLoggingErrorHandler(logger),
|
|
@@ -51,7 +52,7 @@ export class RunningPromise {
|
|
|
51
52
|
while (this.running) {
|
|
52
53
|
const hasRequested = this.requested !== undefined;
|
|
53
54
|
try {
|
|
54
|
-
await this.fn();
|
|
55
|
+
await this.fn(this.requestedArg);
|
|
55
56
|
} catch (err) {
|
|
56
57
|
const code = await this.handleError(err);
|
|
57
58
|
if (code === RunningPromise.EXIT) {
|
|
@@ -64,6 +65,7 @@ export class RunningPromise {
|
|
|
64
65
|
if (hasRequested) {
|
|
65
66
|
this.requested!.resolve();
|
|
66
67
|
this.requested = undefined;
|
|
68
|
+
this.requestedArg = undefined;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
// If no immediate run was requested, sleep for the polling interval.
|
|
@@ -101,15 +103,16 @@ export class RunningPromise {
|
|
|
101
103
|
* Triggers an immediate run of the function, bypassing the polling interval.
|
|
102
104
|
* If the function is currently running, it will be allowed to continue and then called again immediately.
|
|
103
105
|
*/
|
|
104
|
-
public async trigger() {
|
|
106
|
+
public async trigger(arg?: T): Promise<void> {
|
|
105
107
|
if (!this.running) {
|
|
106
|
-
return this.fn();
|
|
108
|
+
return this.fn(arg);
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
let requested = this.requested;
|
|
110
112
|
if (!requested) {
|
|
111
113
|
requested = promiseWithResolvers<void>();
|
|
112
114
|
this.requested = requested;
|
|
115
|
+
this.requestedArg = arg;
|
|
113
116
|
this.interruptibleSleep.interrupt();
|
|
114
117
|
}
|
|
115
118
|
await requested!.promise;
|
package/src/retry/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TimeoutError } from '../error/index.js';
|
|
2
2
|
import { type Logger, createLogger } from '../log/index.js';
|
|
3
3
|
import { sleep } from '../sleep/index.js';
|
|
4
|
-
import { Timer } from '../timer/index.js';
|
|
4
|
+
import { type DateProvider, Timer } from '../timer/index.js';
|
|
5
5
|
|
|
6
6
|
/** An error that indicates that the operation should not be retried. */
|
|
7
7
|
export class NoRetryError extends Error {}
|
|
@@ -72,6 +72,34 @@ export async function retry<Result>(
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Timeout specification accepted by {@link retryUntil}. Either a plain number of seconds, an explicit
|
|
77
|
+
* `{ timeout }` in seconds, or an absolute `{ deadline }` with an optional {@link DateProvider} used to
|
|
78
|
+
* read the current time. A deadline is converted to the remaining seconds at call time; a deadline that
|
|
79
|
+
* has already passed yields a zero remaining budget, matching the immediate-timeout semantics of a
|
|
80
|
+
* non-positive numeric timeout.
|
|
81
|
+
*/
|
|
82
|
+
export type RetryUntilTimeout = number | { timeout: number } | { deadline: Date; dateProvider?: DateProvider };
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Resolves a {@link RetryUntilTimeout} to a numeric timeout in seconds for the legacy timer-based loop.
|
|
86
|
+
* A numeric/`{ timeout }` value passes through unchanged (0 means never time out, negative times out on the
|
|
87
|
+
* first interval). A `{ deadline }` is the remaining seconds until the deadline; when the deadline is already
|
|
88
|
+
* at or past `now` it resolves to a negative value so the loop times out immediately instead of being read as
|
|
89
|
+
* the never-timeout sentinel 0.
|
|
90
|
+
*/
|
|
91
|
+
function resolveRetryUntilTimeoutSeconds(timeout: RetryUntilTimeout): number {
|
|
92
|
+
if (typeof timeout === 'number') {
|
|
93
|
+
return timeout;
|
|
94
|
+
}
|
|
95
|
+
if ('deadline' in timeout) {
|
|
96
|
+
const now = timeout.dateProvider?.now() ?? Date.now();
|
|
97
|
+
const remainingSeconds = (timeout.deadline.getTime() - now) / 1000;
|
|
98
|
+
return remainingSeconds > 0 ? remainingSeconds : -1;
|
|
99
|
+
}
|
|
100
|
+
return timeout.timeout;
|
|
101
|
+
}
|
|
102
|
+
|
|
75
103
|
/**
|
|
76
104
|
* Retry an asynchronous function until it returns a truthy value or the specified timeout is exceeded.
|
|
77
105
|
* The function is retried periodically with a fixed interval between attempts. The operation can be named for better error messages.
|
|
@@ -79,16 +107,20 @@ export async function retry<Result>(
|
|
|
79
107
|
*
|
|
80
108
|
* @param fn - The asynchronous function to be retried, which should return a truthy value upon success or undefined otherwise.
|
|
81
109
|
* @param name - The optional name of the operation, used for generating timeout error message.
|
|
82
|
-
* @param timeout - The
|
|
110
|
+
* @param timeout - The maximum time to keep retrying before throwing a timeout error. Accepts a number of
|
|
111
|
+
* seconds (0 = never time out), an explicit `{ timeout }` in seconds, or an absolute `{ deadline }` with an
|
|
112
|
+
* optional `dateProvider`. A deadline already in the past times out on the first interval, the same as a
|
|
113
|
+
* zero/negative numeric timeout. Defaults to 0 (never timeout).
|
|
83
114
|
* @param interval - The optional interval, in seconds, between retry attempts. Defaults to 1 second.
|
|
84
115
|
* @returns A Promise that resolves with the successful (truthy) result of the provided function, or rejects if timeout is exceeded.
|
|
85
116
|
*/
|
|
86
117
|
export async function retryUntil<T>(
|
|
87
118
|
fn: () => (T | undefined) | Promise<T | undefined>,
|
|
88
119
|
name = '',
|
|
89
|
-
timeout = 0,
|
|
120
|
+
timeout: RetryUntilTimeout = 0,
|
|
90
121
|
interval = 1,
|
|
91
122
|
) {
|
|
123
|
+
const timeoutSeconds = resolveRetryUntilTimeoutSeconds(timeout);
|
|
92
124
|
const timer = new Timer();
|
|
93
125
|
while (true) {
|
|
94
126
|
const result = await fn();
|
|
@@ -98,7 +130,7 @@ export async function retryUntil<T>(
|
|
|
98
130
|
|
|
99
131
|
await sleep(interval * 1000);
|
|
100
132
|
|
|
101
|
-
if (
|
|
133
|
+
if (timeoutSeconds && timer.s() > timeoutSeconds) {
|
|
102
134
|
throw new TimeoutError(name ? `Timeout awaiting ${name}` : 'Timeout');
|
|
103
135
|
}
|
|
104
136
|
}
|
|
@@ -255,6 +255,11 @@ export class BufferReader {
|
|
|
255
255
|
if (maxSize !== undefined && size > maxSize) {
|
|
256
256
|
throw new Error(`Vector size ${size} exceeds maximum allowed ${maxSize}`);
|
|
257
257
|
}
|
|
258
|
+
// Every element consumes at least one byte, so a size beyond the bytes left is unsatisfiable. Reject it
|
|
259
|
+
// up front rather than relying on each item deserializer to bounds-check as the loop runs out of input.
|
|
260
|
+
if (size > this.remainingBytes()) {
|
|
261
|
+
throw new Error(`Vector size ${size} exceeds remaining buffer length ${this.remainingBytes()}`);
|
|
262
|
+
}
|
|
258
263
|
const result = new Array<T>(size);
|
|
259
264
|
for (let i = 0; i < size; i++) {
|
|
260
265
|
result[i] = itemDeserializer.fromBuffer(this);
|
|
@@ -23,56 +23,6 @@ export function serializeArrayOfBufferableToVector(objs: Bufferable[], prefixLen
|
|
|
23
23
|
return Buffer.concat([lengthBuf, ...arr]);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* Helper function for deserializeArrayFromVector.
|
|
28
|
-
*/
|
|
29
|
-
type DeserializeFn<T> = (
|
|
30
|
-
buf: Buffer,
|
|
31
|
-
offset: number,
|
|
32
|
-
) => {
|
|
33
|
-
/**
|
|
34
|
-
* The deserialized type.
|
|
35
|
-
*/
|
|
36
|
-
elem: T;
|
|
37
|
-
/**
|
|
38
|
-
* How many bytes to advance by.
|
|
39
|
-
*/
|
|
40
|
-
adv: number;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Deserializes an array from a vector on an element-by-element basis.
|
|
45
|
-
* @param deserialize - A function used to deserialize each element of the vector.
|
|
46
|
-
* @param vector - The vector to deserialize.
|
|
47
|
-
* @param offset - The position in the vector to start deserializing from.
|
|
48
|
-
* @returns Deserialized array and how many bytes we advanced by.
|
|
49
|
-
*/
|
|
50
|
-
export function deserializeArrayFromVector<T>(
|
|
51
|
-
deserialize: DeserializeFn<T>,
|
|
52
|
-
vector: Buffer,
|
|
53
|
-
offset = 0,
|
|
54
|
-
): {
|
|
55
|
-
/**
|
|
56
|
-
* The deserialized array.
|
|
57
|
-
*/
|
|
58
|
-
elem: T[];
|
|
59
|
-
/**
|
|
60
|
-
* How many bytes we advanced by.
|
|
61
|
-
*/
|
|
62
|
-
adv: number;
|
|
63
|
-
} {
|
|
64
|
-
let pos = offset;
|
|
65
|
-
const size = vector.readUInt32BE(pos);
|
|
66
|
-
pos += 4;
|
|
67
|
-
const arr = new Array<T>(size);
|
|
68
|
-
for (let i = 0; i < size; ++i) {
|
|
69
|
-
const { elem, adv } = deserialize(vector, pos);
|
|
70
|
-
pos += adv;
|
|
71
|
-
arr[i] = elem;
|
|
72
|
-
}
|
|
73
|
-
return { elem: arr, adv: pos - offset };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
26
|
/**
|
|
77
27
|
* Cast a uint8 array to a number.
|
|
78
28
|
* @param array - The uint8 array.
|
package/src/string/index.ts
CHANGED
|
@@ -37,6 +37,20 @@ export function truncate(str: string, length: number = 64): string {
|
|
|
37
37
|
return str.length > length ? str.slice(0, length) + '...' : str;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/** Formats a duration in seconds into a compact human-readable string (e.g. `45s`, `12m`, `2h 5m`). */
|
|
41
|
+
export function formatSeconds(seconds: number): string {
|
|
42
|
+
if (seconds < 60) {
|
|
43
|
+
return `${Math.round(seconds)}s`;
|
|
44
|
+
}
|
|
45
|
+
const minutes = Math.round(seconds / 60);
|
|
46
|
+
if (minutes < 60) {
|
|
47
|
+
return `${minutes}m`;
|
|
48
|
+
}
|
|
49
|
+
const hours = Math.floor(minutes / 60);
|
|
50
|
+
const remMinutes = minutes % 60;
|
|
51
|
+
return remMinutes > 0 ? `${hours}h ${remMinutes}m` : `${hours}h`;
|
|
52
|
+
}
|
|
53
|
+
|
|
40
54
|
export function isoDate(date?: Date) {
|
|
41
55
|
return (date ?? new Date()).toISOString().replace(/[-:T]/g, '').replace(/\..+$/, '');
|
|
42
56
|
}
|
|
@@ -50,14 +50,13 @@ export function updateInlineTestData(targetFileFromRepoRoot: string, itemName: s
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* Updates the sample Prover.toml files in noir-projects/noir-protocol-circuits/crates/.
|
|
53
|
+
* Updates the sample Prover.toml files in noir-projects/fnd/noir-protocol-circuits/crates/.
|
|
54
54
|
* @remarks Requires AZTEC_GENERATE_TEST_DATA=1 to be set
|
|
55
|
-
* To re-gen, run 'AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e full.test'
|
|
56
|
-
* To re-gen public base only, run 'AZTEC_GENERATE_TEST_DATA=1 yarn workspace @aztec/prover-client test orchestrator_public_functions'
|
|
55
|
+
* To re-gen, run 'AZTEC_GENERATE_TEST_DATA=1 FAKE_PROOFS=1 yarn test:e2e e2e_prover/full.test'
|
|
57
56
|
*/
|
|
58
57
|
export function updateProtocolCircuitSampleInputs(circuitName: string, value: string) {
|
|
59
58
|
const logger = createConsoleLogger('aztec:testing:test_data');
|
|
60
|
-
const targetFileFromRepoRoot = `noir-projects/noir-protocol-circuits/crates/${circuitName}/Prover.toml`;
|
|
59
|
+
const targetFileFromRepoRoot = `noir-projects/fnd/noir-protocol-circuits/crates/${circuitName}/Prover.toml`;
|
|
61
60
|
const targetFile = getPathToFile(targetFileFromRepoRoot);
|
|
62
61
|
writeFileSync(targetFile, value);
|
|
63
62
|
logger(`Updated test data in ${targetFile} for ${circuitName}`);
|
|
@@ -38,14 +38,6 @@ export class MembershipWitness<N extends number> {
|
|
|
38
38
|
return [new Fr(this.leafIndex), ...this.siblingPath];
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
/**
|
|
42
|
-
* Returns a representation of the membership witness as expected by intrinsic Noir deserialization.
|
|
43
|
-
*/
|
|
44
|
-
public toNoirRepresentation(): (string | string[])[] {
|
|
45
|
-
// TODO(#12874): remove the stupid as string conversion by modifying ForeignCallOutput type in acvm.js
|
|
46
|
-
return [new Fr(this.leafIndex).toString() as string, this.siblingPath.map(fr => fr.toString()) as string[]];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
41
|
static schemaFor<N extends number>(size: N) {
|
|
50
42
|
return schemas.Buffer.transform(b => MembershipWitness.fromBuffer(b, size));
|
|
51
43
|
}
|
|
@@ -3,15 +3,18 @@ import { z } from 'zod';
|
|
|
3
3
|
import { makeTuple } from '../array/array.js';
|
|
4
4
|
import { Fr } from '../curves/bn254/index.js';
|
|
5
5
|
import { schemas } from '../schemas/index.js';
|
|
6
|
-
import {
|
|
7
|
-
type Tuple,
|
|
8
|
-
assertLength,
|
|
9
|
-
deserializeArrayFromVector,
|
|
10
|
-
serializeArrayOfBufferableToVector,
|
|
11
|
-
} from '../serialize/index.js';
|
|
6
|
+
import { BufferReader, type Tuple, assertLength, serializeArrayOfBufferableToVector } from '../serialize/index.js';
|
|
12
7
|
import { bufferToHex, hexToBuffer } from '../string/index.js';
|
|
13
8
|
import type { Hasher } from './hasher.js';
|
|
14
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Upper bound on the elements a serialized sibling path may declare. The deepest protocol tree is 42 levels,
|
|
12
|
+
* and the stacked path proving L2-to-L1 message inclusion spans four unbalanced trees, so this leaves room to
|
|
13
|
+
* spare while keeping a malformed length prefix from driving a large allocation. `stdlib` asserts that it stays
|
|
14
|
+
* above every protocol tree height, which this package cannot check itself without depending on `@aztec/constants`.
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_SIBLING_PATH_LENGTH = 128;
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Contains functionality to compute and serialize/deserialize a sibling path.
|
|
17
20
|
* E.g. Sibling path for a leaf at index 3 in a tree of depth 3 consists of:
|
|
@@ -118,26 +121,12 @@ export class SiblingPath<N extends number> {
|
|
|
118
121
|
* @param buf - A buffer containing the buffer representation of SiblingPath.
|
|
119
122
|
* @param offset - An offset to start deserializing from.
|
|
120
123
|
* @returns A SiblingPath object.
|
|
124
|
+
* @throws If the length prefix exceeds MAX_SIBLING_PATH_LENGTH or the buffer holds fewer elements than it declares.
|
|
121
125
|
*/
|
|
122
126
|
static fromBuffer<N extends number>(buf: Buffer, offset = 0): SiblingPath<N> {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Deserializes a SiblingPath object from a slice of a part of a buffer and returns the amount of bytes advanced.
|
|
129
|
-
* @param buf - A buffer representation of the sibling path.
|
|
130
|
-
* @param offset - An offset to start deserializing from.
|
|
131
|
-
* @returns The deserialized sibling path and the number of bytes advanced.
|
|
132
|
-
*/
|
|
133
|
-
static deserialize<N extends number>(buf: Buffer, offset = 0) {
|
|
134
|
-
const deserializePath = (buf: Buffer, offset: number) => ({
|
|
135
|
-
elem: buf.slice(offset, offset + 32),
|
|
136
|
-
adv: 32,
|
|
137
|
-
});
|
|
138
|
-
const { elem, adv } = deserializeArrayFromVector(deserializePath, buf, offset);
|
|
139
|
-
const size = elem.length;
|
|
140
|
-
return { elem: new SiblingPath<N>(size as N, elem), adv };
|
|
127
|
+
const reader = new BufferReader(buf, offset);
|
|
128
|
+
const path = reader.readVector({ fromBuffer: r => r.readBytes(Fr.SIZE_IN_BYTES) }, MAX_SIBLING_PATH_LENGTH);
|
|
129
|
+
return new SiblingPath<N>(path.length as N, path);
|
|
141
130
|
}
|
|
142
131
|
|
|
143
132
|
/**
|
package/src/types/index.ts
CHANGED
|
@@ -29,13 +29,43 @@ export function isErrorClass<T extends Error>(value: unknown, errorClass: new (.
|
|
|
29
29
|
return value instanceof errorClass || (value instanceof Error && value.name === errorClass.name);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const MAX_ERR_DEPTH = 10;
|
|
33
|
+
|
|
34
|
+
/** Returns the first error in the cause chain matching the given error class. */
|
|
35
|
+
export function getErrorCause<T extends Error>(err: unknown, errorClass: new (...args: any[]) => T): T | undefined {
|
|
36
|
+
let current = err;
|
|
37
|
+
for (let i = 0; current !== undefined && current !== null && i < MAX_ERR_DEPTH; i++) {
|
|
38
|
+
if (isErrorClass(current, errorClass)) {
|
|
39
|
+
return current;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (typeof current === 'object' && Object.hasOwn(current, 'cause')) {
|
|
43
|
+
current = (current as { cause: unknown }).cause;
|
|
44
|
+
} else {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
32
51
|
/** Resolves a record-like type. Lifted from viem. */
|
|
33
52
|
export type Prettify<T> = {
|
|
34
53
|
[K in keyof T]: T[K];
|
|
35
54
|
} & {};
|
|
36
55
|
|
|
56
|
+
/** Returns a type T based on a flag: T if true, undefined if false, optional otherwise. */
|
|
57
|
+
export type DefineIfFlag<Opts, Key extends keyof Opts, T> = Opts extends {
|
|
58
|
+
[K in Key]: true;
|
|
59
|
+
}
|
|
60
|
+
? T
|
|
61
|
+
: Opts extends { [K in Key]: false }
|
|
62
|
+
? never
|
|
63
|
+
: Opts extends { [K in Key]?: boolean }
|
|
64
|
+
? T | undefined
|
|
65
|
+
: never;
|
|
66
|
+
|
|
37
67
|
/** Returns a type with fields conditionally required based on a flag */
|
|
38
|
-
export type
|
|
68
|
+
export type PickIfFlag<
|
|
39
69
|
OptsSchema,
|
|
40
70
|
Opts extends OptsSchema,
|
|
41
71
|
Key extends keyof OptsSchema,
|
|
@@ -48,6 +78,9 @@ export type IfFlag<
|
|
|
48
78
|
? Partial<Field>
|
|
49
79
|
: {};
|
|
50
80
|
|
|
81
|
+
/** Picks only the defined (non-undefined) properties of a type. */
|
|
82
|
+
export type PickDefined<T> = Prettify<Pick<T, { [K in keyof T]: T[K] extends undefined ? never : K }[keyof T]>>;
|
|
83
|
+
|
|
51
84
|
/**
|
|
52
85
|
* Type-safe Event Emitter type
|
|
53
86
|
* @example
|