@aztec/foundation 0.0.1-commit.a072138 → 0.0.1-commit.a89ec08

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 (105) 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/branded-types/slot.d.ts +4 -1
  5. package/dest/branded-types/slot.d.ts.map +1 -1
  6. package/dest/branded-types/slot.js +3 -0
  7. package/dest/buffer/index.d.ts +2 -1
  8. package/dest/buffer/index.d.ts.map +1 -1
  9. package/dest/buffer/index.js +1 -0
  10. package/dest/buffer/utils.d.ts +3 -0
  11. package/dest/buffer/utils.d.ts.map +1 -0
  12. package/dest/buffer/utils.js +7 -0
  13. package/dest/collection/array.d.ts +3 -1
  14. package/dest/collection/array.d.ts.map +1 -1
  15. package/dest/collection/array.js +15 -0
  16. package/dest/config/env_var.d.ts +2 -2
  17. package/dest/config/env_var.d.ts.map +1 -1
  18. package/dest/config/index.d.ts +1 -1
  19. package/dest/config/index.d.ts.map +1 -1
  20. package/dest/config/index.js +15 -0
  21. package/dest/config/network_config.d.ts +19 -1
  22. package/dest/config/network_config.d.ts.map +1 -1
  23. package/dest/config/network_config.js +4 -1
  24. package/dest/crypto/poseidon/index.d.ts +1 -1
  25. package/dest/crypto/poseidon/index.d.ts.map +1 -1
  26. package/dest/crypto/poseidon/index.js +40 -33
  27. package/dest/crypto/secp256k1-signer/utils.d.ts +12 -1
  28. package/dest/crypto/secp256k1-signer/utils.d.ts.map +1 -1
  29. package/dest/crypto/secp256k1-signer/utils.js +26 -0
  30. package/dest/curves/bn254/field.d.ts +2 -1
  31. package/dest/curves/bn254/field.d.ts.map +1 -1
  32. package/dest/curves/bn254/field.js +5 -2
  33. package/dest/curves/grumpkin/point.d.ts +4 -4
  34. package/dest/curves/grumpkin/point.d.ts.map +1 -1
  35. package/dest/curves/grumpkin/point.js +7 -4
  36. package/dest/eth-signature/eth_signature.d.ts +2 -1
  37. package/dest/eth-signature/eth_signature.d.ts.map +1 -1
  38. package/dest/eth-signature/eth_signature.js +7 -2
  39. package/dest/json-rpc/client/safe_json_rpc_client.d.ts +2 -1
  40. package/dest/json-rpc/client/safe_json_rpc_client.d.ts.map +1 -1
  41. package/dest/json-rpc/client/safe_json_rpc_client.js +1 -1
  42. package/dest/json-rpc/server/api_key_auth.d.ts +19 -0
  43. package/dest/json-rpc/server/api_key_auth.d.ts.map +1 -0
  44. package/dest/json-rpc/server/api_key_auth.js +57 -0
  45. package/dest/json-rpc/server/index.d.ts +2 -1
  46. package/dest/json-rpc/server/index.d.ts.map +1 -1
  47. package/dest/json-rpc/server/index.js +1 -0
  48. package/dest/log/bigint-utils.d.ts +5 -0
  49. package/dest/log/bigint-utils.d.ts.map +1 -0
  50. package/dest/log/bigint-utils.js +21 -0
  51. package/dest/log/gcloud-logger-config.d.ts +1 -1
  52. package/dest/log/gcloud-logger-config.d.ts.map +1 -1
  53. package/dest/log/gcloud-logger-config.js +3 -0
  54. package/dest/log/log-filters.d.ts +17 -4
  55. package/dest/log/log-filters.d.ts.map +1 -1
  56. package/dest/log/log-filters.js +26 -12
  57. package/dest/log/pino-logger.d.ts +1 -1
  58. package/dest/log/pino-logger.d.ts.map +1 -1
  59. package/dest/log/pino-logger.js +6 -2
  60. package/dest/queue/base_memory_queue.d.ts +2 -2
  61. package/dest/queue/base_memory_queue.d.ts.map +1 -1
  62. package/dest/schemas/api.d.ts +2 -2
  63. package/dest/schemas/api.d.ts.map +1 -1
  64. package/dest/serialize/buffer_reader.d.ts +27 -7
  65. package/dest/serialize/buffer_reader.d.ts.map +1 -1
  66. package/dest/serialize/buffer_reader.js +31 -5
  67. package/dest/serialize/field_reader.d.ts +13 -5
  68. package/dest/serialize/field_reader.d.ts.map +1 -1
  69. package/dest/serialize/field_reader.js +13 -0
  70. package/dest/serialize/serialize.d.ts +19 -1
  71. package/dest/serialize/serialize.d.ts.map +1 -1
  72. package/dest/serialize/serialize.js +31 -0
  73. package/dest/timer/date.d.ts +23 -1
  74. package/dest/timer/date.d.ts.map +1 -1
  75. package/dest/timer/date.js +29 -0
  76. package/dest/transport/transport_client.js +2 -2
  77. package/package.json +22 -2
  78. package/src/array/sorted_array.ts +22 -17
  79. package/src/branded-types/slot.ts +5 -0
  80. package/src/buffer/index.ts +1 -0
  81. package/src/buffer/utils.ts +8 -0
  82. package/src/collection/array.ts +14 -0
  83. package/src/config/env_var.ts +45 -6
  84. package/src/config/index.ts +15 -0
  85. package/src/config/network_config.ts +3 -0
  86. package/src/crypto/poseidon/index.ts +42 -34
  87. package/src/crypto/secp256k1-signer/utils.ts +32 -0
  88. package/src/curves/bn254/field.ts +6 -2
  89. package/src/curves/grumpkin/point.ts +6 -3
  90. package/src/eth-signature/eth_signature.ts +7 -1
  91. package/src/json-rpc/client/safe_json_rpc_client.ts +2 -0
  92. package/src/json-rpc/server/api_key_auth.ts +63 -0
  93. package/src/json-rpc/server/index.ts +1 -0
  94. package/src/log/bigint-utils.ts +25 -0
  95. package/src/log/gcloud-logger-config.ts +5 -0
  96. package/src/log/log-filters.ts +29 -11
  97. package/src/log/pino-logger.ts +6 -2
  98. package/src/queue/base_memory_queue.ts +1 -1
  99. package/src/schemas/api.ts +4 -1
  100. package/src/serialize/buffer_reader.ts +44 -6
  101. package/src/serialize/field_reader.ts +22 -4
  102. package/src/serialize/serialize.ts +32 -0
  103. package/src/timer/date.ts +42 -0
  104. package/src/transport/transport_client.ts +2 -2
  105. package/src/trees/membership_witness.ts +2 -2
