@aztec/foundation 0.0.1-commit.0208eb9 → 0.0.1-commit.033589e

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 (85) hide show
  1. package/dest/array/sorted_array.d.ts +6 -1
  2. package/dest/array/sorted_array.d.ts.map +1 -1
  3. package/dest/array/sorted_array.js +18 -15
  4. package/dest/config/env_var.d.ts +2 -2
  5. package/dest/config/env_var.d.ts.map +1 -1
  6. package/dest/config/index.d.ts +1 -1
  7. package/dest/config/index.d.ts.map +1 -1
  8. package/dest/config/index.js +15 -0
  9. package/dest/config/network_config.d.ts +7 -1
  10. package/dest/config/network_config.d.ts.map +1 -1
  11. package/dest/config/network_config.js +2 -1
  12. package/dest/config/network_name.d.ts +2 -2
  13. package/dest/config/network_name.d.ts.map +1 -1
  14. package/dest/config/network_name.js +2 -0
  15. package/dest/crypto/poseidon/index.js +13 -13
  16. package/dest/crypto/secp256k1-signer/utils.d.ts +12 -1
  17. package/dest/crypto/secp256k1-signer/utils.d.ts.map +1 -1
  18. package/dest/crypto/secp256k1-signer/utils.js +26 -0
  19. package/dest/curves/bn254/field.d.ts +2 -1
  20. package/dest/curves/bn254/field.d.ts.map +1 -1
  21. package/dest/curves/bn254/field.js +5 -2
  22. package/dest/eth-signature/eth_signature.d.ts +2 -1
  23. package/dest/eth-signature/eth_signature.d.ts.map +1 -1
  24. package/dest/eth-signature/eth_signature.js +7 -2
  25. package/dest/jest/setup.js +24 -0
  26. package/dest/json-rpc/client/safe_json_rpc_client.d.ts +2 -1
  27. package/dest/json-rpc/client/safe_json_rpc_client.d.ts.map +1 -1
  28. package/dest/json-rpc/client/safe_json_rpc_client.js +1 -1
  29. package/dest/json-rpc/server/api_key_auth.d.ts +19 -0
  30. package/dest/json-rpc/server/api_key_auth.d.ts.map +1 -0
  31. package/dest/json-rpc/server/api_key_auth.js +57 -0
  32. package/dest/json-rpc/server/index.d.ts +2 -1
  33. package/dest/json-rpc/server/index.d.ts.map +1 -1
  34. package/dest/json-rpc/server/index.js +1 -0
  35. package/dest/log/bigint-utils.d.ts +5 -0
  36. package/dest/log/bigint-utils.d.ts.map +1 -0
  37. package/dest/log/bigint-utils.js +21 -0
  38. package/dest/log/gcloud-logger-config.d.ts +1 -1
  39. package/dest/log/gcloud-logger-config.d.ts.map +1 -1
  40. package/dest/log/gcloud-logger-config.js +3 -0
  41. package/dest/log/log-filters.d.ts +17 -4
  42. package/dest/log/log-filters.d.ts.map +1 -1
  43. package/dest/log/log-filters.js +26 -12
  44. package/dest/log/pino-logger.d.ts +1 -1
  45. package/dest/log/pino-logger.d.ts.map +1 -1
  46. package/dest/log/pino-logger.js +6 -2
  47. package/dest/queue/base_memory_queue.d.ts +2 -2
  48. package/dest/queue/base_memory_queue.d.ts.map +1 -1
  49. package/dest/serialize/buffer_reader.d.ts +8 -1
  50. package/dest/serialize/buffer_reader.d.ts.map +1 -1
  51. package/dest/serialize/buffer_reader.js +13 -0
  52. package/dest/serialize/serialize.d.ts +19 -1
  53. package/dest/serialize/serialize.d.ts.map +1 -1
  54. package/dest/serialize/serialize.js +31 -0
  55. package/dest/sleep/index.d.ts +2 -1
  56. package/dest/sleep/index.d.ts.map +1 -1
  57. package/dest/sleep/index.js +10 -1
  58. package/dest/timer/date.d.ts +25 -1
  59. package/dest/timer/date.d.ts.map +1 -1
  60. package/dest/timer/date.js +33 -0
  61. package/dest/transport/transport_client.js +2 -2
  62. package/package.json +22 -2
  63. package/src/array/sorted_array.ts +22 -17
  64. package/src/config/env_var.ts +27 -4
  65. package/src/config/index.ts +15 -0
  66. package/src/config/network_config.ts +1 -0
  67. package/src/config/network_name.ts +4 -1
  68. package/src/crypto/poseidon/index.ts +13 -13
  69. package/src/crypto/secp256k1-signer/utils.ts +32 -0
  70. package/src/curves/bn254/field.ts +6 -2
  71. package/src/eth-signature/eth_signature.ts +7 -1
  72. package/src/jest/setup.mjs +27 -0
  73. package/src/json-rpc/client/safe_json_rpc_client.ts +2 -0
  74. package/src/json-rpc/server/api_key_auth.ts +63 -0
  75. package/src/json-rpc/server/index.ts +1 -0
  76. package/src/log/bigint-utils.ts +25 -0
  77. package/src/log/gcloud-logger-config.ts +5 -0
  78. package/src/log/log-filters.ts +29 -11
  79. package/src/log/pino-logger.ts +6 -2
  80. package/src/queue/base_memory_queue.ts +1 -1
  81. package/src/serialize/buffer_reader.ts +15 -0
  82. package/src/serialize/serialize.ts +32 -0
  83. package/src/sleep/index.ts +10 -1
  84. package/src/timer/date.ts +48 -0
  85. package/src/transport/transport_client.ts +2 -2
@@ -176,6 +176,20 @@ import { numToUInt32BE } from './free_funcs.js';
176
176
  */ export function serializeBigInt(n, width = 32) {
177
177
  return toBufferBE(n, width);
178
178
  }
