@aztec/pxe 0.0.1-commit.381b1a9 → 0.0.1-commit.3a4ae741b

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 (93) hide show
  1. package/dest/config/index.d.ts +2 -2
  2. package/dest/config/index.d.ts.map +1 -1
  3. package/dest/config/index.js +1 -1
  4. package/dest/config/package_info.js +1 -1
  5. package/dest/contract_function_simulator/contract_function_simulator.d.ts +9 -3
  6. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  7. package/dest/contract_function_simulator/contract_function_simulator.js +28 -6
  8. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts +5 -5
  9. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts.map +1 -1
  10. package/dest/contract_function_simulator/execution_tagging_index_cache.js +17 -9
  11. package/dest/contract_function_simulator/index.d.ts +2 -1
  12. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  13. package/dest/contract_function_simulator/index.js +1 -0
  14. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +2 -3
  15. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts.map +1 -1
  16. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +5 -4
  17. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
  18. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts.map +1 -1
  19. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -3
  20. package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts +16 -0
  21. package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts.map +1 -0
  22. package/dest/contract_function_simulator/noir-structs/message_tx_context.js +57 -0
  23. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +2 -4
  24. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts.map +1 -1
  25. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +3 -5
  26. package/dest/contract_function_simulator/oracle/interfaces.d.ts +45 -44
  27. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  28. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +9 -0
  29. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -0
  30. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +42 -0
  31. package/dest/contract_function_simulator/oracle/oracle.d.ts +45 -44
  32. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  33. package/dest/contract_function_simulator/oracle/oracle.js +163 -94
  34. package/dest/contract_function_simulator/oracle/private_execution.js +5 -3
  35. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +22 -47
  36. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  37. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +32 -72
  38. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +46 -32
  39. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  40. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +91 -40
  41. package/dest/contract_sync/contract_sync_service.d.ts +5 -3
  42. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  43. package/dest/contract_sync/contract_sync_service.js +47 -30
  44. package/dest/messages/message_context_service.d.ts +17 -0
  45. package/dest/messages/message_context_service.d.ts.map +1 -0
  46. package/dest/messages/message_context_service.js +36 -0
  47. package/dest/oracle_version.d.ts +2 -2
  48. package/dest/oracle_version.js +3 -3
  49. package/dest/pxe.d.ts +8 -4
  50. package/dest/pxe.d.ts.map +1 -1
  51. package/dest/pxe.js +41 -23
  52. package/dest/storage/metadata.d.ts +1 -1
  53. package/dest/storage/metadata.js +1 -1
  54. package/dest/storage/tagging_store/sender_tagging_store.d.ts +26 -25
  55. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  56. package/dest/storage/tagging_store/sender_tagging_store.js +141 -115
  57. package/dest/tagging/index.d.ts +2 -2
  58. package/dest/tagging/index.d.ts.map +1 -1
  59. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +1 -1
  60. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  61. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +10 -1
  62. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +4 -3
  63. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  64. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +20 -10
  65. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +2 -1
  66. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  67. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +24 -11
  68. package/package.json +16 -16
  69. package/src/config/index.ts +1 -1
  70. package/src/config/package_info.ts +1 -1
  71. package/src/contract_function_simulator/contract_function_simulator.ts +39 -7
  72. package/src/contract_function_simulator/execution_tagging_index_cache.ts +16 -11
  73. package/src/contract_function_simulator/index.ts +1 -0
  74. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +8 -5
  75. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -4
  76. package/src/contract_function_simulator/noir-structs/message_tx_context.ts +55 -0
  77. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +3 -6
  78. package/src/contract_function_simulator/oracle/interfaces.ts +50 -53
  79. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +135 -0
  80. package/src/contract_function_simulator/oracle/oracle.ts +176 -138
  81. package/src/contract_function_simulator/oracle/private_execution.ts +4 -4
  82. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +34 -91
  83. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +125 -48
  84. package/src/contract_sync/contract_sync_service.ts +67 -38
  85. package/src/messages/message_context_service.ts +45 -0
  86. package/src/oracle_version.ts +3 -3
  87. package/src/pxe.ts +59 -22
  88. package/src/storage/metadata.ts +1 -1
  89. package/src/storage/tagging_store/sender_tagging_store.ts +182 -135
  90. package/src/tagging/index.ts +1 -1
  91. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +19 -1
  92. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +26 -11
  93. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +19 -9
@@ -6,6 +6,7 @@ import { Point } from '@aztec/foundation/curves/grumpkin';
6
6
  import { LogLevels, type Logger, createLogger } from '@aztec/foundation/log';
