@aztec/pxe 0.0.1-commit.dbf9cec → 0.0.1-commit.e0f15ab9b

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 (91) 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/contract_function_simulator/contract_function_simulator.d.ts +9 -3
  5. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  6. package/dest/contract_function_simulator/contract_function_simulator.js +28 -6
  7. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts +5 -5
  8. package/dest/contract_function_simulator/execution_tagging_index_cache.d.ts.map +1 -1
  9. package/dest/contract_function_simulator/execution_tagging_index_cache.js +17 -9
  10. package/dest/contract_function_simulator/index.d.ts +2 -1
  11. package/dest/contract_function_simulator/index.d.ts.map +1 -1
  12. package/dest/contract_function_simulator/index.js +1 -0
  13. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +2 -3
  14. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts.map +1 -1
  15. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +5 -4
  16. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
  17. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts.map +1 -1
  18. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -3
  19. package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts +16 -0
  20. package/dest/contract_function_simulator/noir-structs/message_tx_context.d.ts.map +1 -0
  21. package/dest/contract_function_simulator/noir-structs/message_tx_context.js +57 -0
  22. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +2 -4
  23. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts.map +1 -1
  24. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +3 -5
  25. package/dest/contract_function_simulator/oracle/interfaces.d.ts +50 -45
  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 +9 -0
  28. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -0
  29. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +42 -0
  30. package/dest/contract_function_simulator/oracle/oracle.d.ts +45 -44
  31. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  32. package/dest/contract_function_simulator/oracle/oracle.js +163 -94
  33. package/dest/contract_function_simulator/oracle/private_execution.js +5 -3
  34. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +22 -47
  35. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  36. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +30 -62
  37. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +53 -35
  38. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  39. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +101 -43
  40. package/dest/contract_sync/contract_sync_service.d.ts +5 -3
  41. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  42. package/dest/contract_sync/contract_sync_service.js +47 -30
  43. package/dest/messages/message_context_service.d.ts +17 -0
  44. package/dest/messages/message_context_service.d.ts.map +1 -0
  45. package/dest/messages/message_context_service.js +36 -0
  46. package/dest/oracle_version.d.ts +2 -2
  47. package/dest/oracle_version.js +3 -3
  48. package/dest/pxe.d.ts +8 -4
  49. package/dest/pxe.d.ts.map +1 -1
  50. package/dest/pxe.js +39 -22
  51. package/dest/storage/metadata.d.ts +1 -1
  52. package/dest/storage/metadata.js +1 -1
  53. package/dest/storage/tagging_store/sender_tagging_store.d.ts +26 -25
  54. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  55. package/dest/storage/tagging_store/sender_tagging_store.js +141 -115
  56. package/dest/tagging/index.d.ts +2 -2
  57. package/dest/tagging/index.d.ts.map +1 -1
  58. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts +1 -1
  59. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.d.ts.map +1 -1
  60. package/dest/tagging/sender_sync/sync_sender_tagging_indexes.js +10 -1
  61. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts +4 -3
  62. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.d.ts.map +1 -1
  63. package/dest/tagging/sender_sync/utils/get_status_change_of_pending.js +20 -10
  64. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts +2 -1
  65. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.d.ts.map +1 -1
  66. package/dest/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.js +24 -11
  67. package/package.json +16 -16
  68. package/src/config/index.ts +1 -1
  69. package/src/contract_function_simulator/contract_function_simulator.ts +39 -7
  70. package/src/contract_function_simulator/execution_tagging_index_cache.ts +16 -11
  71. package/src/contract_function_simulator/index.ts +1 -0
  72. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +8 -5
  73. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -4
  74. package/src/contract_function_simulator/noir-structs/message_tx_context.ts +55 -0
  75. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +3 -6
  76. package/src/contract_function_simulator/oracle/interfaces.ts +54 -54
  77. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +135 -0
  78. package/src/contract_function_simulator/oracle/oracle.ts +176 -138
  79. package/src/contract_function_simulator/oracle/private_execution.ts +4 -4
  80. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +32 -80
  81. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +136 -53
  82. package/src/contract_sync/contract_sync_service.ts +67 -38
  83. package/src/messages/message_context_service.ts +45 -0
  84. package/src/oracle_version.ts +3 -3
  85. package/src/pxe.ts +57 -21
  86. package/src/storage/metadata.ts +1 -1
  87. package/src/storage/tagging_store/sender_tagging_store.ts +182 -135
  88. package/src/tagging/index.ts +1 -1
  89. package/src/tagging/sender_sync/sync_sender_tagging_indexes.ts +19 -1
  90. package/src/tagging/sender_sync/utils/get_status_change_of_pending.ts +26 -11
  91. package/src/tagging/sender_sync/utils/load_and_store_new_tagging_indexes.ts +19 -9
