@aztec/pxe 0.0.1-commit.e588bc7e5 → 0.0.1-commit.e5a3663dd

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 (134) hide show
  1. package/dest/bin/check_oracle_version.js +4 -4
  2. package/dest/block_synchronizer/block_stream_source.d.ts +10 -0
  3. package/dest/block_synchronizer/block_stream_source.d.ts.map +1 -0
  4. package/dest/block_synchronizer/block_stream_source.js +37 -0
  5. package/dest/block_synchronizer/block_synchronizer.d.ts +6 -2
  6. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  7. package/dest/block_synchronizer/block_synchronizer.js +24 -10
  8. package/dest/config/index.d.ts +1 -1
  9. package/dest/config/index.d.ts.map +1 -1
  10. package/dest/config/index.js +7 -14
  11. package/dest/contract_function_simulator/contract_function_simulator.d.ts +4 -1
  12. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  13. package/dest/contract_function_simulator/contract_function_simulator.js +8 -5
  14. package/dest/contract_function_simulator/ephemeral_array_service.d.ts +28 -0
  15. package/dest/contract_function_simulator/ephemeral_array_service.d.ts.map +1 -0
  16. package/dest/contract_function_simulator/ephemeral_array_service.js +78 -0
  17. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +1 -1
  18. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +1 -1
  19. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.d.ts +1 -1
  20. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.js +1 -1
  21. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
  22. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -1
  23. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +1 -1
  24. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +1 -1
  25. package/dest/contract_function_simulator/oracle/interfaces.d.ts +15 -2
  26. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  27. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +1 -1
  28. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -1
  29. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +10 -1
  30. package/dest/contract_function_simulator/oracle/oracle.d.ts +14 -2
  31. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/oracle/oracle.js +105 -3
  33. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +10 -11
  34. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  35. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +21 -17
  36. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +34 -13
  37. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  38. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +149 -28
  39. package/dest/contract_function_simulator/pick_notes.d.ts +1 -1
  40. package/dest/contract_function_simulator/pick_notes.d.ts.map +1 -1
  41. package/dest/contract_function_simulator/pick_notes.js +20 -3
  42. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts +1 -1
  43. package/dest/contract_function_simulator/proxied_contract_data_source.d.ts.map +1 -1
  44. package/dest/contract_function_simulator/proxied_contract_data_source.js +3 -0
  45. package/dest/contract_sync/contract_sync_service.d.ts +1 -1
  46. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  47. package/dest/contract_sync/contract_sync_service.js +35 -23
  48. package/dest/entrypoints/client/bundle/utils.d.ts +2 -2
  49. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  50. package/dest/entrypoints/client/bundle/utils.js +2 -2
  51. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  52. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  53. package/dest/entrypoints/client/lazy/utils.js +2 -2
  54. package/dest/entrypoints/pxe_creation_options.d.ts +3 -1
  55. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  56. package/dest/entrypoints/pxe_creation_options.js +3 -1
  57. package/dest/entrypoints/server/index.d.ts +2 -2
  58. package/dest/entrypoints/server/index.d.ts.map +1 -1
  59. package/dest/entrypoints/server/index.js +1 -1
  60. package/dest/entrypoints/server/utils.d.ts +2 -2
  61. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  62. package/dest/entrypoints/server/utils.js +2 -2
  63. package/dest/events/event_service.d.ts +1 -1
  64. package/dest/events/event_service.d.ts.map +1 -1
  65. package/dest/events/event_service.js +10 -1
  66. package/dest/events/private_event_filter_validator.d.ts +3 -2
  67. package/dest/events/private_event_filter_validator.d.ts.map +1 -1
  68. package/dest/events/private_event_filter_validator.js +15 -0
  69. package/dest/logs/log_service.d.ts +6 -6
  70. package/dest/logs/log_service.d.ts.map +1 -1
  71. package/dest/logs/log_service.js +8 -20
  72. package/dest/oracle_version.d.ts +4 -3
  73. package/dest/oracle_version.d.ts.map +1 -1
  74. package/dest/oracle_version.js +20 -10
  75. package/dest/private_kernel/private_kernel_execution_prover.d.ts +1 -1
  76. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  77. package/dest/private_kernel/private_kernel_execution_prover.js +4 -7
  78. package/dest/private_kernel/private_kernel_oracle.d.ts +5 -5
  79. package/dest/private_kernel/private_kernel_oracle.d.ts.map +1 -1
  80. package/dest/private_kernel/private_kernel_oracle.js +12 -15
  81. package/dest/pxe.d.ts +16 -4
  82. package/dest/pxe.d.ts.map +1 -1
  83. package/dest/pxe.js +49 -20
  84. package/dest/storage/anchor_block_store/anchor_block_store.js +1 -1
  85. package/dest/storage/capsule_store/capsule_store.d.ts +1 -1
  86. package/dest/storage/capsule_store/capsule_store.d.ts.map +1 -1
  87. package/dest/storage/capsule_store/capsule_store.js +8 -5
  88. package/dest/storage/contract_store/contract_store.d.ts +1 -1
  89. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  90. package/dest/storage/contract_store/contract_store.js +4 -2
  91. package/dest/storage/private_event_store/private_event_store.d.ts +1 -1
  92. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  93. package/dest/storage/private_event_store/private_event_store.js +3 -0
  94. package/dest/storage/private_event_store/stored_private_event.js +1 -1
  95. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts +2 -2
  96. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.d.ts.map +1 -1
  97. package/dest/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.js +2 -16
  98. package/package.json +16 -16
  99. package/src/bin/check_oracle_version.ts +4 -4
  100. package/src/block_synchronizer/block_stream_source.ts +52 -0
  101. package/src/block_synchronizer/block_synchronizer.ts +27 -11
  102. package/src/config/index.ts +2 -8
  103. package/src/contract_function_simulator/contract_function_simulator.ts +8 -4
  104. package/src/contract_function_simulator/ephemeral_array_service.ts +110 -0
  105. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +1 -1
  106. package/src/contract_function_simulator/noir-structs/log_retrieval_request.ts +1 -1
  107. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -1
  108. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +1 -1
  109. package/src/contract_function_simulator/oracle/interfaces.ts +30 -1
  110. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +7 -1
  111. package/src/contract_function_simulator/oracle/oracle.ts +157 -3
  112. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +34 -20
  113. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +241 -55
  114. package/src/contract_function_simulator/pick_notes.ts +22 -3
  115. package/src/contract_function_simulator/proxied_contract_data_source.ts +8 -1
  116. package/src/contract_sync/contract_sync_service.ts +57 -51
  117. package/src/entrypoints/client/bundle/utils.ts +2 -3
  118. package/src/entrypoints/client/lazy/utils.ts +2 -3
  119. package/src/entrypoints/pxe_creation_options.ts +7 -0
  120. package/src/entrypoints/server/index.ts +1 -1
  121. package/src/entrypoints/server/utils.ts +2 -3
  122. package/src/events/event_service.ts +13 -1
  123. package/src/events/private_event_filter_validator.ts +21 -1
  124. package/src/logs/log_service.ts +14 -50
  125. package/src/oracle_version.ts +20 -10
  126. package/src/private_kernel/private_kernel_execution_prover.ts +4 -9
  127. package/src/private_kernel/private_kernel_oracle.ts +14 -14
  128. package/src/pxe.ts +86 -24
  129. package/src/storage/anchor_block_store/anchor_block_store.ts +1 -1
  130. package/src/storage/capsule_store/capsule_store.ts +15 -5
  131. package/src/storage/contract_store/contract_store.ts +8 -6
  132. package/src/storage/private_event_store/private_event_store.ts +4 -0
  133. package/src/storage/private_event_store/stored_private_event.ts +1 -1
  134. package/src/tagging/recipient_sync/load_private_logs_for_sender_recipient_pair.ts +5 -15