7
7
  import type { MembershipWitness } from '@aztec/foundation/trees';
8
8
  import type { KeyStore } from '@aztec/key-store';
9
+ import { isProtocolContract } from '@aztec/protocol-contracts';
9
10
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
10
11
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
11
12
  import { BlockHash } from '@aztec/stdlib/block';
@@ -18,12 +19,14 @@ import { deriveEcdhSharedSecret } from '@aztec/stdlib/logs';
18
19
  import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
19
20
  import type { NoteStatus } from '@aztec/stdlib/note';
20
21
  import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
21
- import type { BlockHeader, Capsule } from '@aztec/stdlib/tx';
22
+ import type { BlockHeader, Capsule, OffchainEffect } from '@aztec/stdlib/tx';
22
23
 
23
24
  import type { AccessScopes } from '../../access_scopes.js';
24
25
  import { createContractLogger, logContractMessage } from '../../contract_logging.js';
26
+ import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
25
27
  import { EventService } from '../../events/event_service.js';
26
28
  import { LogService } from '../../logs/log_service.js';
29
+ import { MessageContextService } from '../../messages/message_context_service.js';
27
30
  import { NoteService } from '../../notes/note_service.js';
28
31
  import { ORACLE_VERSION } from '../../oracle_version.js';
29
32
  import type { AddressStore } from '../../storage/address_store/address_store.js';
@@ -36,6 +39,7 @@ import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_
36
39
  import { EventValidationRequest } from '../noir-structs/event_validation_request.js';
37
40
  import { LogRetrievalRequest } from '../noir-structs/log_retrieval_request.js';
38
41
  import { LogRetrievalResponse } from '../noir-structs/log_retrieval_response.js';
42
+ import { MessageTxContext } from '../noir-structs/message_tx_context.js';
39
43
  import { NoteValidationRequest } from '../noir-structs/note_validation_request.js';
40
44
  import { UtilityContext } from '../noir-structs/utility_context.js';
41
45
  import { pickNotes } from '../pick_notes.js';