179
+ /**
180
+ * Serialize a signed BigInt value into a Buffer of specified width using two's complement.
181
+ * @param n - The signed BigInt value to be serialized.
182
+ * @param width - The width (in bytes) of the output Buffer, optional with default value 32.
183
+ * @returns A Buffer containing the serialized signed BigInt value in big-endian format.
184
+ */ export function serializeSignedBigInt(n, width = 32) {
185
+ const widthBits = BigInt(width * 8);
186
+ const max = 1n << widthBits - 1n;
187
+ if (n < -max || n >= max) {
188
+ throw new Error(`Signed BigInt ${n.toString()} does not fit into ${width} bytes`);
189
+ }
190
+ const unsigned = n < 0n ? (1n << widthBits) + n : n;
191
+ return toBufferBE(unsigned, width);
192
+ }
179
193
  /**
180
194
  * Deserialize a big integer from a buffer, given an offset and width.
181
195
  * Reads the specified number of bytes from the buffer starting at the offset, converts it to a big integer, and returns the deserialized result along with the number of bytes read (advanced).
@@ -190,6 +204,23 @@ import { numToUInt32BE } from './free_funcs.js';
190
204
  adv: width
191
205
  };
192
206
  }
207
+ /**
208
+ * Deserialize a signed BigInt from a buffer (two's complement).
209
+ * @param buf - The buffer containing the signed big integer to be deserialized.
210
+ * @param offset - The position in the buffer where the integer starts. Defaults to 0.
211
+ * @param width - The number of bytes to read from the buffer for the integer. Defaults to 32.
212
+ * @returns An object containing the deserialized signed bigint value ('elem') and bytes advanced ('adv').
213
+ */ export function deserializeSignedBigInt(buf, offset = 0, width = 32) {
214
+ const { elem, adv } = deserializeBigInt(buf, offset, width);
215
+ const widthBits = BigInt(width * 8);
216
+ const signBit = 1n << widthBits - 1n;
217
+ const fullRange = 1n << widthBits;
218
+ const signed = elem >= signBit ? elem - fullRange : elem;
219
+ return {
220
+ elem: signed,
221
+ adv
222
+ };
223
+ }
193
224
  /**
194
225
  * Serializes a Date object into a Buffer containing its timestamp as a big integer value.
195
226
  * The resulting Buffer has a fixed width of 8 bytes, representing a 64-bit big-endian integer.
@@ -20,6 +20,7 @@
20
20
  */
21
21
  export declare class InterruptibleSleep {
22
22
  private interrupts;
23
+ private timeoutIds;
23
24
  /**
24
25
  * Sleep for a specified amount of time in milliseconds.
25
26
  * The sleep function will pause the execution of the current async function
@@ -50,4 +51,4 @@ export declare class InterruptibleSleep {
50
51
  export declare function sleep<T>(ms: number, returnValue?: T): Promise<T>;
51
52
  /** Sleeps until the target date */
52
53
  export declare function sleepUntil<T>(target: Date, now: Date, returnValue?: T): Promise<T>;
53
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zbGVlcC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILHFCQUFhLGtCQUFrQjtJQUM3QixPQUFPLENBQUMsVUFBVSxDQUE2QztJQUUvRDs7Ozs7OztPQU9HO0lBQ1UsS0FBSyxDQUFDLEVBQUUsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQWU1QztJQUVEOzs7Ozs7T0FNRztJQUNJLFNBQVMsQ0FBQyxnQkFBZ0IsVUFBUSxHQUFHLElBQUksQ0FHL0M7Q0FDRjtBQUVEOzs7Ozs7OztHQVFHO0FBQ0gsd0JBQWdCLEtBQUssQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLE1BQU0sRUFBRSxXQUFXLENBQUMsRUFBRSxDQUFDLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUVoRTtBQUVELG1DQUFtQztBQUNuQyx3QkFBZ0IsVUFBVSxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsV0FBVyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FHbEYifQ==
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zbGVlcC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQTs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW1CRztBQUNILHFCQUFhLGtCQUFrQjtJQUM3QixPQUFPLENBQUMsVUFBVSxDQUE2QztJQUMvRCxPQUFPLENBQUMsVUFBVSxDQUF3QjtJQUUxQzs7Ozs7OztPQU9HO0lBQ1UsS0FBSyxDQUFDLEVBQUUsRUFBRSxNQUFNLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQXFCNUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxTQUFTLENBQUMsZ0JBQWdCLFVBQVEsR0FBRyxJQUFJLENBSy9DO0NBQ0Y7QUFFRDs7Ozs7Ozs7R0FRRztBQUNILHdCQUFnQixLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsV0FBVyxDQUFDLEVBQUUsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FFaEU7QUFFRCxtQ0FBbUM7QUFDbkMsd0JBQWdCLFVBQVUsQ0FBQyxDQUFDLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLFdBQVcsQ0FBQyxFQUFFLENBQUMsR0FBRyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBR2xGIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sleep/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAA6C;IAE/D;;;;;;;OAOG;IACU,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5C;IAED;;;;;;OAMG;IACI,SAAS,CAAC,gBAAgB,UAAQ,GAAG,IAAI,CAG/C;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhE;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGlF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sleep/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,UAAU,CAAwB;IAE1C;;;;;;;OAOG;IACU,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB5C;IAED;;;;;;OAMG;IACI,SAAS,CAAC,gBAAgB,UAAQ,GAAG,IAAI,CAK/C;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhE;AAED,mCAAmC;AACnC,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAGlF"}
@@ -20,6 +20,7 @@ import { InterruptError } from '../error/index.js';
20
20
  * setTimeout(() =\> sleeper.interrupt(true), 1500); // Interrupt the sleep after 1.5 seconds
21
21
  */ export class InterruptibleSleep {
22
22
  interrupts = [];
23
+ timeoutIds = [];
23
24
  /**
24
25
  * Sleep for a specified amount of time in milliseconds.
25
26
  * The sleep function will pause the execution of the current async function
@@ -33,11 +34,17 @@ import { InterruptError } from '../error/index.js';
33
34
  interruptResolve = resolve;
34
35
  this.interrupts.push(resolve);
35
36
  });
36
- const timeoutPromise = new Promise((resolve)=>setTimeout(()=>resolve(false), ms));
37
+ let timeoutId;
38
+ const timeoutPromise = new Promise((resolve)=>{
39
+ timeoutId = setTimeout(()=>resolve(false), ms);
40
+ this.timeoutIds.push(timeoutId);
41
+ });
37
42
  const shouldThrow = await Promise.race([
38
43
  interruptPromise,
39
44
  timeoutPromise
40
45
  ]);
46
+ clearTimeout(timeoutId);
47
+ this.timeoutIds = this.timeoutIds.filter((id)=>id !== timeoutId);
41
48
  this.interrupts = this.interrupts.filter((res)=>res !== interruptResolve);
42
49
  if (shouldThrow) {
43
50
  throw new InterruptError('Interrupted.');
@@ -52,6 +59,8 @@ import { InterruptError } from '../error/index.js';
52
59
  */ interrupt(sleepShouldThrow = false) {
53
60
  this.interrupts.forEach((resolve)=>resolve(sleepShouldThrow));
54
61
  this.interrupts = [];
62
+ this.timeoutIds.forEach((id)=>clearTimeout(id));
63
+ this.timeoutIds = [];
55
64
  }
56
65
  }
57
66
  /**
@@ -11,5 +11,29 @@ export declare class TestDateProvider extends DateProvider {
11
11
  constructor(logger?: import("../log/pino-logger.js").Logger);
12
12
  now(): number;
13
13
  setTime(timeMs: number): void;
14
+ /** Resets the time back to real time (offset = 0). */
15
+ reset(): void;
16
+ /** Advances the time by the given number of seconds. */
17
+ advanceTime(seconds: number): void;
14
18
  }