package/package.json CHANGED
@@ -1,9 +1,29 @@
1
1
  {
2
2
  "name": "@aztec/foundation",
3
- "version": "0.0.1-commit.a072138",
3
+ "version": "0.0.1-commit.a89ec08",
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.a072138",
147
+ "@aztec/bb.js": "0.0.1-commit.a89ec08",
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 {
@@ -73,6 +73,11 @@ SlotNumber.isValid = function (value: unknown): value is SlotNumber {
73
73
  return typeof value === 'number' && Number.isInteger(value) && value >= 0;
74
74
  };
75
75
 
76
+ /** Increments a SlotNumber by a given value. */
77
+ SlotNumber.add = function (sn: SlotNumber, increment: number): SlotNumber {
78
+ return SlotNumber(sn + increment);
79
+ };
80
+
76
81
  /**
77
82
  * The zero slot value.
78
83
  */
@@ -1,2 +1,3 @@
1
1
  export * from './buffer32.js';
2
2
  export * from './buffer16.js';
3
+ export * from './utils.js';
@@ -0,0 +1,8 @@
1
+ /** Returns the number of bytes in the buffer, excluding any trailing zero bytes. */
2
+ export function trimmedBytesLength(buf: Uint8Array): number {
3
+ let end = buf.length;
4
+ while (end > 0 && buf[end - 1] === 0) {
5
+ end--;
6
+ }
7
+ return end;
8
+ }
@@ -315,3 +315,17 @@ export function partition<T>(items: T[], predicate: (item: T) => boolean): [T[],
315
315
  }
316
316
  return [pass, fail];
317
317
  }
318
+
319
+ /** Partitions the given iterable into two arrays based on the predicate. */
320
+ export async function partitionAsync<T>(items: T[], predicate: (item: T) => Promise<boolean>): Promise<[T[], T[]]> {
321
+ const pass: T[] = [];
322
+ const fail: T[] = [];
323
+ for (const item of items) {
324
+ if (await predicate(item)) {
325
+ pass.push(item);
326
+ } else {
327
+ fail.push(item);
328
+ }
329
+ }
330
+ return [pass, fail];
331
+ }
@@ -12,6 +12,10 @@ export type EnvVar =
12
12
  | 'ARCHIVER_VIEM_POLLING_INTERVAL_MS'
13
13
  | 'ARCHIVER_BATCH_SIZE'
14
14
  | 'AZTEC_ADMIN_PORT'
15
+ | 'AZTEC_NODE_DEBUG'
16
+ | 'AZTEC_ADMIN_API_KEY_HASH'
17
+ | 'AZTEC_DISABLE_ADMIN_API_KEY'
18
+ | 'AZTEC_RESET_ADMIN_API_KEY'
15
19
  | 'AZTEC_NODE_ADMIN_URL'
16
20
  | 'AZTEC_NODE_URL'
17
21
  | 'AZTEC_PORT'
@@ -47,7 +51,10 @@ export type EnvVar =
47
51
  | 'BOT_TX_MINED_WAIT_SECONDS'
48
52
  | 'BOT_MAX_CONSECUTIVE_ERRORS'
49
53
  | 'BOT_STOP_WHEN_UNHEALTHY'
50
- | 'BOT_AMM_TXS'
54
+ | 'BOT_MODE'
55
+ | 'BOT_L2_TO_L1_MESSAGES_PER_TX'
56
+ | 'BOT_L1_TO_L2_SEED_COUNT'
57
+ | 'BOT_L1_TO_L2_SEED_INTERVAL'
51
58
  | 'COINBASE'
52
59
  | 'CRS_PATH'
53
60
  | 'DATA_DIRECTORY'
@@ -64,6 +71,7 @@ export type EnvVar =
64
71
  | 'PUBLIC_DATA_TREE_MAP_SIZE_KB'
65
72
  | 'DEBUG'
66
73
  | 'DEBUG_P2P_DISABLE_COLOCATION_PENALTY'
74
+ | 'ENABLE_PROVER_NODE'
67
75
  | 'ETHEREUM_HOSTS'
68
76
  | 'ETHEREUM_DEBUG_HOSTS'
69
77
  | 'ETHEREUM_ALLOW_NO_DEBUG_HOSTS'
@@ -73,6 +81,7 @@ export type EnvVar =
73
81
  | 'KEY_STORE_DIRECTORY'
74
82
  | 'L1_CHAIN_ID'
75
83
  | 'L1_CONSENSUS_HOST_URLS'
84
+ | 'ETHEREUM_HTTP_TIMEOUT_MS'
76
85
  | 'L1_CONSENSUS_HOST_API_KEYS'
77
86
  | 'L1_CONSENSUS_HOST_API_KEY_HEADERS'
78
87
  | 'LOG_JSON'
@@ -82,7 +91,6 @@ export type EnvVar =
82
91
  | 'MNEMONIC'
83
92
  | 'NETWORK'
84
93
  | 'NETWORK_CONFIG_LOCATION'
85
- | 'NO_PXE'
86
94
  | 'USE_GCLOUD_LOGGING'
87
95
  | 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT'
88
96
  | 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT'
@@ -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'
@@ -143,6 +152,8 @@ export type EnvVar =
143
152
  | 'P2P_DROP_TX'
144
153
  | 'P2P_DROP_TX_CHANCE'
145
154
  | 'P2P_TX_POOL_DELETE_TXS_AFTER_REORG'
155
+ | 'P2P_MIN_TX_POOL_AGE_MS'
156
+ | 'P2P_RPC_PRICE_BUMP_PERCENTAGE'
146
157
  | 'DEBUG_P2P_INSTRUMENT_MESSAGES'
147
158
  | 'PEER_ID_PRIVATE_KEY'
148
159
  | 'PEER_ID_PRIVATE_KEY_PATH'
@@ -156,8 +167,11 @@ export type EnvVar =
156
167
  | 'PROVER_BROKER_BATCH_INTERVAL_MS'
157
168
  | 'PROVER_BROKER_BATCH_SIZE'
158
169
  | 'PROVER_BROKER_MAX_EPOCHS_TO_KEEP_RESULTS_FOR'
170
+ | 'PROVER_BROKER_DEBUG_REPLAY_ENABLED'
159
171
  | 'PROVER_CANCEL_JOBS_ON_STOP'
172
+ | 'PROVER_ENQUEUE_CONCURRENCY'
160
173
  | 'PROVER_COORDINATION_NODE_URLS'
174
+ | 'PROVER_PROOF_STORE'
161
175
  | 'PROVER_FAILED_PROOF_STORE'
162
176
  | 'PROVER_NODE_FAILED_EPOCH_STORE'
163
177
  | 'PROVER_NODE_DISABLE_PROOF_PUBLISH'
@@ -189,12 +203,14 @@ export type EnvVar =
189
203
  | 'SENTINEL_ENABLED'
190
204
  | 'SENTINEL_HISTORY_LENGTH_IN_EPOCHS'
191
205
  | 'SENTINEL_HISTORIC_PROVEN_PERFORMANCE_LENGTH_IN_EPOCHS'
192
- | 'SEQ_MAX_BLOCK_SIZE_IN_BYTES'
193
206
  | 'SEQ_MAX_TX_PER_BLOCK'
207
+ | 'SEQ_MAX_TX_PER_CHECKPOINT'
194
208
  | 'SEQ_MIN_TX_PER_BLOCK'
195
209
  | 'SEQ_PUBLISH_TXS_WITH_PROPOSALS'
196
210
  | 'SEQ_MAX_DA_BLOCK_GAS'
197
211
  | 'SEQ_MAX_L2_BLOCK_GAS'
212
+ | 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER'
213
+ | 'SEQ_REDISTRIBUTE_CHECKPOINT_BUDGET'
198
214
  | 'SEQ_PUBLISHER_PRIVATE_KEY'
199
215
  | 'SEQ_PUBLISHER_PRIVATE_KEYS'
200
216
  | 'SEQ_PUBLISHER_ADDRESSES'
@@ -205,9 +221,11 @@ export type EnvVar =
205
221
  | 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT'
206
222
  | 'SEQ_ATTESTATION_PROPAGATION_TIME'
207
223
  | 'SEQ_BLOCK_DURATION_MS'
224
+ | 'SEQ_EXPECTED_BLOCK_PROPOSALS_PER_SLOT'
208
225
  | 'SEQ_BUILD_CHECKPOINT_IF_EMPTY'
209
226
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_COMMITTEE_MEMBER'
210
227
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_NON_COMMITTEE_MEMBER'
228
+ | 'SEQ_SKIP_CHECKPOINT_PUBLISH_PERCENT'
211
229
  | 'SLASH_MIN_PENALTY_PERCENTAGE'
212
230
  | 'SLASH_MAX_PENALTY_PERCENTAGE'
213
231
  | 'SLASH_VALIDATORS_ALWAYS'
@@ -218,6 +236,8 @@ export type EnvVar =
218
236
  | 'SLASH_INACTIVITY_TARGET_PERCENTAGE'
219
237
  | 'SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD'
220
238
  | 'SLASH_INVALID_BLOCK_PENALTY'
239
+ | 'SLASH_DUPLICATE_PROPOSAL_PENALTY'
240
+ | 'SLASH_DUPLICATE_ATTESTATION_PENALTY'
221
241
  | 'SLASH_OVERRIDE_PAYLOAD'
222
242
  | 'SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY'
223
243
  | 'SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY'
@@ -232,6 +252,7 @@ export type EnvVar =
232
252
  | 'TELEMETRY'
233
253
  | 'TEST_ACCOUNTS'
234
254
  | 'SPONSORED_FPC'
255
+ | 'PREFUND_ADDRESSES'
235
256
  | 'TX_COLLECTION_FAST_NODES_TIMEOUT_BEFORE_REQ_RESP_MS'
236
257
  | 'TX_COLLECTION_SLOW_NODES_INTERVAL_MS'
237
258
  | 'TX_COLLECTION_SLOW_REQ_RESP_INTERVAL_MS'
@@ -242,12 +263,29 @@ export type EnvVar =
242
263
  | 'TX_COLLECTION_FAST_MAX_PARALLEL_REQUESTS_PER_NODE'
243
264
  | 'TX_COLLECTION_NODE_RPC_MAX_BATCH_SIZE'
244
265
  | 'TX_COLLECTION_NODE_RPC_URLS'
245
- | 'TX_COLLECTION_PROPOSAL_TX_COLLECTOR_TYPE'
266
+ | 'TX_COLLECTION_MISSING_TXS_COLLECTOR_TYPE'
267
+ | 'TX_COLLECTION_FILE_STORE_URLS'
268
+ | 'TX_COLLECTION_FILE_STORE_SLOW_DELAY_MS'
269
+ | 'TX_COLLECTION_FILE_STORE_FAST_DELAY_MS'
270
+ | 'TX_COLLECTION_FILE_STORE_FAST_WORKER_COUNT'
271
+ | 'TX_COLLECTION_FILE_STORE_SLOW_WORKER_COUNT'
272
+ | 'TX_COLLECTION_FILE_STORE_FAST_BACKOFF_BASE_MS'
273
+ | 'TX_COLLECTION_FILE_STORE_SLOW_BACKOFF_BASE_MS'
274
+ | 'TX_COLLECTION_FILE_STORE_FAST_BACKOFF_MAX_MS'
275
+ | 'TX_COLLECTION_FILE_STORE_SLOW_BACKOFF_MAX_MS'
276
+ | 'TX_FILE_STORE_URL'
277
+ | 'TX_FILE_STORE_UPLOAD_CONCURRENCY'
278
+ | 'TX_FILE_STORE_MAX_QUEUE_SIZE'
279
+ | 'TX_FILE_STORE_ENABLED'
246
280
  | 'TX_PUBLIC_SETUP_ALLOWLIST'
247
281
  | 'TXE_PORT'
248
282
  | 'TRANSACTIONS_DISABLED'
249
283
  | 'VALIDATOR_ATTESTATIONS_POLLING_INTERVAL_MS'
250
284
  | 'VALIDATOR_DISABLED'
285
+ | 'VALIDATOR_MAX_DA_BLOCK_GAS'
286
+ | 'VALIDATOR_MAX_L2_BLOCK_GAS'
287
+ | 'VALIDATOR_MAX_TX_PER_BLOCK'
288
+ | 'VALIDATOR_MAX_TX_PER_CHECKPOINT'
251
289
  | 'VALIDATOR_PRIVATE_KEYS'
252
290
  | 'VALIDATOR_PRIVATE_KEY'
253
291
  | 'VALIDATOR_REEXECUTE'
@@ -257,6 +295,7 @@ export type EnvVar =
257
295
  | 'WS_BLOCK_REQUEST_BATCH_SIZE'
258
296
  | 'L1_READER_VIEM_POLLING_INTERVAL_MS'
259
297
  | 'WS_DATA_DIRECTORY'
298
+ | 'WS_NUM_HISTORIC_CHECKPOINTS'
260
299
  | 'WS_NUM_HISTORIC_BLOCKS'
261
300
  | 'ETHEREUM_SLOT_DURATION'
262
301
  | 'AZTEC_SLOT_DURATION'
@@ -311,9 +350,8 @@ export type EnvVar =
311
350
  | 'K8S_POD_NAME'
312
351
  | 'K8S_POD_UID'
313
352
  | 'K8S_NAMESPACE_NAME'
353
+ | 'ENABLE_VERSION_CHECK'
314
354
  | 'VALIDATOR_REEXECUTE_DEADLINE_MS'
315
- | 'AUTO_UPDATE'
316
- | 'AUTO_UPDATE_URL'
317
355
  | 'WEB3_SIGNER_URL'
318
356
  | 'SKIP_ARCHIVER_INITIAL_SYNC'
319
357
  | 'BLOB_ALLOW_EMPTY_SOURCES'
@@ -326,6 +364,7 @@ export type EnvVar =
326
364
  | 'VALIDATOR_HA_POLLING_INTERVAL_MS'
327
365
  | 'VALIDATOR_HA_SIGNING_TIMEOUT_MS'
328
366
  | 'VALIDATOR_HA_MAX_STUCK_DUTIES_AGE_MS'
367
+ | 'VALIDATOR_HA_OLD_DUTIES_MAX_AGE_H'
329
368
  | 'VALIDATOR_HA_DATABASE_URL'
330
369
  | 'VALIDATOR_HA_RUN_MIGRATIONS'
331
370
  | 'VALIDATOR_HA_POOL_MAX'
@@ -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,
@@ -5,10 +5,13 @@ export const NetworkConfigSchema = z
5
5
  bootnodes: z.array(z.string()),
6
6
  snapshots: z.array(z.string()),
7
7
  blobFileStoreUrls: z.array(z.string()).optional(),
8
+ txCollectionFileStoreUrls: z.array(z.string()).optional(),
8
9
  registryAddress: z.string(),
9
10
  feeAssetHandlerAddress: z.string().optional(),
10
11
  l1ChainId: z.number(),
11
12
  blockDurationMs: z.number().positive().optional(),
13
+ nodeVersion: z.string().optional(),
14
+ txPublicSetupAllowListExtend: z.string().optional(),
12
15
  })