@@ -6,24 +6,27 @@ 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';
12
- import type { CompleteAddress, ContractInstance } from '@aztec/stdlib/contract';
13
+ import type { CompleteAddress, ContractInstance, PartialAddress } from '@aztec/stdlib/contract';
13
14
  import { siloNullifier } from '@aztec/stdlib/hash';
14
15
  import type { AztecNode } from '@aztec/stdlib/interfaces/server';
15
16
  import type { KeyValidationRequest } from '@aztec/stdlib/kernel';
16
- import { computeAddressSecret } from '@aztec/stdlib/keys';
17
+ import { type PublicKeys, computeAddressSecret } from '@aztec/stdlib/keys';
17
18
  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}`);
@@ -232,12 +258,18 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
232
258
  }
233
259
 
234
260
  /**
235
- * Retrieve the complete address associated to a given address.
261
+ * Retrieve the public keys and partial address associated to a given address.
236
262
  * @param account - The account address.
237
- * @returns A complete address associated with the input address, or `undefined` if not registered.
263
+ * @returns The public keys and partial address, or `undefined` if the account is not registered.
238
264
  */
239
- public utilityTryGetPublicKeysAndPartialAddress(account: AztecAddress): Promise<CompleteAddress | undefined> {
240
- return this.addressStore.getCompleteAddress(account);
265
+ public async tryGetPublicKeysAndPartialAddress(
266
+ account: AztecAddress,
267
+ ): Promise<{ publicKeys: PublicKeys; partialAddress: PartialAddress } | undefined> {
268
+ const completeAddress = await this.addressStore.getCompleteAddress(account);
269
+ if (!completeAddress) {
270
+ return undefined;
271
+ }
272
+ return { publicKeys: completeAddress.publicKeys, partialAddress: completeAddress.partialAddress };
241
273
  }
242
274
 
243
275
  protected async getCompleteAddressOrFail(account: AztecAddress): Promise<CompleteAddress> {
@@ -256,11 +288,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
256
288
  * @param address - Address.
257
289
  * @returns A contract instance.
258
290
  */
259
- public utilityGetContractInstance(address: AztecAddress): Promise<ContractInstance> {
260
- return this.getContractInstance(address);
261
- }
262
-
263
- protected async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
291
+ public async getContractInstance(address: AztecAddress): Promise<ContractInstance> {
264
292
  const instance = await this.contractStore.getContractInstance(address);
265
293
  if (!instance) {
266
294
  throw new Error(`No contract instance found for address ${address.toString()}`);
@@ -274,7 +302,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
274
302
  * @param messageHash - Hash of the message to authenticate.
275
303
  * @returns Authentication witness for the requested message hash.
276
304
  */
277
- public utilityGetAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
305
+ public getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined> {
278
306
  return Promise.resolve(this.authWitnesses.find(w => w.requestHash.equals(messageHash))?.witness);
279
307
  }
280
308
 
@@ -300,7 +328,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
300
328
  * @param status - The status of notes to fetch.
301
329
  * @returns Array of note data.
302
330
  */
303
- public async utilityGetNotes(
331
+ public async getNotes(
304
332
  owner: AztecAddress | undefined,
305
333
  storageSlot: Fr,
306
334
  numSelects: number,
@@ -340,7 +368,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
340
368
  * @param innerNullifier - The inner nullifier.
341
369
  * @returns A boolean indicating whether the nullifier exists in the tree or not.
342
370
  */
343
- public async utilityCheckNullifierExists(innerNullifier: Fr) {
371
+ public async checkNullifierExists(innerNullifier: Fr) {
344
372
  const [nullifier, anchorBlockHash] = await Promise.all([
345
373
  siloNullifier(this.contractAddress, innerNullifier!),
346
374
  this.anchorBlockHeader.hash(),
@@ -359,7 +387,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
359
387
  * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
360
388
  * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
361
389
  */
362
- public async utilityGetL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
390
+ public async getL1ToL2MembershipWitness(contractAddress: AztecAddress, messageHash: Fr, secret: Fr) {
363
391
  const [messageIndex, siblingPath] = await getNonNullifiedL1ToL2MessageWitness(
364
392
  this.aztecNode,
365
393
  contractAddress,
@@ -377,7 +405,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
377
405
  * @param startStorageSlot - The starting storage slot.
378
406
  * @param numberOfElements - Number of elements to read from the starting storage slot.
379
407
  */
380
- public async utilityStorageRead(
408
+ public async storageRead(
381
409
  blockHash: BlockHash,
382
410
  contractAddress: AztecAddress,
383
411
  startStorageSlot: Fr,
@@ -391,7 +419,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
391
419
  slots.map(storageSlot => this.aztecNode.getPublicStorageAt(blockHash, contractAddress, storageSlot)),
392
420
  );
393
421
 
394
- this.log.debug(
422
+ this.logger.debug(
395
423
  `Oracle storage read: slots=[${slots.map(slot => slot.toString()).join(', ')}] address=${contractAddress.toString()} values=[${values.join(', ')}]`,
396
424
  );
397
425
 
@@ -414,7 +442,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
414
442
  return this.contractLogger;
415
443
  }
416
444
 
417
- public async utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
445
+ public async log(level: number, message: string, fields: Fr[]): Promise<void> {
418
446
  if (!LogLevels[level]) {
419
447
  throw new Error(`Invalid log level: ${level}`);
420
448
  }
@@ -422,7 +450,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
422
450
  logContractMessage(logger, LogLevels[level], message, fields);
423
451
  }
424
452
 
425
- public async utilityFetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
453
+ public async fetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
426
454
  const logService = new LogService(
427
455
  this.aztecNode,
428
456
  this.anchorBlockHeader,
@@ -432,7 +460,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
432
460
  this.senderAddressBookStore,
433
461
  this.addressStore,
434
462
  this.jobId,
435
- this.log.getBindings(),
463
+ this.logger.getBindings(),
436
464
  );
437
465
 
438
466
  await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
@@ -448,10 +476,12 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
448
476
  * @param noteValidationRequestsArrayBaseSlot - The base slot of capsule array containing note validation requests.
449
477
  * @param eventValidationRequestsArrayBaseSlot - The base slot of capsule array containing event validation requests.
450
478
  */
451
- public async utilityValidateAndStoreEnqueuedNotesAndEvents(
479
+ public async validateAndStoreEnqueuedNotesAndEvents(
452
480
  contractAddress: AztecAddress,
453
481
  noteValidationRequestsArrayBaseSlot: Fr,
454
482
  eventValidationRequestsArrayBaseSlot: Fr,
483
+ maxNotePackedLen: number,
484
+ maxEventSerializedLen: number,
455
485
  ) {
456
486
  // TODO(#10727): allow other contracts to store notes
457
487
  if (!this.contractAddress.equals(contractAddress)) {
@@ -462,11 +492,11 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
462
492
  // faster as we don't need to wait for the network round-trip.
463
493
  const noteValidationRequests = (
464
494
  await this.capsuleStore.readCapsuleArray(contractAddress, noteValidationRequestsArrayBaseSlot, this.jobId)
465
- ).map(NoteValidationRequest.fromFields);
495
+ ).map(fields => NoteValidationRequest.fromFields(fields, maxNotePackedLen));
466
496
 
467
497
  const eventValidationRequests = (
468
498
  await this.capsuleStore.readCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, this.jobId)
469
- ).map(EventValidationRequest.fromFields);
499
+ ).map(fields => EventValidationRequest.fromFields(fields, maxEventSerializedLen));
470
500
 
471
501
  const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
472
502
  const noteStorePromises = noteValidationRequests.map(request =>
@@ -504,7 +534,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
504
534
  await this.capsuleStore.setCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, [], this.jobId);
505
535
  }
506
536
 
507
- public async utilityBulkRetrieveLogs(
537
+ public async bulkRetrieveLogs(
508
538
  contractAddress: AztecAddress,
509
539
  logRetrievalRequestsArrayBaseSlot: Fr,
510
540
  logRetrievalResponsesArrayBaseSlot: Fr,
@@ -529,7 +559,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
529
559
  this.senderAddressBookStore,
530
560
  this.addressStore,
531
561
  this.jobId,
532
- this.log.getBindings(),
562
+ this.logger.getBindings(),
533
563
  );
534
564
 
535
565
  const maybeLogRetrievalResponses = await logService.bulkRetrieveLogs(logRetrievalRequests);
@@ -546,7 +576,48 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
546
576
  );
547
577
  }
548
578
 
549
- 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> {
550
621
  if (!contractAddress.equals(this.contractAddress)) {
551
622
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
552
623
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -555,7 +626,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
555
626
  return Promise.resolve();
556
627
  }
557
628
 
558
- public async utilityLoadCapsule(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
629
+ public async loadCapsule(contractAddress: AztecAddress, slot: Fr): Promise<Fr[] | null> {
559
630
  if (!contractAddress.equals(this.contractAddress)) {
560
631
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
561
632
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -567,7 +638,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
567
638
  );
568
639
  }
569
640
 
570
- public utilityDeleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise<void> {
641
+ public deleteCapsule(contractAddress: AztecAddress, slot: Fr): Promise<void> {
571
642
  if (!contractAddress.equals(this.contractAddress)) {
572
643
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
573
644
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -576,12 +647,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
576
647
  return Promise.resolve();
577
648
  }
578
649
 
579
- public utilityCopyCapsule(
580
- contractAddress: AztecAddress,
581
- srcSlot: Fr,
582
- dstSlot: Fr,
583
- numEntries: number,
584
- ): Promise<void> {
650
+ public copyCapsule(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
585
651
  if (!contractAddress.equals(this.contractAddress)) {
586
652
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
587
653
  throw new Error(`Contract ${contractAddress} is not allowed to access ${this.contractAddress}'s PXE DB`);
@@ -589,8 +655,19 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
589
655
  return this.capsuleStore.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries, this.jobId);
590
656
  }
591
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
+
592
669
  // TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
593
- public utilityAes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
670
+ public aes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
594
671
  const aes128 = new Aes128();
595
672
  return aes128.decryptBufferCBC(ciphertext, iv, symKey);
596
673
  }
@@ -601,11 +678,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
601
678
  * @param ephPk - The ephemeral public key to get the secret for.
602
679
  * @returns The secret for the given address.
603
680
  */
604
- public utilityGetSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
605
- return this.getSharedSecret(address, ephPk);
606
- }
607
-
608
- protected async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
681
+ public async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
609
682
  // TODO(#12656): return an app-siloed secret
610
683
  const recipientCompleteAddress = await this.getCompleteAddressOrFail(address);
611
684
  const ivskM = await this.keyStore.getMasterSecretKey(
@@ -614,4 +687,14 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
614
687
  const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
615
688
  return deriveEcdhSharedSecret(addressSecret, ephPk);
616
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
+ }
617
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
+ }