@aztec/foundation 0.42.0 → 0.44.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.
Files changed (53) hide show
  1. package/dest/abi/abi.d.ts +2 -5
  2. package/dest/abi/abi.d.ts.map +1 -1
  3. package/dest/abi/abi.js +1 -1
  4. package/dest/abi/index.d.ts +2 -1
  5. package/dest/abi/index.d.ts.map +1 -1
  6. package/dest/abi/index.js +3 -2
  7. package/dest/abi/note_selector.d.ts +41 -0
  8. package/dest/abi/note_selector.d.ts.map +1 -0
  9. package/dest/abi/note_selector.js +57 -0
  10. package/dest/collection/array.d.ts +13 -0
  11. package/dest/collection/array.d.ts.map +1 -1
  12. package/dest/collection/array.js +12 -1
  13. package/dest/crypto/random/randomness_singleton.js +3 -3
  14. package/dest/error/index.d.ts +1 -1
  15. package/dest/error/index.d.ts.map +1 -1
  16. package/dest/error/index.js +2 -2
  17. package/dest/fields/fields.d.ts +4 -3
  18. package/dest/fields/fields.d.ts.map +1 -1
  19. package/dest/fields/fields.js +29 -11
  20. package/dest/fields/point.d.ts +7 -0
  21. package/dest/fields/point.d.ts.map +1 -1
  22. package/dest/fields/point.js +24 -2
  23. package/dest/fs/run_in_dir.d.ts +1 -1
  24. package/dest/fs/run_in_dir.d.ts.map +1 -1
  25. package/dest/fs/run_in_dir.js +7 -6
  26. package/dest/json-rpc/server/json_rpc_server.d.ts.map +1 -1
  27. package/dest/json-rpc/server/json_rpc_server.js +3 -3
  28. package/dest/log/logger.d.ts.map +1 -1
  29. package/dest/log/logger.js +5 -38
  30. package/dest/promise/running-promise.d.ts +1 -1
  31. package/dest/promise/running-promise.d.ts.map +1 -1
  32. package/dest/promise/running-promise.js +1 -1
  33. package/dest/serialize/buffer_reader.d.ts +1 -0
  34. package/dest/serialize/buffer_reader.d.ts.map +1 -1
  35. package/dest/serialize/buffer_reader.js +17 -1
  36. package/dest/serialize/free_funcs.d.ts +1 -0
  37. package/dest/serialize/free_funcs.d.ts.map +1 -1
  38. package/dest/serialize/free_funcs.js +8 -1
  39. package/package.json +11 -3
  40. package/src/abi/abi.ts +2 -5
  41. package/src/abi/index.ts +2 -1
  42. package/src/abi/note_selector.ts +73 -0
  43. package/src/collection/array.ts +14 -0
  44. package/src/crypto/random/randomness_singleton.ts +2 -2
  45. package/src/error/index.ts +1 -1
  46. package/src/fields/fields.ts +32 -11
  47. package/src/fields/point.ts +28 -2
  48. package/src/fs/run_in_dir.ts +10 -5
  49. package/src/json-rpc/server/json_rpc_server.ts +2 -2
  50. package/src/log/logger.ts +5 -38
  51. package/src/promise/running-promise.ts +1 -1
  52. package/src/serialize/buffer_reader.ts +16 -0
  53. package/src/serialize/free_funcs.ts +8 -0