@@ -1,4 +1,5 @@
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';
@@ -9,6 +10,9 @@ import type { ContractStore } from '../storage/contract_store/contract_store.js'
9
10
  import type { NoteStore } from '../storage/note_store/note_store.js';
10
11
  import { syncState, verifyCurrentClassId } from './helpers.js';
11
12
 
13
+ /** Maximum number of scope syncs running concurrently across the PXE. */
14
+ const MAX_CONCURRENT_SCOPE_SYNCS = 5;
15
+
12
16
  /**
13
17
  * Service for syncing the private state of contracts and verifying that the PXE holds the current class artifact.
14
18
  * It uses a cache to avoid redundant sync operations - the cache is wiped when the anchor block changes.
@@ -26,6 +30,11 @@ export class ContractSyncService implements StagedStore {
26
30
  // Per-job excluded contract addresses - these contracts should not be synced.
27
31
  private excludedFromSync: Map<string, Set<string>> = new Map();
28
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
+
29
38
  constructor(
30
39
  private aztecNode: AztecNode,
31
40
  private contractStore: ContractStore,
@@ -59,15 +68,22 @@ export class ContractSyncService implements StagedStore {
59
68
  return;
60
69
  }
61
70
 
62
- this.#startSyncIfNeeded(contractAddress, scopes, scopesToSync =>
63
- this.#syncContract(
64
- contractAddress,
65
- functionToInvokeAfterSync,
66
- utilityExecutor,
67
- anchorBlockHeader,
68
- jobId,
69
- scopesToSync,
70
- ),
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
+ ),
71
87
  );
72
88
 
73
89
  await this.#awaitSync(contractAddress, scopes);
@@ -81,39 +97,6 @@ export class ContractSyncService implements StagedStore {
81
97
  scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
82
98
  }
83
99
 
84
- async #syncContract(
85
- contractAddress: AztecAddress,
86
- functionToInvokeAfterSync: FunctionSelector | null,
87
- utilityExecutor: (call: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
88
- anchorBlockHeader: BlockHeader,
89
- jobId: string,
90
- scopes: AztecAddress[],
91
- ): Promise<void> {
92
- this.log.debug(`Syncing contract ${contractAddress}`);
93
-
94
- await Promise.all([
95
- // Call sync_state sequentially for each scope address — each invocation synchronizes one account's private
96
- // state using scoped capsule arrays.
97
- (async () => {
98
- for (const scope of scopes) {
99
- await syncState(
100
- contractAddress,
101
- this.contractStore,
102
- functionToInvokeAfterSync,
103
- utilityExecutor,
104
- this.noteStore,
105
- this.aztecNode,
106
- anchorBlockHeader,
107
- jobId,
108
- scope,
109
- );
110
- }
111
- })(),
112
- verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
113
- ]);
114
- this.log.debug(`Contract ${contractAddress} synced`);
115
- }
116
-
117
100
  /** Clears sync cache. Called by BlockSynchronizer when anchor block changes. */