15
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RpbWVyL2RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsZ0NBQWdDO0FBQ2hDLHFCQUFhLFlBQVk7SUFDaEIsR0FBRyxJQUFJLE1BQU0sQ0FFbkI7SUFFTSxZQUFZLElBQUksTUFBTSxDQUU1QjtJQUVNLFNBQVMsSUFBSSxJQUFJLENBRXZCO0NBQ0Y7QUFFRCwwREFBMEQ7QUFDMUQscUJBQWEsZ0JBQWlCLFNBQVEsWUFBWTtJQUdwQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFGbkMsT0FBTyxDQUFDLE1BQU0sQ0FBSztJQUVuQixZQUE2QixNQUFNLHlDQUFnRCxFQUVsRjtJQUVlLEdBQUcsSUFBSSxNQUFNLENBRTVCO0lBRU0sT0FBTyxDQUFDLE1BQU0sRUFBRSxNQUFNLFFBRzVCO0NBQ0YifQ==
19
+ /**
20
+ * A date provider for tests that only advances time via explicit advanceTime() calls.
21
+ * Unlike TestDateProvider, this does NOT track real time progression - time is completely
22
+ * frozen until explicitly advanced. This eliminates flakiness from tests taking
23
+ * varying amounts of real time to execute.
24
+ */
25
+ export declare class ManualDateProvider extends DateProvider {
26
+ private currentTimeMs;
27
+ /**
28
+ * @param initialTimeMs - Initial time in milliseconds. Defaults to a round timestamp for easy visualization.
29
+ */
30
+ constructor(initialTimeMs?: number);
31
+ now(): number;
32
+ /** Sets the current time to the given timestamp in milliseconds. */
33
+ setTime(timeMs: number): void;
34
+ /** Advances the time by the given number of seconds. */
35
+ advanceTime(seconds: number): void;
36
+ /** Advances the time by the given number of milliseconds. */
37
+ advanceTimeMs(ms: number): void;
38
+ }
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0ZS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RpbWVyL2RhdGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsZ0NBQWdDO0FBQ2hDLHFCQUFhLFlBQVk7SUFDaEIsR0FBRyxJQUFJLE1BQU0sQ0FFbkI7SUFFTSxZQUFZLElBQUksTUFBTSxDQUU1QjtJQUVNLFNBQVMsSUFBSSxJQUFJLENBRXZCO0NBQ0Y7QUFFRCwwREFBMEQ7QUFDMUQscUJBQWEsZ0JBQWlCLFNBQVEsWUFBWTtJQUdwQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFGbkMsT0FBTyxDQUFDLE1BQU0sQ0FBSztJQUVuQixZQUE2QixNQUFNLHlDQUFnRCxFQUVsRjtJQUVlLEdBQUcsSUFBSSxNQUFNLENBRTVCO0lBRU0sT0FBTyxDQUFDLE1BQU0sRUFBRSxNQUFNLFFBRzVCO0lBRUQsc0RBQXNEO0lBQy9DLEtBQUssU0FHWDtJQUVELHdEQUF3RDtJQUNqRCxXQUFXLENBQUMsT0FBTyxFQUFFLE1BQU0sUUFFakM7Q0FDRjtBQUVEOzs7OztHQUtHO0FBQ0gscUJBQWEsa0JBQW1CLFNBQVEsWUFBWTtJQUNsRCxPQUFPLENBQUMsYUFBYSxDQUFTO0lBRTlCOztPQUVHO0lBQ0gsWUFBWSxhQUFhLEdBQUUsTUFBc0MsRUFHaEU7SUFFZSxHQUFHLElBQUksTUFBTSxDQUU1QjtJQUVELG9FQUFvRTtJQUM3RCxPQUFPLENBQUMsTUFBTSxFQUFFLE1BQU0sUUFFNUI7SUFFRCx3REFBd0Q7SUFDakQsV0FBVyxDQUFDLE9BQU8sRUFBRSxNQUFNLFFBRWpDO0lBRUQsNkRBQTZEO0lBQ3RELGFBQWEsQ0FBQyxFQUFFLEVBQUUsTUFBTSxRQUU5QjtDQUNGIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/timer/date.ts"],"names":[],"mappings":"AAEA,gCAAgC;AAChC,qBAAa,YAAY;IAChB,GAAG,IAAI,MAAM,CAEnB;IAEM,YAAY,IAAI,MAAM,CAE5B;IAEM,SAAS,IAAI,IAAI,CAEvB;CACF;AAED,0DAA0D;AAC1D,qBAAa,gBAAiB,SAAQ,YAAY;IAGpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,MAAM,CAAK;IAEnB,YAA6B,MAAM,yCAAgD,EAElF;IAEe,GAAG,IAAI,MAAM,CAE5B;IAEM,OAAO,CAAC,MAAM,EAAE,MAAM,QAG5B;CACF"}
1
+ {"version":3,"file":"date.d.ts","sourceRoot":"","sources":["../../src/timer/date.ts"],"names":[],"mappings":"AAEA,gCAAgC;AAChC,qBAAa,YAAY;IAChB,GAAG,IAAI,MAAM,CAEnB;IAEM,YAAY,IAAI,MAAM,CAE5B;IAEM,SAAS,IAAI,IAAI,CAEvB;CACF;AAED,0DAA0D;AAC1D,qBAAa,gBAAiB,SAAQ,YAAY;IAGpC,OAAO,CAAC,QAAQ,CAAC,MAAM;IAFnC,OAAO,CAAC,MAAM,CAAK;IAEnB,YAA6B,MAAM,yCAAgD,EAElF;IAEe,GAAG,IAAI,MAAM,CAE5B;IAEM,OAAO,CAAC,MAAM,EAAE,MAAM,QAG5B;IAED,sDAAsD;IAC/C,KAAK,SAGX;IAED,wDAAwD;IACjD,WAAW,CAAC,OAAO,EAAE,MAAM,QAEjC;CACF;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;IACH,YAAY,aAAa,GAAE,MAAsC,EAGhE;IAEe,GAAG,IAAI,MAAM,CAE5B;IAED,oEAAoE;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,QAE5B;IAED,wDAAwD;IACjD,WAAW,CAAC,OAAO,EAAE,MAAM,QAEjC;IAED,6DAA6D;IACtD,aAAa,CAAC,EAAE,EAAE,MAAM,QAE9B;CACF"}
@@ -26,4 +26,37 @@ import { createLogger } from '../log/pino-logger.js';
26
26
  timeMs