@@ -25,9 +25,6 @@ type DerivedField<T extends BaseField> = {
25
25
  * Conversions from Buffer to BigInt and vice-versa are not cheap.
26
26
  * We allow construction with either form and lazily convert to other as needed.
27
27
  * We only check we are within the field modulus when initializing with bigint.
28
- * If NODE_ENV === 'test', we will always initialize both types to check the modulus.
29
- * This is also necessary in test environment as a lot of tests just use deep equality to check equality.
30
- * WARNING: This could lead to a bugs in production that don't reveal in tests, but it's low risk.
31
28
  */
32
29
  abstract class BaseField {
33
30
  static SIZE_IN_BYTES = 32;
@@ -42,6 +39,11 @@ abstract class BaseField {
42
39
  return this.toBigInt();
43
40
  }
44
41
 
42
+ /** Returns the size in bytes. */
43
+ get size(): number {
44
+ return BaseField.SIZE_IN_BYTES;
45
+ }
46
+
45
47
  protected constructor(value: number | bigint | boolean | BaseField | Buffer) {
46
48
  if (value instanceof Buffer) {
47
49
  if (value.length > BaseField.SIZE_IN_BYTES) {
@@ -62,14 +64,6 @@ abstract class BaseField {
62
64
  } else {
63
65
  throw new Error(`Type '${typeof value}' with value '${value}' passed to BaseField ctor.`);
64
66
  }
65
-
66
- // Loads of our tests are just doing deep equality rather than calling e.g. toBigInt() first.
67
- // This ensures the deep equality passes regardless of the internal representation.
68
- // It also ensures the value range is checked even when initializing as a buffer.
69
- if (process.env.NODE_ENV === 'test') {
70
- this.toBuffer();
71
- this.toBigInt();
72
- }
73
67
  }
74
68
 
75
69
  protected abstract modulus(): bigint;
@@ -243,6 +237,14 @@ export class Fr extends BaseField {
243
237
  return new Fr((this.toBigInt() + rhs.toBigInt()) % Fr.MODULUS);
244
238
  }
245
239
 
240
+ square() {
241
+ return new Fr((this.toBigInt() * this.toBigInt()) % Fr.MODULUS);
242
+ }
243
+
244
+ negate() {
245
+ return new Fr(Fr.MODULUS - this.toBigInt());
246
+ }
247
+
246
248
  sub(rhs: Fr) {
247
249
  const result = this.toBigInt() - rhs.toBigInt();
248
250
  return new Fr(result < 0 ? result + Fr.MODULUS : result);
@@ -392,3 +394,22 @@ export const GrumpkinScalar = Fq;
392
394
  export function reduceFn<TInput, TField extends BaseField>(fn: (input: TInput) => Buffer, field: DerivedField<TField>) {
393
395
  return (input: TInput) => fromBufferReduce(fn(input), field);
394
396
  }
397
+
398
+ /** If we are in test mode, we register a special equality for fields. */
399
+ if (process.env.NODE_ENV === 'test') {
400
+ const areFieldsEqual = (a: unknown, b: unknown): boolean | undefined => {
401
+ const isAField = a instanceof BaseField;
402
+ const isBField = b instanceof BaseField;
403
+
404
+ if (isAField && isBField) {
405
+ return a.equals(b);
406
+ } else if (isAField === isBField) {
407
+ return undefined;
408
+ } else {
409
+ return false;
410
+ }
411
+ };
412
+
413
+ // `addEqualityTesters` doesn't seem to be in the types yet.
414
+ (expect as any).addEqualityTesters([areFieldsEqual]);
415
+ }
@@ -23,7 +23,9 @@ export class Point {
23
23
  * The point's y coordinate
24
24
  */
25
25
  public readonly y: Fr,
26
- ) {}
26
+ ) {
27
+ // TODO: Do we want to check if the point is on the curve here?
28
+ }
27
29
 
28
30
  /**
29
31
  * Generate a random Point instance.
@@ -44,7 +46,7 @@ export class Point {
44
46
  */
45
47
  static fromBuffer(buffer: Buffer | BufferReader) {
46
48
  const reader = BufferReader.asReader(buffer);
47
- return new this(Fr.fromBuffer(reader.readBytes(32)), Fr.fromBuffer(reader.readBytes(32)));
49
+ return new this(Fr.fromBuffer(reader), Fr.fromBuffer(reader));
48
50
  }
49
51
 
50
52
  /**
@@ -134,6 +136,30 @@ export class Point {
134
136
  hash() {
135
137
  return poseidon2Hash(this.toFields());
136
138
  }
139
+
140
+ /**
141
+ * Check if this is point at infinity.
142
+ * Check this is consistent with how bb is encoding the point at infinity
143
+ */
144
+ public get inf() {
145
+ return this.x == Fr.ZERO;
146
+ }
147
+ public toFieldsWithInf() {
148
+ return [this.x, this.y, new Fr(this.inf)];
149
+ }
150
+
151
+ isOnGrumpkin() {
152
+ // TODO: Check this against how bb handles curve check and infinity point check
153
+ if (this.inf) {
154
+ return true;
155
+ }
156
+
157
+ // p.y * p.y == p.x * p.x * p.x - 17
158
+ const A = new Fr(17);
159
+ const lhs = this.y.square();
160
+ const rhs = this.x.square().mul(this.x).sub(A);
161
+ return lhs.equals(rhs);
162
+ }
137
163
  }
138
164
 
139
165
  /**
@@ -1,18 +1,23 @@
1
- import { randomBytes } from 'crypto';
2
1
  import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
3
 
4
4
  // Create a random directory underneath a 'base' directory
5
5
  // Calls a provided method, ensures the random directory is cleaned up afterwards
6
- export async function runInDirectory<T>(workingDirBase: string, fn: (dir: string) => Promise<T>): Promise<T> {
6
+ export async function runInDirectory<T>(
7
+ workingDirBase: string,
8
+ fn: (dir: string) => Promise<T>,
9
+ cleanup: boolean = true,
10
+ ): Promise<T> {
7
11
  // Create random directory to be used for temp files
8
- const workingDirectory = `${workingDirBase}/${randomBytes(8).toString('hex')}`;
9
- await fs.mkdir(workingDirectory, { recursive: true });
12
+ const workingDirectory = await fs.mkdtemp(path.join(workingDirBase, 'tmp-'));
10
13
 
11
14
  await fs.access(workingDirectory);
12
15
 
13
16
  try {
14
17
  return await fn(workingDirectory);
15
18
  } finally {
16
- await fs.rm(workingDirectory, { recursive: true, force: true });
19
+ if (cleanup) {
20
+ await fs.rm(workingDirectory, { recursive: true, force: true });
21
+ }
17
22
  }
18
23
  }
@@ -25,7 +25,7 @@ export class JsonRpcServer {
25
25
  private objectClassMap: JsonClassConverterInput,
26
26
  /** List of methods to disallow from calling remotely */
27
27
  public readonly disallowedMethods: string[] = [],
28
- private log = createDebugLogger('aztec:foundation:json-rpc:server'),
28
+ private log = createDebugLogger('json-rpc:server'),
29
29
  ) {
30
30
  this.proxy = new JsonProxy(handler, stringClassMap, objectClassMap);
31
31
  }
@@ -226,7 +226,7 @@ export type ServerList = {
226
226
  */
227
227
  export function createNamespacedJsonRpcServer(
228
228
  servers: ServerList,
229
- log = createDebugLogger('aztec:foundation:json-rpc:multi-server'),
229
+ log = createDebugLogger('json-rpc:multi-server'),
230
230
  ): JsonRpcServer {
231
231
  const handler = {} as any;
232
232
  const disallowedMethods: string[] = [];
package/src/log/logger.ts CHANGED
@@ -1,6 +1,4 @@
1
1
  import debug from 'debug';
2
- import isNode from 'detect-node';
3
- import { isatty } from 'tty';
4
2
 
5
3
  import { type LogData, type LogFn } from './log_fn.js';
6
4
 
@@ -15,6 +13,9 @@ export type LogLevel = (typeof LogLevels)[number];
15
13
  const envLogLevel = process.env.LOG_LEVEL?.toLowerCase() as LogLevel;
16
14
  const currentLevel = LogLevels.includes(envLogLevel) ? envLogLevel : DefaultLogLevel;
17
15
 
16
+ const namespaces = process.env.DEBUG ?? 'aztec:*';
17
+ debug.enable(namespaces);
18
+
18
19
  /** Log function that accepts an exception object */
19
20
  type ErrorLogFn = (msg: string, err?: Error | unknown, data?: LogData) => void;
20
21
 
@@ -38,9 +39,6 @@ export type DebugLogger = Logger;
38
39
  */
39
40
  export function createDebugLogger(name: string): DebugLogger {
40
41
  const debugLogger = debug(name);
41
- if (currentLevel === 'debug') {
42
- debugLogger.enabled = true;
43
- }
44
42
 
45
43
  const logger = {
46
44
  silent: () => {},
@@ -78,42 +76,11 @@ function logWithDebug(debug: debug.Debugger, level: LogLevel, msg: string, data?
78
76
  }
79
77
 
80
78
  msg = data ? `${msg} ${fmtLogData(data)}` : msg;
81
- if (debug.enabled) {
82
- if (level !== 'debug') {
83
- msg = `${level.toUpperCase()} ${msg}`;
84
- }
85
- debug(msg);
86
- } else if (LogLevels.indexOf(level) <= LogLevels.indexOf(currentLevel)) {
87
- printLog(`${getPrefix(debug, level)} ${msg}`);
79
+ if (debug.enabled && LogLevels.indexOf(level) <= LogLevels.indexOf(currentLevel)) {
80
+ debug('[%s] %s', level.toUpperCase(), msg);
88
81
  }
89
82
  }
90
83
 
91
- /**
92
- * Returns a log prefix that emulates that of npm debug. Uses colors if in node and in a tty.
93
- * @param debugLogger - Instance of npm debug logger.
94
- * @param level - Intended log level (printed out if strictly above current log level).
95
- * @returns Log prefix.
96
- */
97
- function getPrefix(debugLogger: debug.Debugger, level: LogLevel) {
98
- const levelLabel = currentLevel !== level ? ` ${level.toUpperCase()}` : '';
99
- const prefix = `${debugLogger.namespace.replace(/^aztec:/, '')}${levelLabel}`;
100
- if ((!isNode || !isatty(process.stderr.fd)) && !process.env.DEBUG_COLORS) {
101
- return prefix;
102
- }
103
- const colorIndex = debug.selectColor(debugLogger.namespace) as number;
104
- const colorCode = '\u001B[3' + (colorIndex < 8 ? colorIndex : '8;5;' + colorIndex);
105
- return ` ${colorCode};1m${prefix}\u001B[0m`;
106
- }
107
-
108
- /**
109
- * Outputs to console error.
110
- * @param msg - What to log.
111
- */
112
- function printLog(msg: string) {
113
- // eslint-disable-next-line no-console
114
- isNode ? process.stderr.write(msg + '\n') : console.error(msg);
115
- }
116
-
117
84
  /**
118
85
  * Concatenates a log message and an exception.
119
86
  * @param msg - Log message
@@ -10,7 +10,7 @@ export class RunningPromise {
10
10
  private runningPromise = Promise.resolve();
11
11
  private interruptibleSleep = new InterruptibleSleep();
12
12
 
13
- constructor(private fn: () => Promise<void>, private pollingIntervalMS = 10000) {}
13
+ constructor(private fn: () => void | Promise<void>, private pollingIntervalMS = 10000) {}
14
14
 
15
15
  /**
16
16
  * Starts the running promise.
@@ -55,6 +55,7 @@ export class BufferReader {
55
55
  * @returns The read 32-bit unsigned integer value.
56
56
  */
57
57
  public readNumber(): number {
58
+ this.#rangeCheck(4);
58
59
  this.index += 4;
59
60
  return this.buffer.readUint32BE(this.index - 4);
60
61
  }
@@ -76,6 +77,7 @@ export class BufferReader {
76
77
  * @returns The read 16 bit value.
77
78
  */
78
79
  public readUInt16(): number {
80
+ this.#rangeCheck(2);
79
81
  this.index += 2;
80
82
  return this.buffer.readUInt16BE(this.index - 2);
81
83
  }
@@ -87,6 +89,7 @@ export class BufferReader {
87
89
  * @returns The read 8 bit value.
88
90
  */
89
91
  public readUInt8(): number {
92
+ this.#rangeCheck(1);
90
93
  this.index += 1;
91
94
  return this.buffer.readUInt8(this.index - 1);
92
95
  }
@@ -99,6 +102,7 @@ export class BufferReader {
99
102
  * @returns A boolean value representing the byte at the current index.
100
103
  */
101
104
  public readBoolean(): boolean {
105
+ this.#rangeCheck(1);
102
106
  this.index += 1;
103
107
  return Boolean(this.buffer.at(this.index - 1));
104
108
  }
@@ -112,6 +116,7 @@ export class BufferReader {
112
116
  * @returns A new Buffer containing the read bytes.
113
117
  */
114
118
  public readBytes(n: number): Buffer {
119
+ this.#rangeCheck(n);
115
120
  this.index += n;
116
121
  return Buffer.from(this.buffer.subarray(this.index - n, this.index));
117
122
  }
@@ -215,6 +220,7 @@ export class BufferReader {
215
220
  public readBufferArray(size = -1): Buffer[] {
216
221
  const result: Buffer[] = [];
217
222
  const end = size >= 0 ? this.index + size : this.buffer.length;
223
+ this.#rangeCheck(end - this.index);
218
224
  while (this.index < end) {
219
225
  const item = this.readBuffer();
220
226
  result.push(item);
@@ -252,6 +258,7 @@ export class BufferReader {
252
258
  * @returns A Buffer with the next n bytes or the remaining bytes if n is not provided or exceeds the buffer length.
253
259
  */
254
260
  public peekBytes(n?: number): Buffer {
261
+ this.#rangeCheck(n || 0);
255
262
  return this.buffer.subarray(this.index, n ? this.index + n : undefined);
256
263
  }
257
264
 
@@ -276,6 +283,7 @@ export class BufferReader {
276
283
  */
277
284
  public readBuffer(): Buffer {
278
285
  const size = this.readNumber();
286
+ this.#rangeCheck(size);
279
287
  return this.readBytes(size);
280
288
  }
281
289
 
@@ -311,6 +319,14 @@ export class BufferReader {
311
319
  public getLength(): number {
312
320
  return this.buffer.length;
313
321
  }
322
+
323
+ #rangeCheck(numBytes: number) {
324
+ if (this.index + numBytes > this.buffer.length) {
325
+ throw new Error(
326
+ `Attempted to read beyond buffer length. Start index: ${this.index}, Num bytes to read: ${numBytes}, Buffer length: ${this.buffer.length}`,
327
+ );
328
+ }
329
+ }
314
330
  }
315
331
 
316
332
  /**
@@ -194,3 +194,11 @@ export function fromTruncField(field: Fr): Buffer {
194
194
  export function fromFieldsTuple(fields: Tuple<Fr, 2>): Buffer {
195
195
  return from2Fields(fields[0], fields[1]);
196
196
  }
197
+
198
+ export function toHumanReadable(buf: Buffer, maxLen?: number): string {
199
+ const result = buf.every(byte => byte >= 32 && byte <= 126) ? buf.toString('ascii') : `0x${buf.toString('hex')}`;
200
+ if (maxLen && result.length > maxLen) {
201
+ return result.slice(0, maxLen) + '...';
202
+ }
203
+ return result;
204
+ }