118
101
  wipe(): void {
119
102
  this.log.debug(`Wiping contract sync cache (${this.syncedContracts.size} entries)`);
@@ -138,22 +121,45 @@ export class ContractSyncService implements StagedStore {
138
121
  return !!this.excludedFromSync.get(jobId)?.has(contractAddress.toString());
139
122
  }
140
123
 
141
- /** 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
+ */
142
129
  #startSyncIfNeeded(
143
130
  contractAddress: AztecAddress,
144
131
  scopes: AztecAddress[],
145
- syncFn: (scopesToSync: AztecAddress[]) => Promise<void>,
132
+ verifyFn: () => Promise<void>,
133
+ syncScopeFn: (scope: AztecAddress) => Promise<void>,
146
134
  ): void {
147
135
  const scopesToSync = scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
148
- const keys = scopesToSync.map(scope => toKey(contractAddress, scope));
149
- if (keys.length === 0) {
136
+ if (scopesToSync.length === 0) {
150
137
  return;
151
138
  }
152
- const promise = syncFn(scopesToSync).catch(err => {
153
- keys.forEach(key => this.syncedContracts.delete(key));
154
- throw err;
155
- });
156
- keys.forEach(key => this.syncedContracts.set(key, promise));
139
+
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);
152
+ }
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();
162
+ }
157
163
  }
158
164
 
