@aztec/foundation 0.0.1-commit.8afd444 → 0.0.1-commit.8c0b8ff

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 +38 -7
  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.8afd444",
3
+ "version": "0.0.1-commit.8c0b8ff",
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.8afd444",
147
+ "@aztec/bb.js": "0.0.1-commit.8c0b8ff",
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'
@@ -158,6 +169,7 @@ export type EnvVar =
158
169
  | 'PROVER_BROKER_MAX_EPOCHS_TO_KEEP_RESULTS_FOR'
159
170
  | 'PROVER_BROKER_DEBUG_REPLAY_ENABLED'
160
171
  | 'PROVER_CANCEL_JOBS_ON_STOP'
172
+ | 'PROVER_ENQUEUE_CONCURRENCY'
161
173
  | 'PROVER_COORDINATION_NODE_URLS'
162
174
  | 'PROVER_PROOF_STORE'
163
175
  | 'PROVER_FAILED_PROOF_STORE'
@@ -191,12 +203,14 @@ export type EnvVar =
191
203
  | 'SENTINEL_ENABLED'
192
204
  | 'SENTINEL_HISTORY_LENGTH_IN_EPOCHS'
193
205
  | 'SENTINEL_HISTORIC_PROVEN_PERFORMANCE_LENGTH_IN_EPOCHS'
194
- | 'SEQ_MAX_BLOCK_SIZE_IN_BYTES'
195
206
  | 'SEQ_MAX_TX_PER_BLOCK'
207
+ | 'SEQ_MAX_TX_PER_CHECKPOINT'
196
208
  | 'SEQ_MIN_TX_PER_BLOCK'
197
209
  | 'SEQ_PUBLISH_TXS_WITH_PROPOSALS'
198
210
  | 'SEQ_MAX_DA_BLOCK_GAS'
199
211
  | 'SEQ_MAX_L2_BLOCK_GAS'
212
+ | 'SEQ_PER_BLOCK_ALLOCATION_MULTIPLIER'
213
+ | 'SEQ_REDISTRIBUTE_CHECKPOINT_BUDGET'
200
214
  | 'SEQ_PUBLISHER_PRIVATE_KEY'
201
215
  | 'SEQ_PUBLISHER_PRIVATE_KEYS'
202
216
  | 'SEQ_PUBLISHER_ADDRESSES'
@@ -207,9 +221,11 @@ export type EnvVar =
207
221
  | 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT'
208
222
  | 'SEQ_ATTESTATION_PROPAGATION_TIME'
209
223
  | 'SEQ_BLOCK_DURATION_MS'
224
+ | 'SEQ_EXPECTED_BLOCK_PROPOSALS_PER_SLOT'
210
225
  | 'SEQ_BUILD_CHECKPOINT_IF_EMPTY'
211
226
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_COMMITTEE_MEMBER'
212
227
  | 'SEQ_SECONDS_BEFORE_INVALIDATING_BLOCK_AS_NON_COMMITTEE_MEMBER'
228
+ | 'SEQ_SKIP_CHECKPOINT_PUBLISH_PERCENT'
213
229
  | 'SLASH_MIN_PENALTY_PERCENTAGE'
214
230
  | 'SLASH_MAX_PENALTY_PERCENTAGE'
215
231
  | 'SLASH_VALIDATORS_ALWAYS'
@@ -220,6 +236,8 @@ export type EnvVar =
220
236
  | 'SLASH_INACTIVITY_TARGET_PERCENTAGE'
221
237
  | 'SLASH_INACTIVITY_CONSECUTIVE_EPOCH_THRESHOLD'
222
238
  | 'SLASH_INVALID_BLOCK_PENALTY'
239
+ | 'SLASH_DUPLICATE_PROPOSAL_PENALTY'
240
+ | 'SLASH_DUPLICATE_ATTESTATION_PENALTY'
223
241
  | 'SLASH_OVERRIDE_PAYLOAD'
224
242
  | 'SLASH_PROPOSE_INVALID_ATTESTATIONS_PENALTY'
