@aztec/pxe 0.0.1-commit.f5d02921e → 0.0.1-commit.f7ea82942

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 (162) hide show
  1. package/dest/bin/check_oracle_version.js +4 -4
  2. package/dest/block_synchronizer/block_synchronizer.d.ts +6 -2
  3. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  4. package/dest/block_synchronizer/block_synchronizer.js +13 -1
  5. package/dest/config/index.d.ts +1 -1
  6. package/dest/config/index.d.ts.map +1 -1
  7. package/dest/config/index.js +7 -14
  8. package/dest/contract_function_simulator/contract_function_simulator.d.ts +6 -4
  9. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  10. package/dest/contract_function_simulator/contract_function_simulator.js +6 -4
  11. package/dest/contract_function_simulator/ephemeral_array_service.d.ts +28 -0
  12. package/dest/contract_function_simulator/ephemeral_array_service.d.ts.map +1 -0
  13. package/dest/contract_function_simulator/ephemeral_array_service.js +78 -0
  14. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +1 -1
  15. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +1 -1
  16. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.d.ts +1 -1
  17. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.js +1 -1
  18. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
  19. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -1
  20. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +1 -1
  21. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +1 -1
  22. package/dest/contract_function_simulator/oracle/interfaces.d.ts +13 -2
  23. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  24. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +1 -1
  25. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -1
  26. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +10 -1
  27. package/dest/contract_function_simulator/oracle/oracle.d.ts +13 -2
  28. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/oracle/oracle.js +98 -3
  30. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +4 -4
  31. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +19 -4
  33. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +30 -16
  34. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +106 -37
  36. package/dest/contract_function_simulator/pick_notes.d.ts +1 -1
  37. package/dest/contract_function_simulator/pick_notes.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/pick_notes.js +20 -3
  39. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts +1 -1
  40. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts.map +1 -1
  41. package/dest/contract_function_simulator/proxied_contract_data_source.js +3 -0
  42. package/dest/contract_sync/contract_sync_service.d.ts +4 -6
  43. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  44. package/dest/contract_sync/contract_sync_service.js +38 -46
  45. package/dest/contract_sync/helpers.d.ts +2 -3
  46. package/dest/contract_sync/helpers.d.ts.map +1 -1
  47. package/dest/debug/pxe_debug_utils.d.ts +3 -3
  48. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  49. package/dest/entrypoints/client/bundle/index.d.ts +1 -2
  50. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  51. package/dest/entrypoints/client/bundle/index.js +0 -1
  52. package/dest/entrypoints/client/bundle/utils.d.ts +2 -2
  53. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  54. package/dest/entrypoints/client/bundle/utils.js +2 -2
  55. package/dest/entrypoints/client/lazy/index.d.ts +1 -2
  56. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  57. package/dest/entrypoints/client/lazy/index.js +0 -1
  58. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  59. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  60. package/dest/entrypoints/client/lazy/utils.js +2 -2
  61. package/dest/entrypoints/pxe_creation_options.d.ts +3 -1
  62. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  63. package/dest/entrypoints/pxe_creation_options.js +3 -1
  64. package/dest/entrypoints/server/index.d.ts +2 -3
  65. package/dest/entrypoints/server/index.d.ts.map +1 -1
  66. package/dest/entrypoints/server/index.js +1 -2
  67. package/dest/entrypoints/server/utils.d.ts +2 -2
  68. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  69. package/dest/entrypoints/server/utils.js +2 -2
  70. package/dest/events/event_service.d.ts +1 -1
  71. package/dest/events/event_service.d.ts.map +1 -1
  72. package/dest/events/event_service.js +10 -1
  73. package/dest/events/private_event_filter_validator.d.ts +3 -2
  74. package/dest/events/private_event_filter_validator.d.ts.map +1 -1
  75. package/dest/events/private_event_filter_validator.js +15 -0
  76. package/dest/logs/log_service.d.ts +6 -6
  77. package/dest/logs/log_service.d.ts.map +1 -1
  78. package/dest/logs/log_service.js +15 -21
  79. package/dest/notes/note_service.d.ts +3 -4
  80. package/dest/notes/note_service.d.ts.map +1 -1
  81. package/dest/notes_filter.d.ts +2 -3
  82. package/dest/notes_filter.d.ts.map +1 -1
  83. package/dest/oracle_version.d.ts +4 -3
  84. package/dest/oracle_version.d.ts.map +1 -1
  85. package/dest/oracle_version.js +20 -10
  86. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  87. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  88. package/dest/private_kernel/private_kernel_execution_prover.js +4 -7
  89. package/dest/private_kernel/private_kernel_oracle.d.ts +5 -5
  90. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  91. package/dest/private_kernel/private_kernel_oracle.js +12 -15
  92. package/dest/pxe.d.ts +5 -5
  93. package/dest/pxe.d.ts.map +1 -1
  94. package/dest/pxe.js +27 -20
  95. package/dest/storage/anchor_block_store/anchor_block_store.js +1 -1
  96. package/dest/storage/capsule_store/capsule_service.d.ts +2 -3
  97. package/dest/storage/capsule_store/capsule_service.d.ts.map +1 -1
  98. package/dest/storage/capsule_store/capsule_service.js +1 -1
  99. package/dest/storage/capsule_store/capsule_store.d.ts +1 -1
  100. package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
  101. package/dest/storage/capsule_store/capsule_store.js +8 -5
  102. package/dest/storage/contract_store/contract_store.d.ts +1 -1
  103. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  104. package/dest/storage/contract_store/contract_store.js +4 -2
  105. package/dest/storage/note_store/note_store.d.ts +1 -1
  106. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  107. package/dest/storage/note_store/note_store.js +2 -2
  108. package/dest/storage/private_event_store/private_event_store.d.ts +1 -1
  109. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  110. package/dest/storage/private_event_store/private_event_store.js +3 -0
  111. package/dest/storage/private_event_store/stored_private_event.js +1 -1
  112. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +2 -2
  113. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  114. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +2 -16
  115. package/package.json +16 -16
  116. package/src/bin/check_oracle_version.ts +4 -4
  117. package/src/block_synchronizer/block_synchronizer.ts +16 -2
  118. package/src/config/index.ts +2 -8
  119. package/src/contract_function_simulator/contract_function_simulator.ts +9 -7
  120. package/src/contract_function_simulator/ephemeral_array_service.ts +110 -0
  121. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +1 -1
  122. package/src/contract_function_simulator/noir-structs/log_retrieval_request.ts +1 -1
  123. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -1
  124. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +1 -1
  125. package/src/contract_function_simulator/oracle/interfaces.ts +20 -1
  126. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +7 -1
  127. package/src/contract_function_simulator/oracle/oracle.ts +143 -3
  128. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +28 -6
  129. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +171 -67
  130. package/src/contract_function_simulator/pick_notes.ts +22 -3
  131. package/src/contract_function_simulator/proxied_contract_data_source.ts +8 -1
  132. package/src/contract_sync/contract_sync_service.ts +64 -80
  133. package/src/contract_sync/helpers.ts +2 -3
  134. package/src/debug/pxe_debug_utils.ts +3 -3
  135. package/src/entrypoints/client/bundle/index.ts +0 -1
  136. package/src/entrypoints/client/bundle/utils.ts +2 -3
  137. package/src/entrypoints/client/lazy/index.ts +0 -1
  138. package/src/entrypoints/client/lazy/utils.ts +2 -3
  139. package/src/entrypoints/pxe_creation_options.ts +7 -0
  140. package/src/entrypoints/server/index.ts +1 -2
  141. package/src/entrypoints/server/utils.ts +2 -3
  142. package/src/events/event_service.ts +13 -1
  143. package/src/events/private_event_filter_validator.ts +21 -1
  144. package/src/logs/log_service.ts +24 -50
  145. package/src/notes/note_service.ts +2 -3
  146. package/src/notes_filter.ts +1 -3
  147. package/src/oracle_version.ts +20 -10
  148. package/src/private_kernel/private_kernel_execution_prover.ts +4 -9
  149. package/src/private_kernel/private_kernel_oracle.ts +14 -14
  150. package/src/pxe.ts +61 -26
  151. package/src/storage/anchor_block_store/anchor_block_store.ts +1 -1
  152. package/src/storage/capsule_store/capsule_service.ts +5 -6
  153. package/src/storage/capsule_store/capsule_store.ts +15 -5
  154. package/src/storage/contract_store/contract_store.ts +8 -6
  155. package/src/storage/note_store/note_store.ts +2 -5
  156. package/src/storage/private_event_store/private_event_store.ts +4 -0
  157. package/src/storage/private_event_store/stored_private_event.ts +1 -1
  158. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +5 -15
  159. package/dest/access_scopes.d.ts +0 -9
  160. package/dest/access_scopes.d.ts.map +0 -1
  161. package/dest/access_scopes.js +0 -6
  162. package/src/access_scopes.ts +0 -9