159
165
  /** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
@@ -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
- 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
+ }
@@ -5,7 +5,7 @@ export * from '../../error_enriching.js';
5
5
  export * from '../../storage/index.js';
6
6
  export * from './utils.js';
7
7
  export { NoteService } from '../../notes/note_service.js';
8
- export { ORACLE_VERSION } from '../../oracle_version.js';
8
+ export { ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR } from '../../oracle_version.js';
9
9
  export { type PXECreationOptions } from '../pxe_creation_options.js';
10
10
  export { JobCoordinator } from '../../job_coordinator/job_coordinator.js';
11
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(
@@ -187,7 +180,6 @@ export class LogService {
187
180
 
188
181
  if (!secret) {
189
182
  // Note that all senders originate from either the SenderAddressBookStore or the KeyStore.
190
- // TODO(F-512): make sure we actually prevent registering invalid senders.
191
183
  throw new Error(
192
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.`,
193
185
  );
@@ -197,32 +189,4 @@ export class LogService {
197
189
  }),
198
190
  );
199
191
  }
200
-
201
- #storePendingTaggedLogs(
202
- contractAddress: AztecAddress,
203
- capsuleArrayBaseSlot: Fr,
204
- recipient: AztecAddress,
205
- privateLogs: TxScopedL2Log[],
206
- ) {
207
- // Build all pending tagged logs from the scoped logs
208
- const pendingTaggedLogs = privateLogs.map(scopedLog => {
209
- const pendingTaggedLog = new PendingTaggedLog(
210
- scopedLog.logData,
211
- scopedLog.txHash,
212
- scopedLog.noteHashes,
213
- scopedLog.firstNullifier,
214
- );
215
-
216
- return pendingTaggedLog.toFields();
217
- });
218
-
219
- // TODO: This looks like it could belong more at the oracle interface level
220
- return this.capsuleService.appendToCapsuleArray(
221
- contractAddress,
222
- capsuleArrayBaseSlot,
223
- pendingTaggedLogs,
224
- this.jobId,
225
- recipient,
226
- );
227
- }
228
192
  }
@@ -1,12 +1,22 @@
1
- /// The ORACLE_VERSION constant is used to check that the oracle interface is in sync between PXE and Aztec.nr. We need
2
- /// to version the oracle interface to ensure that developers get a reasonable error message if they use incompatible
3
- /// versions of Aztec.nr and PXE. The Noir counterpart is in `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
1
+ /// The oracle version constants are used to check that the oracle interface is in sync between PXE and Aztec.nr.
2
+ /// We version the oracle interface as `major.minor` where:
3
+ /// - `major` = backward-breaking changes (must match exactly between PXE and Aztec.nr)
4
+ /// - `minor` = oracle additions (non-breaking; PXE minor >= contract minor)
4
5
  ///
5
- /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called
6
- /// and if the oracle version is incompatible an error is thrown.
7
- export const ORACLE_VERSION = 22;
6
+ /// The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
7
+ ///
8
+ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called.
9
+ /// If the major version is incompatible, an error is thrown immediately. The minor version is recorded by the PXE and
10
+ /// used to provide helpful error messages if a contract calls an oracle that doesn't exist. We don't throw immediately
11
+ /// if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency without actually
12
+ /// using any of the new oracles then there is no reason to throw.
13
+ export const ORACLE_VERSION_MAJOR = 22;
14
+ export const ORACLE_VERSION_MINOR = 2;
8
15
 
9
- /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes,
10
- /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in
11
- /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
12
- export const ORACLE_INTERFACE_HASH = '83f1de1a9741a34916fd58cf12b857d0bac90f74bf00751b20304301a3f5c8eb';
16
+ /// This hash is computed from the Oracle interface and is used to detect when that interface changes. When it does,
17
+ /// you need to either:
18
+ /// - increment `ORACLE_VERSION_MAJOR` and reset `ORACLE_VERSION_MINOR` to zero if the change is breaking, or
19
+ /// - increment only `ORACLE_VERSION_MINOR` if the change is additive (a new oracle was added).
20
+ ///
21
+ /// These constants must be kept in sync between this file and `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
22
+ export const ORACLE_INTERFACE_HASH = '193fe3f9fee6a84d26803e636c9746dd805a4f389d44a0618de75c2c5eb4912e';
@@ -33,6 +33,7 @@ import {
33
33
  } from '@aztec/stdlib/tx';
34
34
  import { VerificationKeyAsFields, VerificationKeyData, VkData } from '@aztec/stdlib/vks';
35
35
 
36
+ import { computeTxExpirationTimestamp } from './hints/compute_tx_expiration_timestamp.js';
36
37
  import { PrivateKernelResetPrivateInputsBuilder } from './hints/private_kernel_reset_private_inputs_builder.js';
37
38
  import type { PrivateKernelOracle } from './private_kernel_oracle.js';
38
39
 
@@ -267,15 +268,9 @@ export class PrivateKernelExecutionProver {
267
268
  // TODO: Enable padding once we better understand the final amounts to pad to.
268
269
  const paddedSideEffectAmounts = PaddedSideEffectAmounts.empty();
269
270
 
270
- // Use the aggregated expirationTimestamp set throughout the tx execution.
271
- // TODO: Call `computeTxExpirationTimestamp` to round the value down and reduce precision, improving privacy.
272
- const expirationTimestampUpperBound = previousKernelData.publicInputs.expirationTimestamp;
273
- const anchorBlockTimestamp = previousKernelData.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp;
274
- if (expirationTimestampUpperBound <= anchorBlockTimestamp) {
275
- throw new Error(
276
- `Include-by timestamp must be greater than the anchor block timestamp. Anchor block timestamp: ${anchorBlockTimestamp}. Include-by timestamp: ${expirationTimestampUpperBound}.`,
277
- );
278
- }
271
+ // Round the aggregated expirationTimestamp down to reduce precision and avoid leaking which private
272
+ // functions were called via their exact expiration offsets.
273
+ const expirationTimestampUpperBound = computeTxExpirationTimestamp(previousKernelData.publicInputs);
279
274
 
280
275
  const privateInputs = new PrivateKernelTailCircuitPrivateInputs(
281
276
  previousKernelData,
@@ -7,13 +7,13 @@ import { getVKIndex, getVKSiblingPath } from '@aztec/noir-protocol-circuits-type
7
7
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
8
8
  import type { FunctionSelector } from '@aztec/stdlib/abi';
9
9
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
10
- import { BlockHash } from '@aztec/stdlib/block';
11
10
  import { type ContractInstanceWithAddress, computeSaltedInitializationHash } from '@aztec/stdlib/contract';
12
11
  import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from '@aztec/stdlib/delayed-public-mutable';
13
12
  import { computePublicDataTreeLeafSlot } from '@aztec/stdlib/hash';
14
13
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
15
14
  import { UpdatedClassIdHints } from '@aztec/stdlib/kernel';
16
15
  import type { NullifierMembershipWitness } from '@aztec/stdlib/trees';
16
+ import type { BlockHeader } from '@aztec/stdlib/tx';
17
17
  import type { VerificationKeyAsFields } from '@aztec/stdlib/vks';
18
18
 
19
19
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
@@ -26,7 +26,7 @@ export class PrivateKernelOracle {
26
26
  private contractStore: ContractStore,
27
27
  private keyStore: KeyStore,
28
28
  private node: AztecNode,
29
- private blockHash: BlockHash,
29
+ private blockHeader: BlockHeader,
30
30
  ) {}
31
31
 
32
32
  /** Retrieves the preimage of a contract address from the registered contract instances db. */