13
16
  .passthrough(); // Allow additional unknown fields to pass through
14
17
 
@@ -1,21 +1,35 @@
1
- import { BarretenbergSync } from '@aztec/bb.js';
1
+ import { Barretenberg, BarretenbergSync } 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';
5
5
 
6
+ const IS_BROWSER = typeof self !== 'undefined';
7
+
8
+ async function poseidon2HashFields(inputFields: Fr[]): Promise<Fr> {
9
+ if (IS_BROWSER) {
10
+ await BarretenbergSync.initSingleton();
11
+ const api = BarretenbergSync.getSingleton();
12
+ const response = api.poseidon2Hash({
13
+ inputs: inputFields.map(i => i.toBuffer()),
14
+ });
15
+ return Fr.fromBuffer(Buffer.from(response.hash));
16
+ } else {
17
+ await Barretenberg.initSingleton();
18
+ const api = Barretenberg.getSingleton();
19
+ const response = await api.poseidon2Hash({
20
+ inputs: inputFields.map(i => i.toBuffer()),
21
+ });
22
+ return Fr.fromBuffer(Buffer.from(response.hash));
23
+ }
24
+ }
25
+
6
26
  /**
7
27
  * Create a poseidon hash (field) from an array of input fields.
8
28
  * @param input - The input fields to hash.
9
29
  * @returns The poseidon hash.
10
30
  */