@@ -1,15 +1,18 @@
1
1
  import type { Logger } from '@aztec/foundation/log';
2
+ import { Semaphore } from '@aztec/foundation/queue';
2
3
  import type { FunctionCall, FunctionSelector } from '@aztec/stdlib/abi';
3
4
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
5
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
6
  import type { BlockHeader } from '@aztec/stdlib/tx';
6
7
 
7
- import type { AccessScopes } from '../access_scopes.js';
8
8
  import type { StagedStore } from '../job_coordinator/job_coordinator.js';
9
9
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
10
10
  import type { NoteStore } from '../storage/note_store/note_store.js';
11
11
  import { syncState, verifyCurrentClassId } from './helpers.js';
12
12
 
13
+ /** Maximum number of scope syncs running concurrently across the PXE. */
14
+ const MAX_CONCURRENT_SCOPE_SYNCS = 5;
15
+
13
16
  /**
14
17
  * Service for syncing the private state of contracts and verifying that the PXE holds the current class artifact.
15
18
  * It uses a cache to avoid redundant sync operations - the cache is wiped when the anchor block changes.
@@ -27,11 +30,15 @@ export class ContractSyncService implements StagedStore {
27
30
  // Per-job excluded contract addresses - these contracts should not be synced.
28
31
  private excludedFromSync: Map<string, Set<string>> = new Map();
29
32
 
33
+ // Bounds the number of scope syncs running concurrently. Scopes beyond this limit queue here. Sized to trade off
34
+ // parallelism on non-ACIR work (node RPC, note store reads) against memory pressure from concurrent circuit
35
+ // execution.
36
+ #syncSlot = new Semaphore(MAX_CONCURRENT_SCOPE_SYNCS);
37
+
30
38
  constructor(
31
39
  private aztecNode: AztecNode,
32
40
  private contractStore: ContractStore,
33
41
  private noteStore: NoteStore,
34
- private getRegisteredAccounts: () => Promise<AztecAddress[]>,
35
42
  private log: Logger,
36
43
  ) {}
37
44
 
@@ -52,72 +59,42 @@ export class ContractSyncService implements StagedStore {
52
59
  async ensureContractSynced(
53
60
  contractAddress: AztecAddress,
54
61
  functionToInvokeAfterSync: FunctionSelector | null,
55
- utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
62
+ utilityExecutor: (call: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
56
63
  anchorBlockHeader: BlockHeader,
57
64
  jobId: string,
58
- scopes: AccessScopes,
65
+ scopes: AztecAddress[],
59
66
  ): Promise<void> {
60
67
  if (this.#shouldSkipSync(jobId, contractAddress)) {
61
68
  return;
62
69
  }
63
70
 
64
- this.#startSyncIfNeeded(contractAddress, scopes, scopesToSync =>
65
- this.#syncContract(
66
- contractAddress,
67
- functionToInvokeAfterSync,
68
- utilityExecutor,
69
- anchorBlockHeader,
70
- jobId,
71
- scopesToSync,
72
- ),
71
+ this.#startSyncIfNeeded(
72
+ contractAddress,
73
+ scopes,
74
+ () => verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
75
+ scope =>
76
+ syncState(
77
+ contractAddress,
78
+ this.contractStore,
79
+ functionToInvokeAfterSync,
80
+ utilityExecutor,
81
+ this.noteStore,
82
+ this.aztecNode,
83
+ anchorBlockHeader,
84
+ jobId,
85
+ scope,
86
+ ),
73
87
  );
74
88
 
75
89
  await this.#awaitSync(contractAddress, scopes);
76
90
  }
77
91
 
78
- /** Clears sync cache entries for the given scopes of a contract. Also clears the ALL_SCOPES entry. */
92
+ /** Clears sync cache entries for the given scopes of a contract. */
79
93
  invalidateContractForScopes(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
80
94
  if (scopes.length === 0) {
81
95
  return;
82
96
  }
83
97
  scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
84
- this.syncedContracts.delete(toKey(contractAddress, 'ALL_SCOPES'));
85
- }
86
-
87
- async #syncContract(
88
- contractAddress: AztecAddress,
89
- functionToInvokeAfterSync: FunctionSelector | null,
90
- utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
91
- anchorBlockHeader: BlockHeader,
92
- jobId: string,
93
- scopes: AccessScopes,
94
- ): Promise<void> {
95
- this.log.debug(`Syncing contract ${contractAddress}`);
96
-
97
- // Resolve ALL_SCOPES to actual registered accounts, since sync_state must be called once per account.
98
- const scopeAddresses = scopes === 'ALL_SCOPES' ? await this.getRegisteredAccounts() : scopes;
99
-
100
- await Promise.all([
101
- // Call sync_state sequentially for each scope address — each invocation synchronizes one account's private
102
- // state using scoped capsule arrays.
103
- (async () => {
104
- for (const scope of scopeAddresses) {
105
- await syncState(
106
- contractAddress,
107
- this.contractStore,
108
- functionToInvokeAfterSync,
109
- utilityExecutor,
110
- this.noteStore,
111
- this.aztecNode,
112
- anchorBlockHeader,
113
- jobId,
114
- scope,
115
- );
116
- }
117
- })(),
118
- verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
119
- ]);
120
- this.log.debug(`Contract ${contractAddress} synced`);
121
98
  }