@@ -80,22 +80,20 @@ export class PrivateKernelOracle {
80
80
  }
81
81
 
82
82
  /** Returns a membership witness with the sibling path and leaf index in our note hash tree. */
83
- getNoteHashMembershipWitness(noteHash: Fr): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
84
- return this.node.getNoteHashMembershipWitness(this.blockHash, noteHash);
83
+ async getNoteHashMembershipWitness(
84
+ noteHash: Fr,
85
+ ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
86
+ return this.node.getNoteHashMembershipWitness(await this.blockHeader.hash(), noteHash);
85
87
  }
86
88
 
87
89
  /** Returns a membership witness with the sibling path and leaf index in our nullifier indexed merkle tree. */
88
- getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
89
- return this.node.getNullifierMembershipWitness(this.blockHash, nullifier);
90
+ async getNullifierMembershipWitness(nullifier: Fr): Promise<NullifierMembershipWitness | undefined> {
91
+ return this.node.getNullifierMembershipWitness(await this.blockHeader.hash(), nullifier);
90
92
  }
91
93
 
92
94
  /** Returns the root of our note hash merkle tree. */
93
- async getNoteHashTreeRoot(): Promise<Fr> {
94
- const header = await this.node.getBlockHeader(this.blockHash);
95
- if (!header) {
96
- throw new Error(`No block header found for block hash ${this.blockHash}`);
97
- }
98
- return header.state.partial.noteHashTree.root;
95
+ getNoteHashTreeRoot(): Fr {
96
+ return this.blockHeader.state.partial.noteHashTree.root;
99
97
  }
100
98
 
101
99
  /**
@@ -126,14 +124,16 @@ export class PrivateKernelOracle {
126
124
  ProtocolContractAddress.ContractInstanceRegistry,
127
125
  delayedPublicMutableHashSlot,
128
126
  );
129
- const updatedClassIdWitness = await this.node.getPublicDataWitness(this.blockHash, hashLeafSlot);
127
+ const blockHash = await this.blockHeader.hash();
128
+
129
+ const updatedClassIdWitness = await this.node.getPublicDataWitness(blockHash, hashLeafSlot);
130
130
 
131
131
  if (!updatedClassIdWitness) {
132
132
  throw new Error(`No public data tree witness found for ${hashLeafSlot}`);
133
133
  }
134
134
 
135
135
  const readStorage = (storageSlot: Fr) =>
136
- this.node.getPublicStorageAt(this.blockHash, ProtocolContractAddress.ContractInstanceRegistry, storageSlot);
136
+ this.node.getPublicStorageAt(blockHash, ProtocolContractAddress.ContractInstanceRegistry, storageSlot);
137
137
  const delayedPublicMutableValues = await DelayedPublicMutableValues.readFromTree(
138
138
  delayedPublicMutableSlot,
139
139
  readStorage,