225
243
  | 'SLASH_ATTEST_DESCENDANT_OF_INVALID_PENALTY'
@@ -234,6 +252,7 @@ export type EnvVar =
234
252
  | 'TELEMETRY'
235
253
  | 'TEST_ACCOUNTS'
236
254
  | 'SPONSORED_FPC'
255
+ | 'PREFUND_ADDRESSES'
237
256
  | 'TX_COLLECTION_FAST_NODES_TIMEOUT_BEFORE_REQ_RESP_MS'
238
257
  | 'TX_COLLECTION_SLOW_NODES_INTERVAL_MS'
239
258
  | 'TX_COLLECTION_SLOW_REQ_RESP_INTERVAL_MS'
@@ -244,9 +263,17 @@ export type EnvVar =
244
263
  | 'TX_COLLECTION_FAST_MAX_PARALLEL_REQUESTS_PER_NODE'
245
264
  | 'TX_COLLECTION_NODE_RPC_MAX_BATCH_SIZE'
246
265
  | 'TX_COLLECTION_NODE_RPC_URLS'
247
- | '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'
248
276
  | 'TX_FILE_STORE_URL'
249
- | 'TX_FILE_STORE_DOWNLOAD_URL'
250
277
  | 'TX_FILE_STORE_UPLOAD_CONCURRENCY'
251
278
  | 'TX_FILE_STORE_MAX_QUEUE_SIZE'
252
279
  | 'TX_FILE_STORE_ENABLED'
@@ -255,6 +282,10 @@ export type EnvVar =
255
282
  | 'TRANSACTIONS_DISABLED'
256
283
  | 'VALIDATOR_ATTESTATIONS_POLLING_INTERVAL_MS'
257
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'
258
289
  | 'VALIDATOR_PRIVATE_KEYS'
259
290
  | 'VALIDATOR_PRIVATE_KEY'
260
291
  | 'VALIDATOR_REEXECUTE'
@@ -264,6 +295,7 @@ export type EnvVar =
264
295
  | 'WS_BLOCK_REQUEST_BATCH_SIZE'
265
296
  | 'L1_READER_VIEM_POLLING_INTERVAL_MS'
266
297
  | 'WS_DATA_DIRECTORY'
298
+ | 'WS_NUM_HISTORIC_CHECKPOINTS'
267
299
  | 'WS_NUM_HISTORIC_BLOCKS'
268
300
  | 'ETHEREUM_SLOT_DURATION'
269
301
  | 'AZTEC_SLOT_DURATION'
@@ -318,9 +350,8 @@ export type EnvVar =
318
350
  | 'K8S_POD_NAME'
319
351
  | 'K8S_POD_UID'
320
352
  | 'K8S_NAMESPACE_NAME'
353
+ | 'ENABLE_VERSION_CHECK'
321
354
  | 'VALIDATOR_REEXECUTE_DEADLINE_MS'
322
- | 'AUTO_UPDATE'
323
- | 'AUTO_UPDATE_URL'
324
355
  | 'WEB3_SIGNER_URL'
325
356
  | 'SKIP_ARCHIVER_INITIAL_SYNC'
326
357
  | 'BLOB_ALLOW_EMPTY_SOURCES'
@@ -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';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Converts bigint values to strings recursively in a log object to avoid serialization issues.
3
+ */
4
+ export function convertBigintsToStrings(obj: unknown): unknown {
5
+ if (typeof obj === 'bigint') {
6
+ return String(obj);
7
+ }
8
+
9
+ if (Array.isArray(obj)) {
10
+ return obj.map(item => convertBigintsToStrings(item));
11
+ }
12
+
13
+ if (obj !== null && typeof obj === 'object') {
14
+ if (typeof (obj as any).toJSON === 'function') {
15
+ return convertBigintsToStrings((obj as any).toJSON());
16
+ }
17
+ const result: Record<string, unknown> = {};
18
+ for (const key in obj) {
19
+ result[key] = convertBigintsToStrings((obj as Record<string, unknown>)[key]);
20
+ }
21
+ return result;
22
+ }
23
+
24
+ return obj;
25
+ }