27
27
  });
28
28
  }
29
+ /** Resets the time back to real time (offset = 0). */ reset() {
30
+ this.offset = 0;
31
+ this.logger.warn('Time reset to real time');
32
+ }
33
+ /** Advances the time by the given number of seconds. */ advanceTime(seconds) {
34
+ this.offset += seconds * 1000;
35
+ }
36
+ }
37
+ /**
38
+ * A date provider for tests that only advances time via explicit advanceTime() calls.
39
+ * Unlike TestDateProvider, this does NOT track real time progression - time is completely
40
+ * frozen until explicitly advanced. This eliminates flakiness from tests taking
41
+ * varying amounts of real time to execute.
42
+ */ export class ManualDateProvider extends DateProvider {
43
+ currentTimeMs;
44
+ /**
45
+ * @param initialTimeMs - Initial time in milliseconds. Defaults to a round timestamp for easy visualization.
46
+ */ constructor(initialTimeMs = Date.UTC(2025, 0, 1, 0, 0, 0)){
47
+ super();
48
+ this.currentTimeMs = initialTimeMs;
49
+ }
50
+ now() {
51
+ return this.currentTimeMs;
52
+ }
53
+ /** Sets the current time to the given timestamp in milliseconds. */ setTime(timeMs) {
54
+ this.currentTimeMs = timeMs;
55
+ }
56
+ /** Advances the time by the given number of seconds. */ advanceTime(seconds) {
57
+ this.currentTimeMs += seconds * 1000;
58
+ }
59
+ /** Advances the time by the given number of milliseconds. */ advanceTimeMs(ms) {
60
+ this.currentTimeMs += ms;
61
+ }
29
62
  }
@@ -55,7 +55,7 @@ const log = createLogger('foundation:transport_client');
55
55
  msgId,
56
56
  payload
57
57
  };