11
- export async function poseidon2Hash(input: Fieldable[]): Promise<Fr> {
12
- const inputFields = serializeToFields(input);
13
- await BarretenbergSync.initSingleton();
14
- const api = BarretenbergSync.getSingleton();
15
- const response = api.poseidon2Hash({
16
- inputs: inputFields.map(i => i.toBuffer()),
17
- });
18
- return Fr.fromBuffer(Buffer.from(response.hash));
31
+ export function poseidon2Hash(input: Fieldable[]): Promise<Fr> {
32
+ return poseidon2HashFields(serializeToFields(input));
19
33
  }
20
34
 
21
35
  /**
@@ -24,15 +38,10 @@ export async function poseidon2Hash(input: Fieldable[]): Promise<Fr> {
24
38
  * @param separator - The domain separator.
25
39
  * @returns The poseidon hash.
26
40
  */
27
- export async function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Promise<Fr> {
41
+ export function poseidon2HashWithSeparator(input: Fieldable[], separator: number): Promise<Fr> {
28
42
  const inputFields = serializeToFields(input);
29
43
  inputFields.unshift(new Fr(separator));
30
- await BarretenbergSync.initSingleton();
31
- const api = BarretenbergSync.getSingleton();
32
- const response = api.poseidon2Hash({
33
- inputs: inputFields.map(i => i.toBuffer()),
34
- });
35
- return Fr.fromBuffer(Buffer.from(response.hash));
44
+ return poseidon2HashFields(inputFields);
36
45
  }
37
46
 
38
47
  /**
@@ -42,19 +51,24 @@ export async function poseidon2HashWithSeparator(input: Fieldable[], separator:
42
51
  */
43
52
  export async function poseidon2Permutation(input: Fieldable[]): Promise<Fr[]> {
44
53
  const inputFields = serializeToFields(input);
45
- // We'd like this assertion but it's not possible to use it in the browser.
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({
50
- inputs: inputFields.map(i => i.toBuffer()),
51
- });
52
- // We'd like this assertion but it's not possible to use it in the browser.
53
- // assert(response.outputs.length === 4, 'Output state must be of size 4');
54
- return response.outputs.map(o => Fr.fromBuffer(Buffer.from(o)));
54
+ if (IS_BROWSER) {
55
+ await BarretenbergSync.initSingleton();
56
+ const api = BarretenbergSync.getSingleton();
57
+ const response = api.poseidon2Permutation({
58
+ inputs: inputFields.map(i => i.toBuffer()),
59
+ });
60
+ return response.outputs.map(o => Fr.fromBuffer(Buffer.from(o)));
61
+ } else {
62
+ await Barretenberg.initSingleton();
63
+ const api = Barretenberg.getSingleton();
64
+ const response = await api.poseidon2Permutation({
65
+ inputs: inputFields.map(i => i.toBuffer()),
66
+ });
67
+ return response.outputs.map(o => Fr.fromBuffer(Buffer.from(o)));
68
+ }
55
69
  }
56
70
 
57
- export async function poseidon2HashBytes(input: Buffer): Promise<Fr> {
71
+ export function poseidon2HashBytes(input: Buffer): Promise<Fr> {
58
72
  const inputFields = [];
59
73
  for (let i = 0; i < input.length; i += 31) {
60
74
  const fieldBytes = Buffer.alloc(32, 0);
@@ -65,11 +79,5 @@ export async function poseidon2HashBytes(input: Buffer): Promise<Fr> {
65
79
  inputFields.push(Fr.fromBuffer(fieldBytes));
66
80
  }
67
81
 
68
- await BarretenbergSync.initSingleton();
69
- const api = BarretenbergSync.getSingleton();
70
- const response = api.poseidon2Hash({
71
- inputs: inputFields.map(i => i.toBuffer()),
72
- });
73
-
74
- return Fr.fromBuffer(Buffer.from(response.hash));
82
+ return poseidon2HashFields(inputFields);
75
83
  }
@@ -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
  }
@@ -65,7 +65,7 @@ export class Point {
65
65
  }
66
66
 
67
67
  /**
68
- * Generate a random Point instance.
68
+ * Generate a random Point instance that is on the curve.
69
69
  *
70
70
  * @returns A randomly generated Point instance.
71
71
  */
@@ -125,9 +125,12 @@ export class Point {
125
125
  }
126
126
 
127
127
  /**
128
- * Returns the contents of the point as an array of 2 fields.
129
- * @returns The point as an array of 2 fields
128
+ * Returns the contents of the point as an array of 3 fields.
129
+ * @returns The point as an array of 3 fields
130
130
  */
131
+ // TODO(F-553): Once we take the breaking change and drop the custom Noir `Point` wrapper in
132
+ // `noir-projects/noir-protocol-circuits/crates/types/src/point.nr`, revert this to serialize as `[x, y]` (2 fields)
133
+ // and drop the `is_infinite` flag from `fromFields` / `toNoirStruct` below.
131
134
  toFields() {
132
135
  return [this.x, this.y, new Fr(this.isInfinite)];
133
136
  }
@@ -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 {
@@ -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) {
@@ -0,0 +1,63 @@
1
+ import { timingSafeEqual } from 'crypto';
2
+ import type Koa from 'koa';
3
+
4
+ import { sha256 } from '../../crypto/sha256/index.js';
5
+ import { createLogger } from '../../log/index.js';
6
+
7
+ const log = createLogger('json-rpc:api-key-auth');
8
+
9
+ /**
10
+ * Computes the SHA-256 hash of a string and returns it as a Buffer.
11
+ * @param input - The input string to hash.
12
+ * @returns The SHA-256 hash as a Buffer.
13
+ */
14
+ export function sha256Hash(input: string): Buffer {
15
+ return sha256(Buffer.from(input));
16
+ }
17
+
18
+ /**
19
+ * Creates a Koa middleware that enforces API key authentication on all requests
20
+ * except the health check endpoint (GET /status).
21
+ *
22
+ * The API key can be provided via the `x-api-key` header or the `Authorization: Bearer <key>` header.
23
+ * Comparison is done by hashing the provided key with SHA-256 and comparing against the stored hash.
24
+ *
25
+ * @param apiKeyHash - The SHA-256 hash of the expected API key as a Buffer.
26
+ * @returns A Koa middleware that rejects requests without a valid API key.
27
+ */
28
+ export function getApiKeyAuthMiddleware(
29
+ apiKeyHash: Buffer,
30
+ ): (ctx: Koa.Context, next: () => Promise<void>) => Promise<void> {
31
+ return async (ctx: Koa.Context, next: () => Promise<void>) => {
32
+ // Allow health check through without auth
33
+ if (ctx.path === '/status' && ctx.method === 'GET') {
34
+ return next();
35
+ }
36
+
37
+ const providedKey = ctx.get('x-api-key') || ctx.get('authorization')?.replace(/^Bearer\s+/i, '');
38
+ if (!providedKey) {
39
+ log.warn(`Rejected admin RPC request from ${ctx.ip}: missing API key`);
40
+ ctx.status = 401;
41
+ ctx.body = {
42
+ jsonrpc: '2.0',
43
+ id: null,
44
+ error: { code: -32000, message: 'Unauthorized: invalid or missing API key' },
45
+ };
46
+ return;
47
+ }
48
+
49
+ const providedHashBuf = sha256Hash(providedKey);
50
+ if (!timingSafeEqual(apiKeyHash, providedHashBuf)) {
51
+ log.warn(`Rejected admin RPC request from ${ctx.ip}: invalid API key`);
52
+ ctx.status = 401;
53
+ ctx.body = {
54
+ jsonrpc: '2.0',
55
+ id: null,
56
+ error: { code: -32000, message: 'Unauthorized: invalid or missing API key' },
57
+ };
58
+ return;
59
+ }
60
+
61
+ await next();
62
+ };
63
+ }
@@ -1 +1,2 @@
1
+ export * from './api_key_auth.js';
1
2
  export * from './safe_json_rpc_server.js';