@@ -58,6 +62,8 @@ export type UtilityExecutionOracleArgs = {
58
62
  senderAddressBookStore: SenderAddressBookStore;
59
63
  capsuleStore: CapsuleStore;
60
64
  privateEventStore: PrivateEventStore;
65
+ messageContextService: MessageContextService;
66
+ contractSyncService: ContractSyncService;
61
67
  jobId: string;
62
68
  log?: ReturnType<typeof createLogger>;
63
69
  scopes: AccessScopes;
@@ -71,6 +77,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
71
77
  isUtility = true as const;
72
78
 
73
79
  private contractLogger: Logger | undefined;
80
+ private offchainEffects: OffchainEffect[] = [];
74
81
 
75
82
  protected readonly contractAddress: AztecAddress;
76
83
  protected readonly authWitnesses: AuthWitness[];
@@ -85,8 +92,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
85
92
  protected readonly senderAddressBookStore: SenderAddressBookStore;
86
93
  protected readonly capsuleStore: CapsuleStore;
87
94
  protected readonly privateEventStore: PrivateEventStore;
95
+ protected readonly messageContextService: MessageContextService;
96
+ protected readonly contractSyncService: ContractSyncService;
88
97
  protected readonly jobId: string;
89
- protected log: ReturnType<typeof createLogger>;
98
+ protected logger: ReturnType<typeof createLogger>;
90
99
  protected readonly scopes: AccessScopes;
91
100
 
92
101
  constructor(args: UtilityExecutionOracleArgs) {
@@ -103,22 +112,39 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
103
112
  this.senderAddressBookStore = args.senderAddressBookStore;
104
113
  this.capsuleStore = args.capsuleStore;
105
114
  this.privateEventStore = args.privateEventStore;
115
+ this.messageContextService = args.messageContextService;
116
+ this.contractSyncService = args.contractSyncService;
106
117
  this.jobId = args.jobId;
107
- this.log = args.log ?? createLogger('simulator:client_view_context');
118
+ this.logger = args.log ?? createLogger('simulator:client_view_context');
108
119
  this.scopes = args.scopes;
109
120
  }
110
121
 
111
- public utilityAssertCompatibleOracleVersion(version: number): void {
122
+ public assertCompatibleOracleVersion(version: number): void {
123
+ // TODO(F-416): Remove this hack on v5 when protocol contracts are redeployed.
124
+ // Protocol contracts/canonical contracts shipped with committed bytecode that cannot be changed. Assert they use
125
+ // the expected pinned version or the current one. We want to allow for both the pinned and the current versions
126
+ // because we want this code to work with both the pinned and unpinned version since some branches do not have the
127
+ // pinned contracts (like e.g. next)
128
+ const LEGACY_ORACLE_VERSION = 12;
129
+ if (isProtocolContract(this.contractAddress)) {
130
+ if (version !== LEGACY_ORACLE_VERSION && version !== ORACLE_VERSION) {
131
+ throw new Error(
132
+ `Expected legacy oracle version ${LEGACY_ORACLE_VERSION} or current oracle version ${ORACLE_VERSION} for alpha payload contract at ${this.contractAddress}, got ${version}.`,
133
+ );
134
+ }
135
+ return;
136
+ }
137
+
112
138
  if (version !== ORACLE_VERSION) {
113
139
  throw new Error(`Incompatible oracle version. Expected version ${ORACLE_VERSION}, got ${version}.`);
114
140
  }
115
141
  }
116
142
 
117
- public utilityGetRandomField(): Fr {
143
+ public getRandomField(): Fr {
118
144
  return Fr.random();
119
145
  }
120
146
 
121
- public utilityGetUtilityContext(): UtilityContext {
147
+ public getUtilityContext(): UtilityContext {
122
148
  return new UtilityContext(this.anchorBlockHeader, this.contractAddress);
123
149
  }
124
150
 
@@ -129,7 +155,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
129
155
  * @throws If the keys are not registered in the key store.
130
156
  * @throws If scopes are defined and the account is not in the scopes.
131
157
  */
132
- public async utilityGetKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
158
+ public async getKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
133
159
  // If scopes are defined, check that the key belongs to an account in the scopes.
134
160
  if (this.scopes !== 'ALL_SCOPES' && this.scopes.length > 0) {
135
161
  let hasAccess = false;
@@ -152,7 +178,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
152
178
  * @param noteHash - The note hash to find in the note hash tree.
153
179
  * @returns The membership witness containing the leaf index and sibling path
154
180
  */
155
- public utilityGetNoteHashMembershipWitness(
181
+ public getNoteHashMembershipWitness(
156
182
  anchorBlockHash: BlockHash,
157
183
  noteHash: Fr,
158
184
  ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
@@ -170,7 +196,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
170
196
  * @param blockHash - The block hash to find in the archive tree.
171
197
  * @returns The membership witness containing the leaf index and sibling path
172
198
  */
173
- public utilityGetBlockHashMembershipWitness(
199
+ public getBlockHashMembershipWitness(
174
200
  anchorBlockHash: BlockHash,
175
201
  blockHash: BlockHash,
176
202
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
@@ -183,7 +209,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
183
209
  * @param nullifier - Nullifier we try to find witness for.
184
210
  * @returns The nullifier membership witness (if found).
185
211
  */
186
- public utilityGetNullifierMembershipWitness(
212
+ public getNullifierMembershipWitness(
187
213
  blockHash: BlockHash,
188
214
  nullifier: Fr,
189
215
  ): Promise<NullifierMembershipWitness | undefined> {
@@ -199,7 +225,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
199
225
  * list structure" of leaves and proving that a lower nullifier is pointing to a bigger next value than the nullifier
200
226
  * we are trying to prove non-inclusion for.
201
227
  */
202
- public utilityGetLowNullifierMembershipWitness(
228
+ public getLowNullifierMembershipWitness(
203
229
  blockHash: BlockHash,
204
230
  nullifier: Fr,
205
231
  ): Promise<NullifierMembershipWitness | undefined> {
@@ -212,7 +238,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
212
238
  * @param leafSlot - The slot of the public data tree to get the witness for.
213
239
  * @returns - The witness
214
240
  */
215
- public utilityGetPublicDataWitness(blockHash: BlockHash, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
241
+ public getPublicDataWitness(blockHash: BlockHash, leafSlot: Fr): Promise<PublicDataWitness | undefined> {
216
242
  return this.aztecNode.getPublicDataWitness(blockHash, leafSlot);
217
243
  }
218
244
 
@@ -221,7 +247,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
221
247
  * @param blockNumber - The number of a block of which to get the block header.
222
248
  * @returns Block extracted from a block with block number `blockNumber`.
223
249
  */
224
- public async utilityGetBlockHeader(blockNumber: BlockNumber): Promise<BlockHeader | undefined> {
250
+ public async getBlockHeader(blockNumber: BlockNumber): Promise<BlockHeader | undefined> {
225
251
  const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
226
252
  if (blockNumber > anchorBlockNumber) {
227
253
  throw new Error(`Block number ${blockNumber} is higher than current block ${anchorBlockNumber}`);
@@ -236,7 +262,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
236
262
  * @param account - The account address.
237
263
  * @returns The public keys and partial address, or `undefined` if the account is not registered.
238
264
  */
239
- public async utilityTryGetPublicKeysAndPartialAddress(
265
+ public async tryGetPublicKeysAndPartialAddress(
240
266
  account: AztecAddress,
241
267
  ): Promise<{ publicKeys: PublicKeys; partialAddress: PartialAddress } | undefined> {
242
268
  const completeAddress = await this.addressStore.getCompleteAddress(account);
@@ -262,11 +288,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
262
288
  * @param address - Address.
263
289
  * @returns A contract instance.
264
290
  */
265
- public utilityGetContractInstance(address: AztecAddress): Promise<ContractInstance> {
266
- return this.getContractInstance(address);
267
- }
268
-
269
- protected async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
291
+ public async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
270
292
  const instance = await this.contractStore.getContractInstance(address);
271
293
  if (!instance) {
272
294
  throw new Error(`No contract instance found for address ${address.toString()}`);
@@ -280,7 +302,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
280
302
  * @param messageHash - Hash of the message to authenticate.
281
303
  * @returns Authentication witness for the requested message hash.
282
304
  */
283
- public utilityGetAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
305
+ public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
284
306
  return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
285
307
  }
286
308
 
@@ -306,7 +328,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
306
328
  * @param status - The status of notes to fetch.
307
329
  * @returns Array of note data.
308
330
  */
309
- public async utilityGetNotes(
331
+ public async getNotes(
310
332
  owner: AztecAddress | undefined,
311
333
  storageSlot: Fr,
312
334
  numSelects: number,
@@ -346,7 +368,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
346
368
  * @param innerNullifier - The inner nullifier.
347
369
  * @returns A boolean indicating whether the nullifier exists in the tree or not.
348
370
  */
349
- public async utilityCheckNullifierExists(innerNullifier: Fr) {
371
+ public async checkNullifierExists(innerNullifier: Fr) {
350
372
  const [nullifier, anchorBlockHash] = await Promise.all([
351
373
  siloNullifier(this.contractAddress, innerNullifier!),
352
374
  this.anchorBlockHeader.hash(),
@@ -365,7 +387,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
365
387
  * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
366
388
  * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
367
389
  */
368
- public async utilityGetL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
390
+ public async getL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
369
391
  const [messageIndex, siblingPath] = await getNonNullifiedL1ToL2MessageWitness(
370
392
  this.aztecNode,
371
393
  contractAddress,
@@ -383,7 +405,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
383
405
  * @param startStorageSlot - The starting storage slot.
384
406
  * @param numberOfElements - Number of elements to read from the starting storage slot.
385
407
  */
386
- public async utilityStorageRead(
408
+ public async storageRead(
387
409
  blockHash: BlockHash,
388
410
  contractAddress: AztecAddress,
389
411
  startStorageSlot: Fr,
@@ -397,7 +419,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
397
419
  slots.map(storageSlot => this.aztecNode.getPublicStorageAt(blockHash, contractAddress, storageSlot)),
398
420
  );
399
421
 
400
- this.log.debug(
422
+ this.logger.debug(
401
423
  `Oracle storage read: slots=[${slots.map(slot => slot.toString()).join(', ')}] address=${contractAddress.toString()} values=[${values.join(', ')}]`,
402
424
  );
403
425
 
@@ -420,7 +442,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
420
442
  return this.contractLogger;
421
443
  }
422
444
 
423
- public async utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
445
+ public async log(level: number, message: string, fields: Fr[]): Promise<void> {
424
446
  if (!LogLevels[level]) {
425
447
  throw new Error(`Invalid log level: ${level}`);
426
448
  }
@@ -428,7 +450,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
428
450
  logContractMessage(logger, LogLevels[level], message, fields);
429
451
  }
430
452
 
431
- public async utilityFetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
453
+ public async fetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
432
454
  const logService = new LogService(
433
455
  this.aztecNode,
434
456
  this.anchorBlockHeader,
@@ -438,7 +460,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
438
460
  this.senderAddressBookStore,
439
461
  this.addressStore,
440
462
  this.jobId,
441
- this.log.getBindings(),
463
+ this.logger.getBindings(),
442
464
  );
443
465
 
444
466
  await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
@@ -454,10 +476,12 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
454
476
  * @param noteValidationRequestsArrayBaseSlot - The base slot of capsule array containing note validation requests.
455
477
  * @param eventValidationRequestsArrayBaseSlot - The base slot of capsule array containing event validation requests.
456
478
  */
457
- public async utilityValidateAndStoreEnqueuedNotesAndEvents(
479
+ public async validateAndStoreEnqueuedNotesAndEvents(
458
480
  contractAddress: AztecAddress,
459
481
  noteValidationRequestsArrayBaseSlot: Fr,
460
482
  eventValidationRequestsArrayBaseSlot: Fr,
483
+ maxNotePackedLen: number,
484
+ maxEventSerializedLen: number,
461
485
  ) {
462
486
  // TODO(#10727): allow other contracts to store notes
463
487
  if (!this.contractAddress.equals(contractAddress)) {
@@ -468,11 +492,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
468
492
  // faster as we don't need to wait for the network round-trip.
469
493
  const noteValidationRequests = (
470
494
  await this.capsuleStore.readCapsuleArray(contractAddress, noteValidationRequestsArrayBaseSlot, this.jobId)
471
- ).map(NoteValidationRequest.fromFields);
495
+ ).map(fields => NoteValidationRequest.fromFields(fields, maxNotePackedLen));
472
496
 
473
497
  const eventValidationRequests = (
474
498
  await this.capsuleStore.readCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, this.jobId)
475
- ).map(EventValidationRequest.fromFields);
499
+ ).map(fields => EventValidationRequest.fromFields(fields, maxEventSerializedLen));
476
500
 
477
501
  const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
478
502
  const noteStorePromises = noteValidationRequests.map(request =>
@@ -510,7 +534,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
510
534
  await this.capsuleStore.setCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, [], this.jobId);
511
535
  }
512
536
 
513
- public async utilityBulkRetrieveLogs(
537
+ public async bulkRetrieveLogs(
514
538
  contractAddress: AztecAddress,
515
539
  logRetrievalRequestsArrayBaseSlot: Fr,
516
540
  logRetrievalResponsesArrayBaseSlot: Fr,
@@ -535,7 +559,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
535
559
  this.senderAddressBookStore,
536
560
  this.addressStore,
537
561
  this.jobId,
538
- this.log.getBindings(),
562
+ this.logger.getBindings(),
539
563
  );
540
564
 
541
565
  const maybeLogRetrievalResponses = await logService.bulkRetrieveLogs(logRetrievalRequests);
@@ -552,7 +576,48 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
552
576
  );
553
577
  }
554
578
 
555
- public utilityStoreCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise<void> {
579
+ public async utilityResolveMessageContexts(
580
+ contractAddress: AztecAddress,
581
+ messageContextRequestsArrayBaseSlot: Fr,
582
+ messageContextResponsesArrayBaseSlot: Fr,
583
+ ) {
584
+ try {
585
+ if (!this.contractAddress.equals(contractAddress)) {
586
+ throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`);
587
+ }
588
+ const requestCapsules = await this.capsuleStore.readCapsuleArray(
589
+ contractAddress,
590
+ messageContextRequestsArrayBaseSlot,
591
+ this.jobId,
592
+ );
593
+
594
+ const txHashes = requestCapsules.map((fields, i) => {
595
+ if (fields.length !== 1) {
596
+ throw new Error(
597
+ `Malformed message context request at index ${i}: expected 1 field (tx hash), got ${fields.length}`,
598
+ );
599
+ }
600
+ return fields[0];
601
+ });
602
+
603
+ const maybeMessageContexts = await this.messageContextService.resolveMessageContexts(
604
+ txHashes,
605
+ this.anchorBlockHeader.getBlockNumber(),
606
+ );
607
+
608
+ // Leave response in response capsule array.
609
+ await this.capsuleStore.setCapsuleArray(
610
+ contractAddress,
611
+ messageContextResponsesArrayBaseSlot,
612
+ maybeMessageContexts.map(MessageTxContext.toSerializedOption),
613
+ this.jobId,
614
+ );
615
+ } finally {
616
+ await this.capsuleStore.setCapsuleArray(contractAddress, messageContextRequestsArrayBaseSlot, [], this.jobId);
617
+ }
618
+ }
619
+
620
+ public storeCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[]): Promise<void> {
556
621
  if (!contractAddress.equals(this.contractAddress)) {
557
622
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
558
623
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -561,7 +626,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
561
626
  return Promise.resolve();
562
627
  }
563
628
 
564
- public async utilityLoadCapsule(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
629
+ public async loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
565
630
  if (!contractAddress.equals(this.contractAddress)) {
566
631
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
567
632
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -573,7 +638,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
573
638
  );
574
639
  }
575
640
 
576
- public utilityDeleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise<void> {
641
+ public deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise<void> {
577
642
  if (!contractAddress.equals(this.contractAddress)) {
578
643
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
579
644
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -582,12 +647,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
582
647
  return Promise.resolve();
583
648
  }
584
649
 
585
- public utilityCopyCapsule(
586
- contractAddress: AztecAddress,
587
- srcSlot: Fr,
588
- dstSlot: Fr,
589
- numEntries: number,
590
- ): Promise<void> {
650
+ public copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
591
651
  if (!contractAddress.equals(this.contractAddress)) {
592
652
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
593
653
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -595,8 +655,19 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
595
655
  return this.capsuleStore.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries, this.jobId);
596
656
  }
597
657
 
658
+ /**
659
+ * Clears cached sync state for a contract for a set of scopes, forcing re-sync on the next query so that newly
660
+ * stored notes or events are discovered.
661
+ */
662
+ public invalidateContractSyncCache(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
663
+ if (!contractAddress.equals(this.contractAddress)) {
664
+ throw new Error(`Contract ${this.contractAddress} cannot invalidate sync cache of ${contractAddress}`);
665
+ }
666
+ this.contractSyncService.invalidateContractForScopes(contractAddress, scopes);
667
+ }
668
+
598
669
  // TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
599
- public utilityAes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
670
+ public aes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
600
671
  const aes128 = new Aes128();
601
672
  return aes128.decryptBufferCBC(ciphertext, iv, symKey);
602
673
  }
@@ -607,11 +678,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
607
678
  * @param ephPk - The ephemeral public key to get the secret for.
608
679
  * @returns The secret for the given address.
609
680
  */
610
- public utilityGetSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
611
- return this.getSharedSecret(address, ephPk);
612
- }
613
-
614
- protected async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
681
+ public async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
615
682
  // TODO(#12656): return an app-siloed secret
616
683
  const recipientCompleteAddress = await this.getCompleteAddressOrFail(address);
617
684
  const ivskM = await this.keyStore.getMasterSecretKey(
@@ -620,4 +687,14 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
620
687
  const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
621
688
  return deriveEcdhSharedSecret(addressSecret, ephPk);
622
689
  }
690
+
691
+ public emitOffchainEffect(data: Fr[]): Promise<void> {
692
+ this.offchainEffects.push({ data, contractAddress: this.contractAddress });
693
+ return Promise.resolve();
694
+ }
695
+
696
+ /** Returns offchain effects collected during execution. */
697
+ public getOffchainEffects(): OffchainEffect[] {
698
+ return this.offchainEffects;
699
+ }
623
700
  }
@@ -20,12 +20,12 @@ export class ContractSyncService implements StagedStore {
20
20
  readonly storeName = 'contract_sync';
21
21
 
22
22
  // Tracks contracts synced since last wipe. The cache is keyed per individual scope address
23
- // (`contractAddress:scopeAddress`), or `contractAddress:*` for undefined scopes (all accounts).
23
+ // (`contractAddress:scopeAddress`), or `contractAddress:*` for all scopes (all accounts).
24
24
  // The value is a promise that resolves when the contract is synced.
25
25
  private syncedContracts: Map<string, Promise<void>> = new Map();
26
26
 
27
- // Per-job overridden contract addresses - these contracts should not be synced.
28
- private overriddenContracts: Map<string, Set<string>> = new Map();
27
+ // Per-job excluded contract addresses - these contracts should not be synced.
28
+ private excludedFromSync: Map<string, Set<string>> = new Map();
29
29
 
30
30
  constructor(
31
31
  private aztecNode: AztecNode,
@@ -35,8 +35,8 @@ export class ContractSyncService implements StagedStore {
35
35
  ) {}
36
36
 
37
37
  /** Sets contracts that should be skipped during sync for a specific job. */
38
- setOverriddenContracts(jobId: string, addresses: Set<string>): void {
39
- this.overriddenContracts.set(jobId, addresses);
38
+ setExcludedFromSync(jobId: string, addresses: Set<string>): void {
39
+ this.excludedFromSync.set(jobId, addresses);
40
40
  }
41
41
 
42
42
  /**
@@ -56,47 +56,34 @@ export class ContractSyncService implements StagedStore {
56
56
  jobId: string,
57
57
  scopes: AccessScopes,
58
58
  ): Promise<void> {
59
- // Skip sync if this contract has an override for this job (overrides are keyed by contract address only)
60
- const overrides = this.overriddenContracts.get(jobId);
61
- if (overrides?.has(contractAddress.toString())) {
59
+ if (this.#shouldSkipSync(jobId, contractAddress)) {
62
60
  return;
63
61
  }
64
62
 
65
- // Skip sync if we already synced for "all scopes", or if we have an empty list of scopes
66
- const allScopesKey = toKey(contractAddress, 'ALL_SCOPES');
67
- const allScopesExisting = this.syncedContracts.get(allScopesKey);
68
- if (allScopesExisting || (scopes !== 'ALL_SCOPES' && scopes.length == 0)) {
69
- return;
70
- }
71
-
72
- const unsyncedScopes =
73
- scopes === 'ALL_SCOPES'
74
- ? scopes
75
- : scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
76
- const unsyncedScopesKeys = toKeys(contractAddress, unsyncedScopes);
77
-
78
- if (unsyncedScopesKeys.length > 0) {
79
- // Start sync and store the promise for all unsynced scopes
80
- const promise = this.#doSync(
63
+ this.#startSyncIfNeeded(contractAddress, scopes, scopesToSync =>
64
+ this.#syncContract(
81
65
  contractAddress,
82
66
  functionToInvokeAfterSync,
83
67
  utilityExecutor,
84
68
  anchorBlockHeader,
85
69
  jobId,
86
- unsyncedScopes,
87
- ).catch(err => {
88
- // There was an error syncing the contract, so we remove it from the cache so that it can be retried.
89
- unsyncedScopesKeys.forEach(key => this.syncedContracts.delete(key));
90
- throw err;
91
- });
92
- unsyncedScopesKeys.forEach(key => this.syncedContracts.set(key, promise));
93
- }
70
+ scopesToSync,
71
+ ),
72
+ );
94
73
 
95
- const promises = toKeys(contractAddress, scopes).map(key => this.syncedContracts.get(key)!);
96
- await Promise.all(promises);
74
+ await this.#awaitSync(contractAddress, scopes);
97
75
  }
98
76
 
99
- async #doSync(
77
+ /** Clears sync cache entries for the given scopes of a contract. Also clears the ALL_SCOPES entry. */
78
+ invalidateContractForScopes(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
79
+ if (scopes.length === 0) {
80
+ return;
81
+ }
82
+ scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
83
+ this.syncedContracts.delete(toKey(contractAddress, 'ALL_SCOPES'));
84
+ }
85
+
86
+ async #syncContract(
100
87
  contractAddress: AztecAddress,
101
88
  functionToInvokeAfterSync: FunctionSelector | null,
102
89
  utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
@@ -129,8 +116,8 @@ export class ContractSyncService implements StagedStore {
129
116
  }
130
117
 
131
118
  commit(jobId: string): Promise<void> {
132
- // Clear overridden contracts for this job
133
- this.overriddenContracts.delete(jobId);
119
+ // Clear excluded contracts for this job
120
+ this.excludedFromSync.delete(jobId);
134
121
  return Promise.resolve();
135
122
  }
136
123
 
@@ -138,9 +125,51 @@ export class ContractSyncService implements StagedStore {
138
125
  // We clear the synced contracts cache here because, when the job is discarded, any associated database writes from
139
126
  // the sync are also undone.
140
127
  this.syncedContracts.clear();
141
- this.overriddenContracts.delete(jobId);
128
+ this.excludedFromSync.delete(jobId);
142
129
  return Promise.resolve();
143
130
  }
131
+ /** Returns true if sync should be skipped for this contract */
132
+ #shouldSkipSync(jobId: string, contractAddress: AztecAddress): boolean {
133
+ return !!this.excludedFromSync.get(jobId)?.has(contractAddress.toString());
134
+ }
135
+
136
+ /** If there are unsynced scopes, starts sync and stores the promise in cache with error cleanup. */
137
+ #startSyncIfNeeded(
138
+ contractAddress: AztecAddress,
139
+ scopes: AccessScopes,
140
+ syncFn: (scopesToSync: AccessScopes) => Promise<void>,
141
+ ): void {
142
+ const scopesToSync = this.#getScopesToSync(contractAddress, scopes);
143
+ const keys = toKeys(contractAddress, scopesToSync);
144
+ if (keys.length === 0) {
145
+ return;
146
+ }
147
+ const promise = syncFn(scopesToSync).catch(err => {
148
+ keys.forEach(key => this.syncedContracts.delete(key));
149
+ throw err;
150
+ });
151
+ keys.forEach(key => this.syncedContracts.set(key, promise));
152
+ }
153
+
154
+ /** Filters out scopes that are already cached, returning only those that still need syncing. */
155
+ #getScopesToSync(contractAddress: AztecAddress, scopes: AccessScopes): AccessScopes {
156
+ if (this.syncedContracts.has(toKey(contractAddress, 'ALL_SCOPES'))) {
157
+ // If we are already syncing all scopes, then return an empty list
158
+ return [];
159
+ }
160
+ if (scopes === 'ALL_SCOPES') {
161
+ return 'ALL_SCOPES';
162
+ }
163
+ return scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
164
+ }
165
+
166
+ /** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
167
+ async #awaitSync(contractAddress: AztecAddress, scopes: AccessScopes): Promise<void> {
168
+ const promises = toKeys(contractAddress, scopes)
169
+ .map(key => this.syncedContracts.get(key))
170
+ .filter(p => p !== undefined);
171
+ await Promise.all(promises);
172
+ }
144
173
  }
145
174
 
146
175
  function toKeys(contract: AztecAddress, scopes: AccessScopes) {
@@ -0,0 +1,45 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+ import type { AztecNode } from '@aztec/stdlib/interfaces/server';
3
+ import { TxHash } from '@aztec/stdlib/tx';
4
+
5
+ import { MessageTxContext } from '../contract_function_simulator/noir-structs/message_tx_context.js';
6
+
7
+ /** Resolves transaction hashes into the context needed to process messages. */
8
+ export class MessageContextService {
9
+ constructor(private readonly aztecNode: AztecNode) {}
10
+
11
+ /**
12
+ * Resolves a list of tx hashes into their message contexts.
13
+ *
14
+ * For each tx hash, looks up the corresponding tx effect and extracts the note hashes and first nullifier needed to
15
+ * process messages that originated from that transaction. Returns `null` for tx hashes that are zero, not yet
16
+ * available, or in blocks beyond the anchor block.
17
+ */
18
+ resolveMessageContexts(txHashes: Fr[], anchorBlockNumber: number): Promise<(MessageTxContext | null)[]> {
19
+ // TODO: optimize, we might be hitting the node to get the same txHash repeatedly
20
+ return Promise.all(
21
+ txHashes.map(async txHashField => {
22
+ // A zero tx hash indicates a tx-less offchain message (e.g. one not tied to any onchain transaction).
23
+ // These messages don't have a transaction context to resolve, so we return null.
24
+ if (txHashField.isZero()) {
25
+ return null;
26
+ }
27
+
28
+ const txHash = TxHash.fromField(txHashField);
29
+ const txEffect = await this.aztecNode.getTxEffect(txHash);
30
+ if (!txEffect || txEffect.l2BlockNumber > anchorBlockNumber) {
31
+ return null;
32
+ }
33
+
34
+ // Every tx has at least one nullifier (the first nullifier derived from the tx hash). Hitting this condition
35
+ // would mean a buggy node, but since we need to access data.nullifiers[0], the defensive check does no harm.
36
+ const data = txEffect.data;
37
+ if (data.nullifiers.length === 0) {
38
+ throw new Error(`Tx effect for ${txHash} has no nullifiers`);
39
+ }
40
+
41
+ return new MessageTxContext(data.txHash, data.noteHashes, data.nullifiers[0]);
42
+ }),
43
+ );
44
+ }
45
+ }
@@ -2,11 +2,11 @@
2
2
  /// to version the oracle interface to ensure that developers get a reasonable error message if they use incompatible
3
3
  /// versions of Aztec.nr and PXE. The Noir counterpart is in `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
4
4
  ///
5
- /// @dev Whenever a contract function or Noir test is run, the `utilityAssertCompatibleOracleVersion` oracle is called
5
+ /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called
6
6
  /// and if the oracle version is incompatible an error is thrown.
7
- export const ORACLE_VERSION = 12;
7
+ export const ORACLE_VERSION = 18;
8
8
 
9
9
  /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes,
10
10
  /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in
11
11
  /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
12
- export const ORACLE_INTERFACE_HASH = '666a8a7fc697f72b29dbf0ae7464db269cf5afa019acac8861f814543147dbb4';
12
+ export const ORACLE_INTERFACE_HASH = '57e5b07c6d55fb167ef90f8d0f410f9bdb5b154a31159c624a061be40b02a2c2';