58
- log.debug(format(`->`, msg));
58
+ log.trace(format(`->`, msg));
59
59
  return new Promise((resolve, reject)=>{
60
60
  this.pendingRequests.push({
61
61
  resolve,
@@ -77,7 +77,7 @@ const log = createLogger('foundation:transport_client');
77
77
  this.close();
78
78
  return;
79
79
  }
80
- log.debug(format(`<-`, msg));
80
+ log.trace(format(`<-`, msg));
81
81
  if (isEventMessage(msg)) {
82
82
  this.emit('event_msg', msg.payload);
83
83
  return;
package/package.json CHANGED
@@ -1,9 +1,29 @@
1
1
  {
2
2
  "name": "@aztec/foundation",
3
- "version": "0.0.1-commit.0208eb9",
3
+ "version": "0.0.1-commit.033589e",
4
4
  "type": "module",
5
5
  "main": "./dest/index.js",
6
6
  "types": "./dest/index.d.ts",
7
+ "typedocOptions": {
8
+ "entryPoints": [
9
+ "./src/buffer/index.ts",
10
+ "./src/collection/index.ts",
11
+ "./src/config/index.ts",
12
+ "./src/crypto/random/index.ts",
13
+ "./src/error/index.ts",
14
+ "./src/eth-address/index.ts",
15
+ "./src/json-rpc/index.ts",
16
+ "./src/log/index.ts",
17
+ "./src/retry/index.ts",
18
+ "./src/schemas/index.ts",
19
+ "./src/serialize/index.ts",
20
+ "./src/sleep/index.ts",
21
+ "./src/timer/index.ts",
22
+ "./src/types/index.ts"
23
+ ],
24
+ "name": "Foundation",
25
+ "tsconfig": "./tsconfig.json"
26
+ },
7
27
  "exports": {
8
28
  "./eslint": "./eslint.config.js",
9
29
  "./eslint.docs": "./eslint.config.docs.js",
@@ -124,7 +144,7 @@
124
144
  "testEnvironment": "../../foundation/src/jest/env.mjs"
125
145
  },
126
146
  "dependencies": {
127
- "@aztec/bb.js": "0.0.1-commit.0208eb9",
147
+ "@aztec/bb.js": "0.0.1-commit.033589e",
128
148
  "@koa/cors": "^5.0.0",
129
149
  "@noble/curves": "=1.7.0",
130
150
  "@noble/hashes": "^1.6.1",
@@ -21,34 +21,39 @@ export function dedupeSortedArray<T>(arr: T[], cmp: Cmp<T>): void {
21
21
  }
22
22
 
23
23
  export function insertIntoSortedArray<T>(arr: T[], item: T, cmp: Cmp<T>, allowDuplicates = true): boolean {
24
+ const index = findInsertionIndexInSortedArray(arr, item, cmp);
25
+
26
+ if (!allowDuplicates) {
27
+ // Check element before insertion point (upper bound returns index after equal elements)
28
+ if (index > 0 && cmp(arr[index - 1], item) === 0) {
29
+ return false;
30
+ }
31
+ }
32
+
33
+ arr.splice(index, 0, item);
34
+ return true;
35
+ }
36
+
37
+ /**
38
+ * Finds the index where needle would be inserted to maintain sorted order.
39
+ * Returns the count of elements less than or equal to needle.
40
+ */
41
+ export function findInsertionIndexInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): number {
24
42
  let start = 0;
25
- let end = arr.length;
43
+ let end = values.length;
26
44
 
27
45
  while (start < end) {
28
46
  const mid = start + (((end - start) / 2) | 0);
29
- const comparison = cmp(arr[mid], item);
47
+ const comparison = cmp(values[mid], needle);
30
48
 
31
- if (comparison < 0) {
49
+ if (comparison <= 0) {
32
50
  start = mid + 1;
33
51
  } else {
34
52
  end = mid;
35
53
  }
36
54
  }
37
55
 
38
- if (!allowDuplicates) {
39
- // Check element at insertion point
40
- if (start < arr.length && cmp(arr[start], item) === 0) {
41
- return false;
42
- }
43
-
44
- // Check element before insertion point (in case we landed after duplicates)
45
- if (start > 0 && cmp(arr[start - 1], item) === 0) {
46
- return false;
47
- }
48
- }
49
-
50
- arr.splice(start, 0, item);
51
- return true;
56
+ return start;
52
57
  }
53
58
 
54
59
  export function findIndexInSortedArray<T, N>(values: T[], needle: N, cmp: (a: T, b: N) => number): number {
@@ -12,6 +12,9 @@ export type EnvVar =
12
12
  | 'ARCHIVER_VIEM_POLLING_INTERVAL_MS'
13
13
  | 'ARCHIVER_BATCH_SIZE'
14
14
  | 'AZTEC_ADMIN_PORT'
15
+ | 'AZTEC_ADMIN_API_KEY_HASH'
16
+ | 'AZTEC_DISABLE_ADMIN_API_KEY'
17
+ | 'AZTEC_RESET_ADMIN_API_KEY'
15
18
  | 'AZTEC_NODE_ADMIN_URL'
16
19
  | 'AZTEC_NODE_URL'
17
20
  | 'AZTEC_PORT'
@@ -47,7 +50,10 @@ export type EnvVar =
47
50
  | 'BOT_TX_MINED_WAIT_SECONDS'
48
51
  | 'BOT_MAX_CONSECUTIVE_ERRORS'
49
52
  | 'BOT_STOP_WHEN_UNHEALTHY'
50
- | 'BOT_AMM_TXS'
53
+ | 'BOT_MODE'
54
+ | 'BOT_L2_TO_L1_MESSAGES_PER_TX'
55
+ | 'BOT_L1_TO_L2_SEED_COUNT'
56
+ | 'BOT_L1_TO_L2_SEED_INTERVAL'
51
57
  | 'COINBASE'
52
58
  | 'CRS_PATH'
53
59
  | 'DATA_DIRECTORY'
@@ -64,6 +70,7 @@ export type EnvVar =
64
70
  | 'PUBLIC_DATA_TREE_MAP_SIZE_KB'
65
71
  | 'DEBUG'
66
72
  | 'DEBUG_P2P_DISABLE_COLOCATION_PENALTY'
73
+ | 'ENABLE_PROVER_NODE'
67
74
  | 'ETHEREUM_HOSTS'
68
75
  | 'ETHEREUM_DEBUG_HOSTS'
69
76
  | 'ETHEREUM_ALLOW_NO_DEBUG_HOSTS'
@@ -75,6 +82,7 @@ export type EnvVar =
75
82
  | 'L1_CONSENSUS_HOST_URLS'
76
83
  | 'L1_CONSENSUS_HOST_API_KEYS'
77
84
  | 'L1_CONSENSUS_HOST_API_KEY_HEADERS'
85
+ | 'L1_TX_FAILED_STORE'
78
86
  | 'LOG_JSON'
79
87
  | 'LOG_MULTILINE'
80
88
  | 'LOG_NO_COLOR_PER_ACTOR'
@@ -100,6 +108,7 @@ export type EnvVar =
100
108
  | 'P2P_BATCH_TX_REQUESTER_TX_BATCH_SIZE'
101
109
  | 'P2P_BATCH_TX_REQUESTER_BAD_PEER_THRESHOLD'
102
110
  | 'P2P_BLOCK_CHECK_INTERVAL_MS'
111
+ | 'P2P_SLOT_CHECK_INTERVAL_MS'
103
112
  | 'P2P_BLOCK_REQUEST_BATCH_SIZE'
104
113
  | 'P2P_BOOTSTRAP_NODE_ENR_VERSION_CHECK'
105
114
  | 'P2P_BOOTSTRAP_NODES_AS_FULL_PEERS'
@@ -140,9 +149,9 @@ export type EnvVar =
140
149
  | 'P2P_PREFERRED_PEERS'
141
150
  | 'P2P_MAX_PENDING_TX_COUNT'
142
151
  | 'P2P_SEEN_MSG_CACHE_SIZE'
143
- | 'P2P_DROP_TX'
144
152
  | 'P2P_DROP_TX_CHANCE'
145
153
  | 'P2P_TX_POOL_DELETE_TXS_AFTER_REORG'
154
+ | 'P2P_MIN_TX_POOL_AGE_MS'
146
155
  | 'DEBUG_P2P_INSTRUMENT_MESSAGES'
147
156
  | 'PEER_ID_PRIVATE_KEY'
148
157
  | 'PEER_ID_PRIVATE_KEY_PATH'
@@ -207,9 +216,11 @@ export type EnvVar =
207
216
  | 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT'
208
217
  | 'SEQ_ATTESTATION_PROPAGATION_TIME'
209
218
  | 'SEQ_BLOCK_DURATION_MS'
219
+ | 'SEQ_EXPECTED_BLOCK_PROPOSALS_PER_SLOT'
210
220
  | 'SEQ_BUILD_CHECKPOINT_IF_EMPTY'
211
221
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_COMMITTEE_MEMBER'
212
222
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_NON_COMMITTEE_MEMBER'
223
+ | 'SEQ_SKIP_CHECKPOINT_PUBLISH_PERCENT'
213
224
  | 'SLASH_MIN_PENALTY_PERCENTAGE'
214
225
  | 'SLASH_MAX_PENALTY_PERCENTAGE'
215
226
  | 'SLASH_VALIDATORS_ALWAYS'
@@ -220,6 +231,8 @@ export type EnvVar =
220
231
  | 'SLASH_INACTIVITY_TARGET_PERCENTAGE'
221
232
  | 'SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD'
222
233
  | 'SLASH_INVALID_BLOCK_PENALTY'
234
+ | 'SLASH_DUPLICATE_PROPOSAL_PENALTY'
235
+ | 'SLASH_DUPLICATE_ATTESTATION_PENALTY'
223
236
  | 'SLASH_OVERRIDE_PAYLOAD'
224
237
  | 'SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY'
225
238
  | 'SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY'
@@ -234,6 +247,7 @@ export type EnvVar =
234
247
  | 'TELEMETRY'
235
248
  | 'TEST_ACCOUNTS'
236
249
  | 'SPONSORED_FPC'
250
+ | 'PREFUND_ADDRESSES'
237
251
  | 'TX_COLLECTION_FAST_NODES_TIMEOUT_BEFORE_REQ_RESP_MS'
238
252
  | 'TX_COLLECTION_SLOW_NODES_INTERVAL_MS'
239
253
  | 'TX_COLLECTION_SLOW_REQ_RESP_INTERVAL_MS'
@@ -244,9 +258,17 @@ export type EnvVar =
244
258
  | 'TX_COLLECTION_FAST_MAX_PARALLEL_REQUESTS_PER_NODE'
245
259
  | 'TX_COLLECTION_NODE_RPC_MAX_BATCH_SIZE'
246
260
  | 'TX_COLLECTION_NODE_RPC_URLS'
247
- | 'TX_COLLECTION_PROPOSAL_TX_COLLECTOR_TYPE'
261
+ | 'TX_COLLECTION_MISSING_TXS_COLLECTOR_TYPE'
262
+ | 'TX_COLLECTION_FILE_STORE_URLS'
263
+ | 'TX_COLLECTION_FILE_STORE_SLOW_DELAY_MS'
264
+ | 'TX_COLLECTION_FILE_STORE_FAST_DELAY_MS'
265
+ | 'TX_COLLECTION_FILE_STORE_FAST_WORKER_COUNT'
266
+ | 'TX_COLLECTION_FILE_STORE_SLOW_WORKER_COUNT'
267
+ | 'TX_COLLECTION_FILE_STORE_FAST_BACKOFF_BASE_MS'
268
+ | 'TX_COLLECTION_FILE_STORE_SLOW_BACKOFF_BASE_MS'
269
+ | 'TX_COLLECTION_FILE_STORE_FAST_BACKOFF_MAX_MS'
270
+ | 'TX_COLLECTION_FILE_STORE_SLOW_BACKOFF_MAX_MS'
248
271
  | 'TX_FILE_STORE_URL'
249
- | 'TX_FILE_STORE_DOWNLOAD_URL'
250
272
  | 'TX_FILE_STORE_UPLOAD_CONCURRENCY'
251
273
  | 'TX_FILE_STORE_MAX_QUEUE_SIZE'
252
274
  | 'TX_FILE_STORE_ENABLED'
@@ -264,6 +286,7 @@ export type EnvVar =
264
286
  | 'WS_BLOCK_REQUEST_BATCH_SIZE'
265
287
  | 'L1_READER_VIEM_POLLING_INTERVAL_MS'
266
288
  | 'WS_DATA_DIRECTORY'
289
+ | 'WS_NUM_HISTORIC_CHECKPOINTS'
267
290
  | 'WS_NUM_HISTORIC_BLOCKS'
268
291
  | 'ETHEREUM_SLOT_DURATION'
269
292
  | 'AZTEC_SLOT_DURATION'
@@ -177,6 +177,21 @@ export function bigintConfigHelper(defaultVal?: bigint): Pick<ConfigMapping, 'pa
177
177
  if (val === '') {
178
178
  return defaultVal;
179
179
  }
180
+ // Handle scientific notation (e.g. "1e+23", "2E23") which BigInt() doesn't accept directly.
181
+ // We parse it losslessly using bigint arithmetic instead of going through float64.
182
+ if (/[eE]/.test(val)) {
183
+ const match = val.match(/^(-?\d+(?:\.(\d+))?)[eE]([+-]?\d+)$/);
184
+ if (!match) {
185
+ throw new Error(`Cannot convert '${val}' to a BigInt`);
186
+ }
187
+ const digits = match[1].replace('.', '');
188
+ const decimalPlaces = match[2]?.length ?? 0;
189
+ const exponent = parseInt(match[3], 10) - decimalPlaces;
190
+ if (exponent < 0) {
191
+ throw new Error(`Cannot convert '${val}' to a BigInt: result is not an integer`);
192
+ }
193
+ return BigInt(digits) * 10n ** BigInt(exponent);
194
+ }
180
195
  return BigInt(val);
181
196
  },
182
197
  defaultValue: defaultVal,
@@ -9,6 +9,7 @@ export const NetworkConfigSchema = z
9
9
  feeAssetHandlerAddress: z.string().optional(),
10
10
  l1ChainId: z.number(),
11
11
  blockDurationMs: z.number().positive().optional(),
12
+ txPublicSetupAllowListExtend: z.string().optional(),
12
13
  })
13
14
  .passthrough(); // Allow additional unknown fields to pass through
14
15
 
@@ -5,7 +5,8 @@ export type NetworkNames =
5
5
  | 'testnet'
6
6
  | 'mainnet'
7
7
  | 'next-net'
8
- | 'devnet';
8
+ | 'devnet'
9
+ | `v${number}-devnet-${number}`;
9
10
 
10
11
  export function getActiveNetworkName(name?: string): NetworkNames {
11
12
  const network = name || process.env.NETWORK;
@@ -23,6 +24,8 @@ export function getActiveNetworkName(name?: string): NetworkNames {
23
24
  return 'next-net';
24
25
  } else if (network === 'devnet') {
25
26
  return 'devnet';
27
+ } else if (/^v\d+-devnet-\d+$/.test(network)) {
28
+ return network as `v${number}-devnet-${number}`;
26
29
  }
27
30
  throw new Error(`Unknown network: ${network}`);
28
31
  }
@@ -1,4 +1,4 @@
1
- import { BarretenbergSync } from '@aztec/bb.js';
1
+ import { Barretenberg } from '@aztec/bb.js';
2
2
 
3
3
  import { Fr } from '../../curves/bn254/field.js';
4
4
  import { type Fieldable, serializeToFields } from '../../serialize/serialize.js';
@@ -10,9 +10,9 @@ import { type Fieldable, serializeToFields } from '../../serialize/serialize.js'
10
10
  */
11
11
  export async function poseidon2Hash(input: Fieldable[]): Promise<Fr> {
12
12
  const inputFields = serializeToFields(input);
13
- await BarretenbergSync.initSingleton();
14
- const api = BarretenbergSync.getSingleton();
15
- const response = api.poseidon2Hash({
13
+ await Barretenberg.initSingleton();
14
+ const api = Barretenberg.getSingleton();
15
+ const response = await api.poseidon2Hash({
16
16
  inputs: inputFields.map(i => i.toBuffer()),
17
17
  });
18
18
  return Fr.fromBuffer(Buffer.from(response.hash));
@@ -27,9 +27,9 @@ export async function poseidon2Hash(input: Fieldable[]): Promise<Fr> {
27
27
  export async function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Promise<Fr> {
28
28
  const inputFields = serializeToFields(input);
29
29
  inputFields.unshift(new Fr(separator));
30
- await BarretenbergSync.initSingleton();
31
- const api = BarretenbergSync.getSingleton();
32
- const response = api.poseidon2Hash({
30
+ await Barretenberg.initSingleton();
31
+ const api = Barretenberg.getSingleton();
32
+ const response = await api.poseidon2Hash({
33
33
  inputs: inputFields.map(i => i.toBuffer()),
34
34
  });
35
35
  return Fr.fromBuffer(Buffer.from(response.hash));
@@ -44,9 +44,9 @@ export async function poseidon2Permutation(input: Fieldable[]): Promise<Fr[]> {
44
44
  const inputFields = serializeToFields(input);
45
45
  // We'd like this assertion but it's not possible to use it in the browser.
46
46
  // assert(input.length === 4, 'Input state must be of size 4');
47
- await BarretenbergSync.initSingleton();
48
- const api = BarretenbergSync.getSingleton();
49
- const response = api.poseidon2Permutation({
47
+ await Barretenberg.initSingleton();
48
+ const api = Barretenberg.getSingleton();
49
+ const response = await api.poseidon2Permutation({
50
50
  inputs: inputFields.map(i => i.toBuffer()),
51
51
  });
52
52
  // We'd like this assertion but it's not possible to use it in the browser.
@@ -65,9 +65,9 @@ export async function poseidon2HashBytes(input: Buffer): Promise<Fr> {
65
65
  inputFields.push(Fr.fromBuffer(fieldBytes));
66
66
  }
67
67
 
68
- await BarretenbergSync.initSingleton();
69
- const api = BarretenbergSync.getSingleton();
70
- const response = api.poseidon2Hash({
68
+ await Barretenberg.initSingleton();
69
+ const api = Barretenberg.getSingleton();
70
+ const response = await api.poseidon2Hash({
71
71
  inputs: inputFields.map(i => i.toBuffer()),
72
72
  });
73
73
 
@@ -210,3 +210,35 @@ export function recoverPublicKey(hash: Buffer32, signature: Signature, opts: Rec
210
210
  const publicKey = sig.recoverPublicKey(hash.buffer).toHex(false);
211
211
  return Buffer.from(publicKey, 'hex');
212
212
  }
213
+
214
+ /** Arbitrary hash used for testing signature recoverability. */
215
+ const PROBE_HASH = Buffer32.fromBuffer(keccak256(Buffer.from('signature-recoverability-probe')));
216
+
217
+ /**
218
+ * Generates a random valid ECDSA signature that is recoverable to some address.
219
+ * Since Signature.random() produces real signatures via secp256k1 signing, the result is always
220
+ * recoverable, but we verify defensively by checking tryRecoverAddress.
221
+ */
222
+ export function generateRecoverableSignature(): Signature {
223
+ for (let i = 0; i < 100; i++) {
224
+ const sig = Signature.random();
225
+ if (tryRecoverAddress(PROBE_HASH, sig) !== undefined) {
226
+ return sig;
227
+ }
228
+ }
229
+ throw new Secp256k1Error('Failed to generate a recoverable signature after 100 attempts');
230
+ }
231
+
232
+ /**
233
+ * Generates a random signature where ECDSA address recovery fails.
234
+ * Uses random r/s values (not from real signing) so that r is unlikely to be a valid secp256k1 x-coordinate.
235
+ */
236
+ export function generateUnrecoverableSignature(): Signature {
237
+ for (let i = 0; i < 100; i++) {
238
+ const sig = new Signature(Buffer32.random(), Buffer32.random(), 27);
239
+ if (tryRecoverAddress(PROBE_HASH, sig) === undefined) {
240
+ return sig;
241
+ }
242
+ }
243
+ throw new Secp256k1Error('Failed to generate an unrecoverable signature after 100 attempts');
244
+ }
@@ -118,14 +118,18 @@ abstract class BaseField {
118
118
  }
119
119
 
120
120
  cmp(rhs: BaseField): -1 | 0 | 1 {
121
- const rhsBigInt = rhs.asBigInt;
122
- return this.asBigInt === rhsBigInt ? 0 : this.asBigInt < rhsBigInt ? -1 : 1;
121
+ return BaseField.cmpAsBigInt(this.asBigInt, rhs.asBigInt);
123
122
  }
124
123
 
125
124
  static cmp(lhs: BaseField, rhs: BaseField): -1 | 0 | 1 {
126
125
  return lhs.cmp(rhs);
127
126
  }
128
127
 
128
+ // Actual bigint comparison. Arguments must have been validated previously.
129
+ static cmpAsBigInt(lhs: bigint, rhs: bigint): -1 | 0 | 1 {
130
+ return lhs === rhs ? 0 : lhs < rhs ? -1 : 1;
131
+ }
132
+
129
133
  isZero(): boolean {
130
134
  return this.asBigInt === 0n;
131
135
  }
@@ -1,8 +1,10 @@
1
1
  import { Buffer32 } from '@aztec/foundation/buffer';
2
2
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
3
 
4
+ import { secp256k1 } from '@noble/curves/secp256k1';
4
5
  import { z } from 'zod';
5
6
 
7
+ import { randomBytes } from '../crypto/random/index.js';
6
8
  import { hasHexPrefix, hexToBuffer } from '../string/index.js';
7
9
 
8
10
  /**
@@ -77,8 +79,12 @@ export class Signature {
77
79
  return new Signature(Buffer32.fromBuffer(hexToBuffer(sig.r)), Buffer32.fromBuffer(hexToBuffer(sig.s)), sig.yParity);
78
80
  }
79
81
 
82
+ /** Generates a random valid ECDSA signature with a low s-value by signing a random message with a random key. */
80
83
  static random(): Signature {
81
- return new Signature(Buffer32.random(), Buffer32.random(), 1);
84
+ const privateKey = randomBytes(32);
85
+ const message = randomBytes(32);
86
+ const { r, s, recovery } = secp256k1.sign(message, privateKey);
87
+ return new Signature(Buffer32.fromBigInt(r), Buffer32.fromBigInt(s), recovery ? 28 : 27);
82
88
  }
83
89
 
84
90
  static empty(): Signature {
@@ -10,3 +10,30 @@ import pretty from 'pino-pretty';
10
10
  if (!parseBooleanEnv(process.env.LOG_JSON)) {
11
11
  overwriteLoggingStream(pretty(pinoPrettyOpts));
12
12
  }
13
+
14
+ // Prevent timers from keeping the process alive after tests complete.
15
+ // Libraries like viem create internal polling loops (via setTimeout) that
16
+ // reschedule themselves indefinitely. In test environments we never want a
17
+ // timer to be the reason the process can't exit. We also unref stdout/stderr
18
+ // which, when they are pipes (as in Jest workers), remain ref'd by default.
19
+ {
20
+ const origSetTimeout = globalThis.setTimeout;
21
+ const origSetInterval = globalThis.setInterval;
22
+ globalThis.setTimeout = function unrefSetTimeout(...args) {
23
+ const id = origSetTimeout.apply(this, args);
24
+ id?.unref?.();
25
+ return id;
26
+ };
27
+ // Preserve .unref, .__promisify__ etc. that may exist on the original
28
+ Object.setPrototypeOf(globalThis.setTimeout, origSetTimeout);
29
+
30
+ globalThis.setInterval = function unrefSetInterval(...args) {
31
+ const id = origSetInterval.apply(this, args);
32
+ id?.unref?.();
33
+ return id;
34
+ };
35
+ Object.setPrototypeOf(globalThis.setInterval, origSetInterval);
36
+
37
+ if (process.stdout?._handle?.unref) process.stdout._handle.unref();
38
+ if (process.stderr?._handle?.unref) process.stderr._handle.unref();
39
+ }
@@ -24,6 +24,7 @@ export type SafeJsonRpcClientOptions = {
24
24
  batchWindowMS?: number;
25
25
  maxBatchSize?: number;
26
26
  maxRequestBodySize?: number;
27
+ extraHeaders?: Record<string, string>;
27
28
  onResponse?: (res: {
28
29
  response: any;
29
30
  headers: { get: (header: string) => string | null | undefined };
@@ -129,6 +130,7 @@ export function createSafeJsonRpcClient<T extends object>(
129
130
  const { response, headers } = await fetch(
130
131
  host,
131
132
  rpcCalls.map(({ request }) => request),
133
+ config.extraHeaders,
132
134
  );
133
135
 
134
136
  if (config.onResponse) {