122
99
 
123
100
  /** Clears sync cache. Called by BlockSynchronizer when anchor block changes. */
@@ -144,49 +121,56 @@ export class ContractSyncService implements StagedStore {
144
121
  return !!this.excludedFromSync.get(jobId)?.has(contractAddress.toString());
145
122
  }
146
123
 
147
- /** If there are unsynced scopes, starts sync and stores the promise in cache with error cleanup. */
124
+ /**
125
+ * If there are unsynced scopes, starts one sync per scope (bounded by #syncSlot) and stores each promise in the
126
+ * cache with per-scope error cleanup. The verifyFn runs once for the whole fan-out and is awaited by every new
127
+ * scope's promise, matching the pre-parallelization invariant that a cache-miss batch re-verifies the class id.
128
+ */
148
129
  #startSyncIfNeeded(
149
130
  contractAddress: AztecAddress,
150
- scopes: AccessScopes,
151
- syncFn: (scopesToSync: AccessScopes) => Promise<void>,
131
+ scopes: AztecAddress[],
132
+ verifyFn: () => Promise<void>,
133
+ syncScopeFn: (scope: AztecAddress) => Promise<void>,
152
134
  ): void {
153
- const scopesToSync = this.#getScopesToSync(contractAddress, scopes);
154
- const keys = toKeys(contractAddress, scopesToSync);
155
- if (keys.length === 0) {
135
+ const scopesToSync = scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
136
+ if (scopesToSync.length === 0) {
156
137
  return;
157
138
  }
158
- const promise = syncFn(scopesToSync).catch(err => {
159
- keys.forEach(key => this.syncedContracts.delete(key));
160
- throw err;
161
- });
162
- keys.forEach(key => this.syncedContracts.set(key, promise));
163
- }
164
139
 
165
- /** Filters out scopes that are already cached, returning only those that still need syncing. */
166
- #getScopesToSync(contractAddress: AztecAddress, scopes: AccessScopes): AccessScopes {
167
- if (this.syncedContracts.has(toKey(contractAddress, 'ALL_SCOPES'))) {
168
- // If we are already syncing all scopes, then return an empty list
169
- return [];
140
+ this.log.debug(`Syncing contract ${contractAddress} for ${scopesToSync.length} scope(s)`);
141
+ const verifyPromise = verifyFn();
142
+
143
+ for (const scope of scopesToSync) {
144
+ const key = toKey(contractAddress, scope);
145
+ const promise = Promise.all([verifyPromise, this.#runBounded(() => syncScopeFn(scope))])
146
+ .then(() => {})
147
+ .catch(err => {
148
+ this.syncedContracts.delete(key);
149
+ throw err;
150
+ });
151
+ this.syncedContracts.set(key, promise);
170
152
  }
171
- if (scopes === 'ALL_SCOPES') {
172
- return 'ALL_SCOPES';
153
+ }
154
+
155
+ /** Runs fn while holding a slot in #syncSlot, bounding total concurrent scope syncs. */
156
+ async #runBounded<T>(fn: () => Promise<T>): Promise<T> {
157
+ await this.#syncSlot.acquire();
158
+ try {
159
+ return await fn();
160
+ } finally {
161
+ this.#syncSlot.release();
173
162
  }
174
- return scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
175
163
  }
176
164
 
177
165
  /** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
178
- async #awaitSync(contractAddress: AztecAddress, scopes: AccessScopes): Promise<void> {
179
- const promises = toKeys(contractAddress, scopes)
180
- .map(key => this.syncedContracts.get(key))
166
+ async #awaitSync(contractAddress: AztecAddress, scopes: AztecAddress[]): Promise<void> {
167
+ const promises = scopes
168
+ .map(scope => this.syncedContracts.get(toKey(contractAddress, scope)))
181
169
  .filter(p => p !== undefined);
182
170
  await Promise.all(promises);
183
171
  }
184
172
  }
185
173
 
186
- function toKeys(contract: AztecAddress, scopes: AccessScopes) {
187
- return scopes === 'ALL_SCOPES' ? [toKey(contract, scopes)] : scopes.map(scope => toKey(contract, scope));
188
- }
189
-
190
- function toKey(contract: AztecAddress, scope: AztecAddress | 'ALL_SCOPES') {
191
- return scope === 'ALL_SCOPES' ? `${contract.toString()}:*` : `${contract.toString()}:${scope.toString()}`;
174
+ function toKey(contract: AztecAddress, scope: AztecAddress) {
175
+ return `${contract.toString()}:${scope.toString()}`;
192
176
  }
@@ -6,7 +6,6 @@ import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from '
6
6
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
7
7
  import type { BlockHeader } from '@aztec/stdlib/tx';
8
8
 
9
- import type { AccessScopes } from '../access_scopes.js';
10
9
  import { NoteService } from '../notes/note_service.js';
11
10
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
12
11
  import type { NoteStore } from '../storage/note_store/note_store.js';
@@ -43,7 +42,7 @@ export async function syncState(
43
42
  contractAddress: AztecAddress,
44
43
  contractStore: ContractStore,
45
44
  functionToInvokeAfterSync: FunctionSelector | null,
46
- utilityExecutor: (privateSyncCall: FunctionCall, scopes: AccessScopes) => Promise<any>,
45
+ utilityExecutor: (privateSyncCall: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
47
46
  noteStore: NoteStore,
48
47
  aztecNode: AztecNode,
49
48
  anchorBlockHeader: BlockHeader,
@@ -60,7 +59,7 @@ export async function syncState(
60
59
  }
61
60
 
62
61
  const noteService = new NoteService(noteStore, aztecNode, anchorBlockHeader, jobId);
63
- const scopes: AccessScopes = [scope];
62
+ const scopes: AztecAddress[] = [scope];
64
63
 
65
64
  // Both sync_state and syncNoteNullifiers interact with the note store, but running them in parallel is safe
66
65
  // because note store is designed to handle concurrent operations.
@@ -1,9 +1,9 @@
1
1
  import type { FunctionCall } from '@aztec/stdlib/abi';
2
2
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
3
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
4
  import type { NoteDao } from '@aztec/stdlib/note';
4
5
  import type { ContractOverrides } from '@aztec/stdlib/tx';
5
6
 
6
- import type { AccessScopes } from '../access_scopes.js';
7
7
  import type { BlockSynchronizer } from '../block_synchronizer/block_synchronizer.js';
8
8
  import type { ContractFunctionSimulator } from '../contract_function_simulator/contract_function_simulator.js';
9
9
  import type { ContractSyncService } from '../contract_sync/contract_sync_service.js';
@@ -22,7 +22,7 @@ export class PXEDebugUtils {
22
22
  contractFunctionSimulator: ContractFunctionSimulator,
23
23
  call: FunctionCall,
24
24
  authWitnesses: AuthWitness[] | undefined,
25
- scopes: AccessScopes,
25
+ scopes: AztecAddress[],
26
26
  jobId: string,
27
27
  ) => Promise<any>;
28
28
 
@@ -41,7 +41,7 @@ export class PXEDebugUtils {
41
41
  contractFunctionSimulator: ContractFunctionSimulator,
42
42
  call: FunctionCall,
43
43
  authWitnesses: AuthWitness[] | undefined,
44
- scopes: AccessScopes,
44
+ scopes: AztecAddress[],
45
45
  jobId: string,
46
46
  ) => Promise<any>,
47
47
  ) {
@@ -1,4 +1,3 @@
1
- export * from '../../../access_scopes.js';
2
1
  export * from '../../../notes_filter.js';
3
2
  export * from '../../../pxe.js';
4
3
  export * from '../../../config/index.js';
@@ -1,4 +1,3 @@
1
- import { BBPrivateKernelProver } from '@aztec/bb-prover/client';
2
1
  import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { createStore } from '@aztec/kv-store/indexeddb';
@@ -9,7 +8,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
9
8
  import type { PXEConfig } from '../../../config/index.js';
10
9
  import { PXE } from '../../../pxe.js';
11
10
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
12
- import type { PXECreationOptions } from '../../pxe_creation_options.js';
11
+ import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
13
12
 
14
13
  /**
15
14
  * Create and start an PXE instance with the given AztecNode.
@@ -44,7 +43,7 @@ export async function createPXE(
44
43
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
45
44
 
46
45
  let prover;
47
- if (options.proverOrOptions instanceof BBPrivateKernelProver) {
46
+ if (isPrivateKernelProver(options.proverOrOptions)) {
48
47
  prover = options.proverOrOptions;
49
48
  } else {
50
49
  prover = new BBBundlePrivateKernelProver(simulator, { ...options.proverOrOptions, logger: proverLogger });
@@ -1,4 +1,3 @@
1
- export * from '../../../access_scopes.js';
2
1
  export * from '../../../notes_filter.js';
3
2
  export * from '../../../pxe.js';
4
3
  export * from '../../../config/index.js';
@@ -1,4 +1,3 @@
1
- import { BBPrivateKernelProver } from '@aztec/bb-prover/client';
2
1
  import { BBLazyPrivateKernelProver } from '@aztec/bb-prover/client/lazy';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { createStore } from '@aztec/kv-store/indexeddb';
@@ -9,7 +8,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
9
8
  import type { PXEConfig } from '../../../config/index.js';
10
9
  import { PXE } from '../../../pxe.js';
11
10
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
12
- import type { PXECreationOptions } from '../../pxe_creation_options.js';
11
+ import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
13
12
 
14
13
  /**
15
14
  * Create and start an PXE instance with the given AztecNode.
@@ -44,7 +43,7 @@ export async function createPXE(
44
43
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
45
44
 
46
45
  let prover;
47
- if (options.proverOrOptions instanceof BBPrivateKernelProver) {
46
+ if (isPrivateKernelProver(options.proverOrOptions)) {
48
47
  prover = options.proverOrOptions;
49
48
  } else {
50
49
  prover = new BBLazyPrivateKernelProver(simulator, { ...options.proverOrOptions, logger: proverLogger });
@@ -12,3 +12,10 @@ export type PXECreationOptions = {
12
12
  store?: AztecAsyncKVStore;
13
13
  simulator?: CircuitSimulator;
14
14
  };
15
+
16
+ /** Checks if the given value implements the PrivateKernelProver interface via duck-typing. */
17
+ export function isPrivateKernelProver(value: unknown): value is PrivateKernelProver {
18
+ return (
19
+ typeof value === 'object' && value !== null && typeof (value as PrivateKernelProver).createChonkProof === 'function'
20
+ );
21
+ }
@@ -1,4 +1,3 @@
1
- export * from '../../access_scopes.js';
2
1
  export * from '../../notes_filter.js';
3
2
  export * from '../../pxe.js';
4
3
  export * from '../../config/index.js';
@@ -6,7 +5,7 @@ export * from '../../error_enriching.js';
6
5
  export * from '../../storage/index.js';
7
6
  export * from './utils.js';
8
7
  export { NoteService } from '../../notes/note_service.js';
9
- export { ORACLE_VERSION } from '../../oracle_version.js';
8
+ export { ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR } from '../../oracle_version.js';
10
9
  export { type PXECreationOptions } from '../pxe_creation_options.js';
11
10
  export { JobCoordinator } from '../../job_coordinator/job_coordinator.js';
12
11
  export { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
@@ -1,4 +1,3 @@
1
- import { BBPrivateKernelProver } from '@aztec/bb-prover/client';
2
1
  import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { createStore } from '@aztec/kv-store/lmdb-v2';
@@ -10,7 +9,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
10
9
  import type { PXEConfig } from '../../config/index.js';
11
10
  import { PXE } from '../../pxe.js';
12
11
  import { PXE_DATA_SCHEMA_VERSION } from '../../storage/index.js';
13
- import type { PXECreationOptions } from '../pxe_creation_options.js';
12
+ import { type PXECreationOptions, isPrivateKernelProver } from '../pxe_creation_options.js';
14
13
 
15
14
  type PXEConfigWithoutDefaults = Omit<PXEConfig, 'l1Contracts' | 'l1ChainId' | 'l2BlockBatchSize' | 'rollupVersion'>;
16
15
 
@@ -49,7 +48,7 @@ export async function createPXE(
49
48
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:native', { actor });
50
49
 
51
50
  let prover;
52
- if (options.proverOrOptions instanceof BBPrivateKernelProver) {
51
+ if (isPrivateKernelProver(options.proverOrOptions)) {
53
52
  prover = options.proverOrOptions;
54
53
  } else {
55
54
  prover = new BBBundlePrivateKernelProver(simulator, { ...options.proverOrOptions, logger: proverLogger });
@@ -2,7 +2,7 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
  import type { EventSelector } from '@aztec/stdlib/abi';
4
4
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
5
- import { siloNullifier } from '@aztec/stdlib/hash';
5
+ import { computePrivateEventCommitment, siloNullifier } from '@aztec/stdlib/hash';
6
6
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
7
7
  import type { BlockHeader, TxHash } from '@aztec/stdlib/tx';
8
8
 
@@ -26,6 +26,18 @@ export class EventService {
26
26
  txHash: TxHash,
27
27
  scope: AztecAddress,
28
28
  ): Promise<void> {
29
+ // Defense-in-depth: the built-in private-event path derives this commitment from content before enqueueing, but
30
+ // unconstrained PXE-side code (e.g. a custom message handler) can reach this oracle with arbitrary
31
+ // (content, commitment) pairs. Without this check it could bind arbitrary content to a legitimate tx nullifier,
32
+ // causing PXE to surface fabricated event data.
33
+ const recomputedCommitment = await computePrivateEventCommitment(randomness, selector.toField(), content);
34
+ if (!recomputedCommitment.equals(eventCommitment)) {
35
+ this.log.warn(
36
+ `Skipping event whose content does not hash to the provided commitment. contract=${contractAddress}, selector=${selector}, eventCommitment=${eventCommitment}, txHash=${txHash}, recomputedCommitment=${recomputedCommitment}`,
37
+ );
38
+ return;
39
+ }
40
+
29
41
  // While using 'latest' block number would be fine for private events since they cannot be accessed from Aztec.nr
30
42
  // (and thus we're less concerned about being ahead of the synced block), we use the synced block number to
31
43
  // maintain consistent behavior in the PXE. Additionally, events should never be ahead of the synced block here
@@ -1,11 +1,14 @@
1
1
  import type { PrivateEventFilter } from '@aztec/aztec.js/wallet';
2
2
  import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
3
3
  import { BlockNumber } from '@aztec/foundation/branded-types';
4
+ import { createLogger } from '@aztec/foundation/log';
4
5
 
5
6
  import type { PrivateEventStoreFilter } from '../storage/private_event_store/private_event_store.js';
6
7
 
7
8
  export class PrivateEventFilterValidator {
8
- constructor(private lastBlock: BlockNumber) {}
9
+ private readonly log = createLogger('pxe:private_event_filter_validator');
10
+
11
+ constructor(private readonly lastBlock: BlockNumber) {}
9
12
 
10
13
  validate(filter: PrivateEventFilter): PrivateEventStoreFilter {
11
14
  let { fromBlock, toBlock } = filter;
@@ -35,6 +38,23 @@ export class PrivateEventFilterValidator {
35
38
  throw new Error('toBlock must be strictly greater than fromBlock');
36
39
  }
37
40
 
41
+ // Cap the requested range to the synced block range. Without this, callers that pass a large
42
+ // toBlock (e.g. Number.MAX_SAFE_INTEGER as a "give me everything" idiom) would silently receive
43
+ // only the events that happen to be synced and believe they have complete coverage.
44
+ // We warn + cap rather than throw so callers don't need to query the last synced block before
45
+ // every request (which would also be unreliable, as the block can advance between the two calls).
46
+ const syncedUpperBound = BlockNumber(this.lastBlock + 1);
47
+ if (fromBlock >= syncedUpperBound) {
48
+ this.log.warn(
49
+ `Requested fromBlock ${fromBlock} is past last synced block ${this.lastBlock}; no events will be returned until PXE syncs further.`,
50
+ );
51
+ } else if (toBlock > syncedUpperBound) {
52
+ this.log.warn(
53
+ `Requested toBlock ${toBlock} exceeds last synced block ${this.lastBlock}; capping to ${syncedUpperBound}. Retry once PXE is further synced for complete coverage.`,
54
+ );
55
+ toBlock = syncedUpperBound;
56
+ }
57
+
38
58
  return {
39
59
  contractAddress: filter.contractAddress,
40
60
  scopes: filter.scopes,
@@ -1,21 +1,14 @@
1
- import type { Fr } from '@aztec/foundation/curves/bn254';
2
1
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
2
  import type { KeyStore } from '@aztec/key-store';
4
3
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
4
+ import type { L2TipsProvider } from '@aztec/stdlib/block';
5
5
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
6
- import {
7
- ExtendedDirectionalAppTaggingSecret,
8
- PendingTaggedLog,
9
- SiloedTag,
10
- Tag,
11
- TxScopedL2Log,
12
- } from '@aztec/stdlib/logs';
6
+ import { ExtendedDirectionalAppTaggingSecret, PendingTaggedLog, SiloedTag, Tag } from '@aztec/stdlib/logs';
13
7
  import type { BlockHeader } from '@aztec/stdlib/tx';
14
8
 
15
9
  import type { LogRetrievalRequest } from '../contract_function_simulator/noir-structs/log_retrieval_request.js';
16
10
  import { LogRetrievalResponse } from '../contract_function_simulator/noir-structs/log_retrieval_response.js';
17
11
  import { AddressStore } from '../storage/address_store/address_store.js';
18
- import type { CapsuleService } from '../storage/capsule_store/capsule_service.js';
19
12
  import type { RecipientTaggingStore } from '../storage/tagging_store/recipient_tagging_store.js';
20
13
  import type { SenderAddressBookStore } from '../storage/tagging_store/sender_address_book_store.js';
21
14
  import {
@@ -30,8 +23,8 @@ export class LogService {
30
23
  constructor(
31
24
  private readonly aztecNode: AztecNode,
32
25
  private readonly anchorBlockHeader: BlockHeader,
26
+ private readonly l2TipsStore: L2TipsProvider,
33
27
  private readonly keyStore: KeyStore,
34
- private readonly capsuleService: CapsuleService,
35
28
  private readonly recipientTaggingStore: RecipientTaggingStore,
36
29
  private readonly senderAddressBookStore: SenderAddressBookStore,
37
30
  private readonly addressStore: AddressStore,
@@ -120,17 +113,15 @@ export class LogService {
120
113
  );
121
114
  }
122
115
 
123
- public async fetchTaggedLogs(
124
- contractAddress: AztecAddress,
125
- pendingTaggedLogArrayBaseSlot: Fr,
126
- recipient: AztecAddress,
127
- ) {
116
+ public async fetchTaggedLogs(contractAddress: AztecAddress, recipient: AztecAddress): Promise<PendingTaggedLog[]> {
128
117
  this.log.verbose(`Fetching tagged logs for ${contractAddress.toString()}`);
129
118
 
130
119
  // We only load logs from block up to and including the anchor block number
131
120
  const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
132
121
  const anchorBlockHash = await this.anchorBlockHeader.hash();
133
122
 
123
+ const l2Tips = await this.l2TipsStore.getL2Tips();
124
+ const currentTimestamp = this.anchorBlockHeader.globalVariables.timestamp;
134
125
  // Get all secrets for this recipient (one per sender)
135
126
  const secrets = await this.#getSecretsForSenders(contractAddress, recipient);
136
127
 
@@ -143,17 +134,19 @@ export class LogService {
143
134
  this.recipientTaggingStore,
144
135
  anchorBlockNumber,
145
136
  anchorBlockHash,
137
+ currentTimestamp,
138
+ l2Tips.finalized.block.number,
146
139
  this.jobId,
147
140
  ),
148
141
  ),
149
142
  );
150
143
 
151
- // Flatten all logs from all secrets
152
- const allLogs = logArrays.flat();
153
-
154
- if (allLogs.length > 0) {
155
- await this.#storePendingTaggedLogs(contractAddress, pendingTaggedLogArrayBaseSlot, recipient, allLogs);
156
- }
144
+ return logArrays
145
+ .flat()
146
+ .map(
147
+ scopedLog =>
148
+ new PendingTaggedLog(scopedLog.logData, scopedLog.txHash, scopedLog.noteHashes, scopedLog.firstNullifier),
149
+ );
157
150
  }
158
151
 
159
152
  async #getSecretsForSenders(
@@ -176,43 +169,24 @@ export class LogService {
176
169
  );
177
170
 
178
171
  return Promise.all(
179
- deduplicatedSenders.map(sender => {
180
- return ExtendedDirectionalAppTaggingSecret.compute(
172
+ deduplicatedSenders.map(async sender => {
173
+ const secret = await ExtendedDirectionalAppTaggingSecret.compute(
181
174
  recipientCompleteAddress,
182
175
  recipientIvsk,
183
176
  sender,
184
177
  contractAddress,
185
178
  recipient,
186
179
  );
187
- }),
188
- );
189
- }
190
180
 
191
- #storePendingTaggedLogs(
192
- contractAddress: AztecAddress,
193
- capsuleArrayBaseSlot: Fr,
194
- recipient: AztecAddress,
195
- privateLogs: TxScopedL2Log[],
196
- ) {
197
- // Build all pending tagged logs from the scoped logs
198
- const pendingTaggedLogs = privateLogs.map(scopedLog => {
199
- const pendingTaggedLog = new PendingTaggedLog(
200
- scopedLog.logData,
201
- scopedLog.txHash,
202
- scopedLog.noteHashes,
203
- scopedLog.firstNullifier,
204
- );
205
-
206
- return pendingTaggedLog.toFields();
207
- });
181
+ if (!secret) {
182
+ // Note that all senders originate from either the SenderAddressBookStore or the KeyStore.
183
+ throw new Error(
184
+ `Failed to compute a tagging secret for sender ${sender} - this implies this is an invalid address, which should not happen as they have been previously registered in PXE.`,
185
+ );
186
+ }
208
187
 
209
- // TODO: This looks like it could belong more at the oracle interface level
210
- return this.capsuleService.appendToCapsuleArray(
211
- contractAddress,
212
- capsuleArrayBaseSlot,
213
- pendingTaggedLogs,
214
- this.jobId,
215
- recipient,
188
+ return secret;
189
+ }),
216
190
  );
217
191
  }
218
192
  }
@@ -7,7 +7,6 @@ import { Note, NoteDao, NoteStatus } from '@aztec/stdlib/note';
7
7
  import { MerkleTreeId } from '@aztec/stdlib/trees';
8
8
  import type { BlockHeader, TxHash } from '@aztec/stdlib/tx';
9
9
 
10
- import type { AccessScopes } from '../access_scopes.js';
11
10
  import type { NoteStore } from '../storage/note_store/note_store.js';
12
11
 
13
12
  export class NoteService {
@@ -32,7 +31,7 @@ export class NoteService {
32
31
  owner: AztecAddress | undefined,
33
32
  storageSlot: Fr,
34
33
  status: NoteStatus,
35
- scopes: AccessScopes,
34
+ scopes: AztecAddress[],
36
35
  ) {
37
36
  const noteDaos = await this.noteStore.getNotes(
38
37
  {
@@ -71,7 +70,7 @@ export class NoteService {
71
70
  *
72
71
  * @param contractAddress - The contract whose notes should be checked and nullified.
73
72
  */
74
- public async syncNoteNullifiers(contractAddress: AztecAddress, scopes: AccessScopes): Promise<void> {
73
+ public async syncNoteNullifiers(contractAddress: AztecAddress, scopes: AztecAddress[]): Promise<void> {
75
74
  const anchorBlockHash = await this.anchorBlockHeader.hash();
76
75
 
77
76
  const contractNotes = await this.noteStore.getNotes({ contractAddress, scopes }, this.jobId);
@@ -2,8 +2,6 @@ import type { Fr } from '@aztec/foundation/curves/bn254';
2
2
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
3
  import type { NoteStatus } from '@aztec/stdlib/note';
4
4
 
5
- import type { AccessScopes } from './access_scopes.js';
6
-
7
5
  /**
8
6
  * A filter used to fetch notes.
9
7
  * @remarks This filter is applied as an intersection of all its params.
@@ -22,5 +20,5 @@ export type NotesFilter = {
22
20
  status?: NoteStatus;
23
21
  /** The siloed nullifier for the note. */
24
22
  siloedNullifier?: Fr;
25
- scopes: AccessScopes;
23
+ scopes: AztecAddress[];
26
24
  };