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

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 (113) hide show
  1. package/dest/bin/check_oracle_version.js +4 -4
  2. package/dest/contract_function_simulator/contract_function_simulator.d.ts +3 -4
  3. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  4. package/dest/contract_function_simulator/ephemeral_array_service.d.ts +28 -0
  5. package/dest/contract_function_simulator/ephemeral_array_service.d.ts.map +1 -0
  6. package/dest/contract_function_simulator/ephemeral_array_service.js +78 -0
  7. package/dest/contract_function_simulator/noir-structs/event_validation_request.d.ts +1 -1
  8. package/dest/contract_function_simulator/noir-structs/event_validation_request.js +1 -1
  9. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.d.ts +1 -1
  10. package/dest/contract_function_simulator/noir-structs/log_retrieval_request.js +1 -1
  11. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.d.ts +1 -1
  12. package/dest/contract_function_simulator/noir-structs/log_retrieval_response.js +1 -1
  13. package/dest/contract_function_simulator/noir-structs/note_validation_request.d.ts +1 -1
  14. package/dest/contract_function_simulator/noir-structs/note_validation_request.js +1 -1
  15. package/dest/contract_function_simulator/oracle/interfaces.d.ts +13 -2
  16. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  17. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +1 -1
  18. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -1
  19. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +10 -1
  20. package/dest/contract_function_simulator/oracle/oracle.d.ts +13 -2
  21. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  22. package/dest/contract_function_simulator/oracle/oracle.js +98 -3
  23. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +2 -3
  24. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  25. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +9 -0
  26. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +25 -12
  27. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  28. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +102 -34
  29. package/dest/contract_function_simulator/pick_notes.d.ts +1 -1
  30. package/dest/contract_function_simulator/pick_notes.d.ts.map +1 -1
  31. package/dest/contract_function_simulator/pick_notes.js +9 -2
  32. package/dest/contract_sync/contract_sync_service.d.ts +4 -6
  33. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  34. package/dest/contract_sync/contract_sync_service.js +7 -27
  35. package/dest/contract_sync/helpers.d.ts +2 -3
  36. package/dest/contract_sync/helpers.d.ts.map +1 -1
  37. package/dest/debug/pxe_debug_utils.d.ts +3 -3
  38. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  39. package/dest/entrypoints/client/bundle/index.d.ts +1 -2
  40. package/dest/entrypoints/client/bundle/index.d.ts.map +1 -1
  41. package/dest/entrypoints/client/bundle/index.js +0 -1
  42. package/dest/entrypoints/client/bundle/utils.d.ts +2 -2
  43. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  44. package/dest/entrypoints/client/bundle/utils.js +2 -2
  45. package/dest/entrypoints/client/lazy/index.d.ts +1 -2
  46. package/dest/entrypoints/client/lazy/index.d.ts.map +1 -1
  47. package/dest/entrypoints/client/lazy/index.js +0 -1
  48. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  49. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  50. package/dest/entrypoints/client/lazy/utils.js +2 -2
  51. package/dest/entrypoints/pxe_creation_options.d.ts +3 -1
  52. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  53. package/dest/entrypoints/pxe_creation_options.js +3 -1
  54. package/dest/entrypoints/server/index.d.ts +2 -3
  55. package/dest/entrypoints/server/index.d.ts.map +1 -1
  56. package/dest/entrypoints/server/index.js +1 -2
  57. package/dest/entrypoints/server/utils.d.ts +2 -2
  58. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  59. package/dest/entrypoints/server/utils.js +2 -2
  60. package/dest/logs/log_service.d.ts +4 -6
  61. package/dest/logs/log_service.d.ts.map +1 -1
  62. package/dest/logs/log_service.js +11 -20
  63. package/dest/notes/note_service.d.ts +3 -4
  64. package/dest/notes/note_service.d.ts.map +1 -1
  65. package/dest/notes_filter.d.ts +2 -3
  66. package/dest/notes_filter.d.ts.map +1 -1
  67. package/dest/oracle_version.d.ts +4 -3
  68. package/dest/oracle_version.d.ts.map +1 -1
  69. package/dest/oracle_version.js +20 -10
  70. package/dest/pxe.d.ts +4 -5
  71. package/dest/pxe.d.ts.map +1 -1
  72. package/dest/pxe.js +4 -4
  73. package/dest/storage/capsule_store/capsule_service.d.ts +2 -3
  74. package/dest/storage/capsule_store/capsule_service.d.ts.map +1 -1
  75. package/dest/storage/capsule_store/capsule_service.js +1 -1
  76. package/dest/storage/note_store/note_store.d.ts +1 -1
  77. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  78. package/dest/storage/note_store/note_store.js +2 -2
  79. package/package.json +16 -16
  80. package/src/bin/check_oracle_version.ts +4 -4
  81. package/src/contract_function_simulator/contract_function_simulator.ts +2 -3
  82. package/src/contract_function_simulator/ephemeral_array_service.ts +110 -0
  83. package/src/contract_function_simulator/noir-structs/event_validation_request.ts +1 -1
  84. package/src/contract_function_simulator/noir-structs/log_retrieval_request.ts +1 -1
  85. package/src/contract_function_simulator/noir-structs/log_retrieval_response.ts +1 -1
  86. package/src/contract_function_simulator/noir-structs/note_validation_request.ts +1 -1
  87. package/src/contract_function_simulator/oracle/interfaces.ts +20 -1
  88. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +7 -1
  89. package/src/contract_function_simulator/oracle/oracle.ts +143 -3
  90. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +12 -3
  91. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +164 -63
  92. package/src/contract_function_simulator/pick_notes.ts +9 -2
  93. package/src/contract_sync/contract_sync_service.ts +15 -37
  94. package/src/contract_sync/helpers.ts +2 -3
  95. package/src/debug/pxe_debug_utils.ts +3 -3
  96. package/src/entrypoints/client/bundle/index.ts +0 -1
  97. package/src/entrypoints/client/bundle/utils.ts +2 -3
  98. package/src/entrypoints/client/lazy/index.ts +0 -1
  99. package/src/entrypoints/client/lazy/utils.ts +2 -3
  100. package/src/entrypoints/pxe_creation_options.ts +7 -0
  101. package/src/entrypoints/server/index.ts +1 -2
  102. package/src/entrypoints/server/utils.ts +2 -3
  103. package/src/logs/log_service.ts +19 -50
  104. package/src/notes/note_service.ts +2 -3
  105. package/src/notes_filter.ts +1 -3
  106. package/src/oracle_version.ts +20 -10
  107. package/src/pxe.ts +8 -10
  108. package/src/storage/capsule_store/capsule_service.ts +5 -6
  109. package/src/storage/note_store/note_store.ts +2 -5
  110. package/dest/access_scopes.d.ts +0 -9
  111. package/dest/access_scopes.d.ts.map +0 -1
  112. package/dest/access_scopes.js +0 -6
  113. package/src/access_scopes.ts +0 -9
@@ -21,14 +21,13 @@ import type { NoteStatus } from '@aztec/stdlib/note';
21
21
  import { MerkleTreeId, type NullifierMembershipWitness, PublicDataWitness } from '@aztec/stdlib/trees';
22
22
  import type { BlockHeader, Capsule, OffchainEffect } from '@aztec/stdlib/tx';
23
23
 
24
- import type { AccessScopes } from '../../access_scopes.js';
25
24
  import { createContractLogger, logContractMessage, stripAztecnrLogPrefix } from '../../contract_logging.js';
26
25
  import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
27
26
  import { EventService } from '../../events/event_service.js';
28
27
  import { LogService } from '../../logs/log_service.js';
29
28
  import { MessageContextService } from '../../messages/message_context_service.js';
30
29
  import { NoteService } from '../../notes/note_service.js';
31
- import { ORACLE_VERSION } from '../../oracle_version.js';
30
+ import { ORACLE_VERSION_MAJOR } from '../../oracle_version.js';
32
31
  import type { AddressStore } from '../../storage/address_store/address_store.js';
33
32
  import type { CapsuleService } from '../../storage/capsule_store/capsule_service.js';
34
33
  import type { ContractStore } from '../../storage/contract_store/contract_store.js';
@@ -36,6 +35,7 @@ import type { NoteStore } from '../../storage/note_store/note_store.js';
36
35
  import type { PrivateEventStore } from '../../storage/private_event_store/private_event_store.js';
37
36
  import type { RecipientTaggingStore } from '../../storage/tagging_store/recipient_tagging_store.js';
38
37
  import type { SenderAddressBookStore } from '../../storage/tagging_store/sender_address_book_store.js';
38
+ import { EphemeralArrayService } from '../ephemeral_array_service.js';
39
39
  import { EventValidationRequest } from '../noir-structs/event_validation_request.js';
40
40
  import { LogRetrievalRequest } from '../noir-structs/log_retrieval_request.js';
41
41
  import { LogRetrievalResponse } from '../noir-structs/log_retrieval_response.js';
@@ -65,7 +65,7 @@ export type UtilityExecutionOracleArgs = {
65
65
  contractSyncService: ContractSyncService;
66
66
  jobId: string;
67
67
  log?: ReturnType<typeof createLogger>;
68
- scopes: AccessScopes;
68
+ scopes: AztecAddress[];
69
69
  };
70
70
 
71
71
  /**
@@ -78,6 +78,10 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
78
78
  private contractLogger: Logger | undefined;
79
79
  private aztecnrLogger: Logger | undefined;
80
80
  private offchainEffects: OffchainEffect[] = [];
81
+ private readonly ephemeralArrayService = new EphemeralArrayService();
82
+
83
+ // We store oracle version to be able to show a nice error message when an oracle handler is missing.
84
+ private contractOracleVersion: { major: number; minor: number } | undefined;
81
85
 
82
86
  protected readonly contractAddress: AztecAddress;
83
87
  protected readonly authWitnesses: AuthWitness[];
@@ -96,7 +100,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
96
100
  protected readonly contractSyncService: ContractSyncService;
97
101
  protected readonly jobId: string;
98
102
  protected logger: ReturnType<typeof createLogger>;
99
- protected readonly scopes: AccessScopes;
103
+ protected readonly scopes: AztecAddress[];
100
104
 
101
105
  constructor(args: UtilityExecutionOracleArgs) {
102
106
  this.contractAddress = args.contractAddress;
@@ -119,7 +123,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
119
123
  this.scopes = args.scopes;
120
124
  }
121
125
 
122
- public assertCompatibleOracleVersion(version: number): void {
126
+ public assertCompatibleOracleVersion(major: number, minor: number): void {
123
127
  // TODO(F-416): Remove this hack on v5 when protocol contracts are redeployed.
124
128
  // Protocol contracts/canonical contracts shipped with committed bytecode that cannot be changed. Assert they use
125
129
  // the expected pinned version or the current one. We want to allow for both the pinned and the current versions
@@ -127,27 +131,36 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
127
131
  // pinned contracts (like e.g. next)
128
132
  const LEGACY_ORACLE_VERSION = 12;
129
133
  if (isProtocolContract(this.contractAddress)) {
130
- if (version !== LEGACY_ORACLE_VERSION && version !== ORACLE_VERSION) {
134
+ if (major !== LEGACY_ORACLE_VERSION && major !== ORACLE_VERSION_MAJOR) {
131
135
  const hint =
132
- version > ORACLE_VERSION
136
+ major > ORACLE_VERSION_MAJOR
133
137
  ? 'The contract was compiled with a newer version of Aztec.nr than your private environment supports. Upgrade your private environment to a compatible version.'
134
138
  : 'The contract was compiled with an older version of Aztec.nr than your private environment supports. Recompile the contract with a compatible version of Aztec.nr.';
135
139
  throw new Error(
136
- `Incompatible private environment version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle version ${LEGACY_ORACLE_VERSION} or ${ORACLE_VERSION}, got ${version})`,
140
+ `Incompatible private environment version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${LEGACY_ORACLE_VERSION} or ${ORACLE_VERSION_MAJOR}, got ${major})`,
137
141
  );
138
142
  }
143
+ this.contractOracleVersion = { major, minor };
139
144
  return;
140
145
  }
141
146
 
142
- if (version !== ORACLE_VERSION) {
147
+ if (major !== ORACLE_VERSION_MAJOR) {
143
148
  const hint =
144
- version > ORACLE_VERSION
149
+ major > ORACLE_VERSION_MAJOR
145
150
  ? 'The contract was compiled with a newer version of Aztec.nr than your private environment supports. Upgrade your private environment to a compatible version.'
146
151
  : 'The contract was compiled with an older version of Aztec.nr than your private environment supports. Recompile the contract with a compatible version of Aztec.nr.';
147
152
  throw new Error(
148
- `Incompatible private environment version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle version ${ORACLE_VERSION}, got ${version})`,
153
+ `Incompatible private environment version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
149
154
  );
150
155
  }
156
+
157
+ // Major matches - store both major and minor for later diagnostics (e.g. when an oracle is not found)
158
+ this.contractOracleVersion = { major, minor };
159
+ }
160
+
161
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
162
+ public nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
163
+ return this.contractOracleVersion;
151
164
  }
152
165
 
153
166
  public getRandomField(): Fr {
@@ -166,18 +179,15 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
166
179
  * @throws If scopes are defined and the account is not in the scopes.
167
180
  */
168
181
  public async getKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
169
- // If scopes are defined, check that the key belongs to an account in the scopes.
170
- if (this.scopes !== 'ALL_SCOPES' && this.scopes.length > 0) {
171
- let hasAccess = false;
172
- for (let i = 0; i < this.scopes.length && !hasAccess; i++) {
173
- if (await this.keyStore.accountHasKey(this.scopes[i], pkMHash)) {
174
- hasAccess = true;
175
- }
176
- }
177
- if (!hasAccess) {
178
- throw new Error(`Key validation request denied: no scoped account has a key with hash ${pkMHash.toString()}.`);
182
+ let hasAccess = false;
183
+ for (let i = 0; i < this.scopes.length && !hasAccess; i++) {
184
+ if (await this.keyStore.accountHasKey(this.scopes[i], pkMHash)) {
185
+ hasAccess = true;
179
186
  }
180
187
  }
188
+ if (!hasAccess) {
189
+ throw new Error(`Key validation request denied: no scoped account has a key with hash ${pkMHash.toString()}.`);
190
+ }
181
191
  return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress);
182
192
  }
183
193
 
@@ -497,31 +507,43 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
497
507
  logContractMessage(logger, LogLevels[level], strippedMessage, fields);
498
508
  }
499
509
 
510
+ // Deprecated, only kept for backwards compatibility until Alpha v5 rolls out.
500
511
  public async getPendingTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr, scope: AztecAddress) {
501
- const logService = new LogService(
512
+ const logService = this.#createLogService();
513
+ const logs = await logService.fetchTaggedLogs(this.contractAddress, scope);
514
+ await this.capsuleService.appendToCapsuleArray(
515
+ this.contractAddress,
516
+ pendingTaggedLogArrayBaseSlot,
517
+ logs.map(log => log.toFields()),
518
+ this.jobId,
519
+ scope,
520
+ );
521
+ }
522
+
523
+ /** Fetches pending tagged logs into a freshly allocated ephemeral array and returns its base slot. */
524
+ public async getPendingTaggedLogsV2(scope: AztecAddress): Promise<Fr> {
525
+ const logService = this.#createLogService();
526
+ const logs = await logService.fetchTaggedLogs(this.contractAddress, scope);
527
+ return this.ephemeralArrayService.newArray(logs.map(log => log.toFields()));
528
+ }
529
+
530
+ #createLogService(): LogService {
531
+ return new LogService(
502
532
  this.aztecNode,
503
533
  this.anchorBlockHeader,
504
534
  this.keyStore,
505
- this.capsuleService,
506
535
  this.recipientTaggingStore,
507
536
  this.senderAddressBookStore,
508
537
  this.addressStore,
509
538
  this.jobId,
510
539
  this.logger.getBindings(),
511
540
  );
512
-
513
- await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, scope);
514
541
  }
515
542
 
516
543
  /**
517
- * Validates all note and event validation requests enqueued via `enqueue_note_for_validation` and
518
- * `enqueue_event_for_validation`, inserting them into the note database and event store respectively, making them
519
- * queryable via `get_notes` and `getPrivateEvents`.
544
+ * Legacy: validates note/event requests stored in capsule arrays.
520
545
  *
521
- * This automatically clears both validation request queues, so no further work needs to be done by the caller.
522
- * @param contractAddress - The address of the contract that the logs are tagged for.
523
- * @param noteValidationRequestsArrayBaseSlot - The base slot of capsule array containing note validation requests.
524
- * @param eventValidationRequestsArrayBaseSlot - The base slot of capsule array containing event validation requests.
546
+ * Deprecated, only kept for backwards compatibility until Alpha v5 rolls out.
525
547
  */
526
548
  public async validateAndStoreEnqueuedNotesAndEvents(
527
549
  contractAddress: AztecAddress,
@@ -536,8 +558,6 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
536
558
  throw new Error(`Got a note validation request from ${contractAddress}, expected ${this.contractAddress}`);
537
559
  }
538
560
 
539
- // We read all note and event validation requests and process them all concurrently. This makes the process much
540
- // faster as we don't need to wait for the network round-trip.
541
561
  const noteValidationRequests = (
542
562
  await this.capsuleService.readCapsuleArray(
543
563
  contractAddress,
@@ -556,6 +576,53 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
556
576
  )
557
577
  ).map(fields => EventValidationRequest.fromFields(fields, maxEventSerializedLen));
558
578
 
579
+ await this.#processValidationRequests(noteValidationRequests, eventValidationRequests, scope);
580
+
581
+ await this.capsuleService.setCapsuleArray(
582
+ contractAddress,
583
+ noteValidationRequestsArrayBaseSlot,
584
+ [],
585
+ this.jobId,
586
+ scope,
587
+ );
588
+ await this.capsuleService.setCapsuleArray(
589
+ contractAddress,
590
+ eventValidationRequestsArrayBaseSlot,
591
+ [],
592
+ this.jobId,
593
+ scope,
594
+ );
595
+ }
596
+
597
+ public async validateAndStoreEnqueuedNotesAndEventsV2(
598
+ noteValidationRequestsArrayBaseSlot: Fr,
599
+ eventValidationRequestsArrayBaseSlot: Fr,
600
+ maxNotePackedLen: number,
601
+ maxEventSerializedLen: number,
602
+ scope: AztecAddress,
603
+ ) {
604
+ const noteValidationRequests = this.ephemeralArrayService
605
+ .readArrayAt(noteValidationRequestsArrayBaseSlot)
606
+ .map(fields => NoteValidationRequest.fromFields(fields, maxNotePackedLen));
607
+
608
+ const eventValidationRequests = this.ephemeralArrayService
609
+ .readArrayAt(eventValidationRequestsArrayBaseSlot)
610
+ .map(fields => EventValidationRequest.fromFields(fields, maxEventSerializedLen));
611
+
612
+ await this.#processValidationRequests(noteValidationRequests, eventValidationRequests, scope);
613
+ }
614
+
615
+ /**
616
+ * Dispatches note and event validation requests to the service layer.
617
+ *
618
+ * This function is an auxiliary to support legacy (capsule backed) and new (ephemeral array backed) versions of the
619
+ * `validateAndStoreEnqueuedNotesAndEvents` oracle.
620
+ */
621
+ async #processValidationRequests(
622
+ noteValidationRequests: NoteValidationRequest[],
623
+ eventValidationRequests: EventValidationRequest[],
624
+ scope: AztecAddress,
625
+ ) {
559
626
  const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
560
627
  const noteStorePromises = noteValidationRequests.map(request =>
561
628
  noteService.validateAndStoreNote(
@@ -586,22 +653,6 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
586
653
  );
587
654
 
588
655
  await Promise.all([...noteStorePromises, ...eventStorePromises]);
589
-
590
- // Requests are cleared once we're done.
591
- await this.capsuleService.setCapsuleArray(
592
- contractAddress,
593
- noteValidationRequestsArrayBaseSlot,
594
- [],
595
- this.jobId,
596
- scope,
597
- );
598
- await this.capsuleService.setCapsuleArray(
599
- contractAddress,
600
- eventValidationRequestsArrayBaseSlot,
601
- [],
602
- this.jobId,
603
- scope,
604
- );
605
656
  }
606
657
 
607
658
  public async getLogsByTag(
@@ -621,18 +672,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
621
672
  await this.capsuleService.readCapsuleArray(contractAddress, logRetrievalRequestsArrayBaseSlot, this.jobId, scope)
622
673
  ).map(LogRetrievalRequest.fromFields);
623
674
 
624
- const logService = new LogService(
625
- this.aztecNode,
626
- this.anchorBlockHeader,
627
- this.keyStore,
628
- this.capsuleService,
629
- this.recipientTaggingStore,
630
- this.senderAddressBookStore,
631
- this.addressStore,
632
- this.jobId,
633
- this.logger.getBindings(),
634
- );
635
-
675
+ const logService = this.#createLogService();
636
676
  const maybeLogRetrievalResponses = await logService.fetchLogsByTag(contractAddress, logRetrievalRequests);
637
677
 
638
678
  // Requests are cleared once we're done.
@@ -654,6 +694,18 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
654
694
  );
655
695
  }
656
696
 
697
+ public async getLogsByTagV2(requestArrayBaseSlot: Fr): Promise<Fr> {
698
+ const logRetrievalRequests = this.ephemeralArrayService
699
+ .readArrayAt(requestArrayBaseSlot)
700
+ .map(LogRetrievalRequest.fromFields);
701
+ const logService = this.#createLogService();
702
+
703
+ const maybeLogRetrievalResponses = await logService.fetchLogsByTag(this.contractAddress, logRetrievalRequests);
704
+
705
+ return this.ephemeralArrayService.newArray(maybeLogRetrievalResponses.map(LogRetrievalResponse.toSerializedOption));
706
+ }
707
+
708
+ // Deprecated, only kept for backwards compatibility until Alpha v5 rolls out.
657
709
  public async getMessageContextsByTxHash(
658
710
  contractAddress: AztecAddress,
659
711
  messageContextRequestsArrayBaseSlot: Fr,
@@ -665,7 +717,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
665
717
  throw new Error(`Got a message context request from ${contractAddress}, expected ${this.contractAddress}`);
666
718
  }
667
719
 
668
- // TODO(@mverzilli): this is a prime example of where using a volatile array would make much more sense, we don't
720
+ // TODO(@mverzilli): this is a prime example of where using an ephemeral array would make much more sense, we don't
669
721
  // need scopes here, we just need a bit of shared memory to cross boundaries between Noir and TS.
670
722
  // At the same time, we don't want to allow any global scope access other than where backwards compatibility
671
723
  // forces us to. Hence we need the scope here to be artificial.
@@ -709,6 +761,27 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
709
761
  }
710
762
  }
711
763
 
764
+ /** Reads tx hash requests from an ephemeral array, resolves their contexts, and returns the response slot. */
765
+ public async getMessageContextsByTxHashV2(requestArrayBaseSlot: Fr): Promise<Fr> {
766
+ const requestFields = this.ephemeralArrayService.readArrayAt(requestArrayBaseSlot);
767
+
768
+ const txHashes = requestFields.map((fields, i) => {
769
+ if (fields.length !== 1) {
770
+ throw new Error(
771
+ `Malformed message context request at index ${i}: expected 1 field (tx hash), got ${fields.length}`,
772
+ );
773
+ }
774
+ return fields[0];
775
+ });
776
+
777
+ const maybeMessageContexts = await this.messageContextService.getMessageContextsByTxHash(
778
+ txHashes,
779
+ this.anchorBlockHeader.getBlockNumber(),
780
+ );
781
+
782
+ return this.ephemeralArrayService.newArray(maybeMessageContexts.map(MessageContext.toSerializedOption));
783
+ }
784
+
712
785
  public setCapsule(contractAddress: AztecAddress, slot: Fr, capsule: Fr[], scope: AztecAddress): void {
713
786
  if (!contractAddress.equals(this.contractAddress)) {
714
787
  // TODO(#10727): instead of this check that this.contractAddress is allowed to access the external DB
@@ -785,6 +858,34 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
785
858
  return deriveAppSiloedSharedSecret(addressSecret, ephPk, this.contractAddress);
786
859
  }
787
860
 
861
+ public pushEphemeral(slot: Fr, elements: Fr[]): number {
862
+ return this.ephemeralArrayService.push(slot, elements);
863
+ }
864
+
865
+ public popEphemeral(slot: Fr): Fr[] {
866
+ return this.ephemeralArrayService.pop(slot);
867
+ }
868
+
869
+ public getEphemeral(slot: Fr, index: number): Fr[] {
870
+ return this.ephemeralArrayService.get(slot, index);
871
+ }
872
+
873
+ public setEphemeral(slot: Fr, index: number, elements: Fr[]): void {
874
+ this.ephemeralArrayService.set(slot, index, elements);
875
+ }
876
+
877
+ public getEphemeralLen(slot: Fr): number {
878
+ return this.ephemeralArrayService.len(slot);
879
+ }
880
+
881
+ public removeEphemeral(slot: Fr, index: number): void {
882
+ this.ephemeralArrayService.remove(slot, index);
883
+ }
884
+
885
+ public clearEphemeral(slot: Fr): void {
886
+ this.ephemeralArrayService.clear(slot);
887
+ }
888
+
788
889
  public emitOffchainEffect(data: Fr[]): Promise<void> {
789
890
  this.offchainEffects.push({ data, contractAddress: this.contractAddress });
790
891
  return Promise.resolve();
@@ -86,8 +86,15 @@ interface ContainsNote {
86
86
 
87
87
  const selectPropertyFromPackedNoteContent = (noteData: Fr[], selector: PropertySelector): Fr => {
88
88
  const noteValueBuffer = noteData[selector.index].toBuffer();
89
- const noteValue = noteValueBuffer.subarray(selector.offset, selector.offset + selector.length);
90
- return Fr.fromBuffer(noteValue);
89
+ // Noir's PropertySelector counts offset from the LSB (last byte of the big-endian buffer),
90
+ // so offset=0,length=Fr.SIZE_IN_BYTES reads the entire field, and offset=0,length=1 reads the last byte.
91
+ const start = Fr.SIZE_IN_BYTES - selector.offset - selector.length;
92
+ const end = Fr.SIZE_IN_BYTES - selector.offset;
93
+ const noteValue = noteValueBuffer.subarray(start, end);
94
+ // Left-pad to Fr.SIZE_IN_BYTES so Fr.fromBuffer interprets the value correctly.
95
+ const padded = Buffer.alloc(Fr.SIZE_IN_BYTES);
96
+ noteValue.copy(padded, Fr.SIZE_IN_BYTES - noteValue.length);
97
+ return Fr.fromBuffer(padded);
91
98
  };
92
99
 
93
100
  const selectNotes = <T extends ContainsNote>(noteDatas: T[], selects: Select[]): T[] =>
@@ -4,7 +4,6 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
4
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
5
  import type { BlockHeader } from '@aztec/stdlib/tx';
6
6
 
7
- import type { AccessScopes } from '../access_scopes.js';
8
7
  import type { StagedStore } from '../job_coordinator/job_coordinator.js';
9
8
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
10
9
  import type { NoteStore } from '../storage/note_store/note_store.js';
@@ -31,7 +30,6 @@ export class ContractSyncService implements StagedStore {
31
30
  private aztecNode: AztecNode,
32
31
  private contractStore: ContractStore,
33
32
  private noteStore: NoteStore,
34
- private getRegisteredAccounts: () => Promise<AztecAddress[]>,
35
33
  private log: Logger,
36
34
  ) {}
37
35
 
@@ -52,10 +50,10 @@ export class ContractSyncService implements StagedStore {
52
50
  async ensureContractSynced(
53
51
  contractAddress: AztecAddress,
54
52
  functionToInvokeAfterSync: FunctionSelector | null,
55
- utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
53
+ utilityExecutor: (call: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
56
54
  anchorBlockHeader: BlockHeader,
57
55
  jobId: string,
58
- scopes: AccessScopes,
56
+ scopes: AztecAddress[],
59
57
  ): Promise<void> {
60
58
  if (this.#shouldSkipSync(jobId, contractAddress)) {
61
59
  return;
@@ -75,33 +73,29 @@ export class ContractSyncService implements StagedStore {
75
73
  await this.#awaitSync(contractAddress, scopes);
76
74
  }
77
75
 
78
- /** Clears sync cache entries for the given scopes of a contract. Also clears the ALL_SCOPES entry. */
76
+ /** Clears sync cache entries for the given scopes of a contract. */
79
77
  invalidateContractForScopes(contractAddress: AztecAddress, scopes: AztecAddress[]): void {
80
78
  if (scopes.length === 0) {
81
79
  return;
82
80
  }
83
81
  scopes.forEach(scope => this.syncedContracts.delete(toKey(contractAddress, scope)));
84
- this.syncedContracts.delete(toKey(contractAddress, 'ALL_SCOPES'));
85
82
  }
86
83
 
87
84
  async #syncContract(
88
85
  contractAddress: AztecAddress,
89
86
  functionToInvokeAfterSync: FunctionSelector | null,
90
- utilityExecutor: (call: FunctionCall, scopes: AccessScopes) => Promise<any>,
87
+ utilityExecutor: (call: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
91
88
  anchorBlockHeader: BlockHeader,
92
89
  jobId: string,
93
- scopes: AccessScopes,
90
+ scopes: AztecAddress[],
94
91
  ): Promise<void> {
95
92
  this.log.debug(`Syncing contract ${contractAddress}`);
96
93
 
97
- // Resolve ALL_SCOPES to actual registered accounts, since sync_state must be called once per account.
98
- const scopeAddresses = scopes === 'ALL_SCOPES' ? await this.getRegisteredAccounts() : scopes;
99
-
100
94
  await Promise.all([
101
95
  // Call sync_state sequentially for each scope address — each invocation synchronizes one account's private
102
96
  // state using scoped capsule arrays.
103
97
  (async () => {
104
- for (const scope of scopeAddresses) {
98
+ for (const scope of scopes) {
105
99
  await syncState(
106
100
  contractAddress,
107
101
  this.contractStore,
@@ -147,11 +141,11 @@ export class ContractSyncService implements StagedStore {
147
141
  /** If there are unsynced scopes, starts sync and stores the promise in cache with error cleanup. */
148
142
  #startSyncIfNeeded(
149
143
  contractAddress: AztecAddress,
150
- scopes: AccessScopes,
151
- syncFn: (scopesToSync: AccessScopes) => Promise<void>,
144
+ scopes: AztecAddress[],
145
+ syncFn: (scopesToSync: AztecAddress[]) => Promise<void>,
152
146
  ): void {
153
- const scopesToSync = this.#getScopesToSync(contractAddress, scopes);
154
- const keys = toKeys(contractAddress, scopesToSync);
147
+ const scopesToSync = scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
148
+ const keys = scopesToSync.map(scope => toKey(contractAddress, scope));
155
149
  if (keys.length === 0) {
156
150
  return;
157
151
  }
@@ -162,31 +156,15 @@ export class ContractSyncService implements StagedStore {
162
156
  keys.forEach(key => this.syncedContracts.set(key, promise));
163
157
  }
164
158
 
165
- /** Filters out scopes that are already cached, returning only those that still need syncing. */
166
- #getScopesToSync(contractAddress: AztecAddress, scopes: AccessScopes): AccessScopes {
167
- if (this.syncedContracts.has(toKey(contractAddress, 'ALL_SCOPES'))) {
168
- // If we are already syncing all scopes, then return an empty list
169
- return [];
170
- }
171
- if (scopes === 'ALL_SCOPES') {
172
- return 'ALL_SCOPES';
173
- }
174
- return scopes.filter(scope => !this.syncedContracts.has(toKey(contractAddress, scope)));
175
- }
176
-
177
159
  /** Collects all relevant scope promises (including in-flight ones from concurrent calls) and awaits them. */
178
- async #awaitSync(contractAddress: AztecAddress, scopes: AccessScopes): Promise<void> {
179
- const promises = toKeys(contractAddress, scopes)
180
- .map(key => this.syncedContracts.get(key))
160
+ async #awaitSync(contractAddress: AztecAddress, scopes: AztecAddress[]): Promise<void> {
161
+ const promises = scopes
162
+ .map(scope => this.syncedContracts.get(toKey(contractAddress, scope)))
181
163
  .filter(p => p !== undefined);
182
164
  await Promise.all(promises);
183
165
  }
184
166
  }
185
167
 
186
- function toKeys(contract: AztecAddress, scopes: AccessScopes) {
187
- return scopes === 'ALL_SCOPES' ? [toKey(contract, scopes)] : scopes.map(scope => toKey(contract, scope));
188
- }
189
-
190
- function toKey(contract: AztecAddress, scope: AztecAddress | 'ALL_SCOPES') {
191
- return scope === 'ALL_SCOPES' ? `${contract.toString()}:*` : `${contract.toString()}:${scope.toString()}`;
168
+ function toKey(contract: AztecAddress, scope: AztecAddress) {
169
+ return `${contract.toString()}:${scope.toString()}`;
192
170
  }
@@ -6,7 +6,6 @@ import { DelayedPublicMutableValues, DelayedPublicMutableValuesWithHash } from '
6
6
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
7
7
  import type { BlockHeader } from '@aztec/stdlib/tx';
8
8
 
9
- import type { AccessScopes } from '../access_scopes.js';
10
9
  import { NoteService } from '../notes/note_service.js';
11
10
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
12
11
  import type { NoteStore } from '../storage/note_store/note_store.js';
@@ -43,7 +42,7 @@ export async function syncState(
43
42
  contractAddress: AztecAddress,
44
43
  contractStore: ContractStore,
45
44
  functionToInvokeAfterSync: FunctionSelector | null,
46
- utilityExecutor: (privateSyncCall: FunctionCall, scopes: AccessScopes) => Promise<any>,
45
+ utilityExecutor: (privateSyncCall: FunctionCall, scopes: AztecAddress[]) => Promise<any>,
47
46
  noteStore: NoteStore,
48
47
  aztecNode: AztecNode,
49
48
  anchorBlockHeader: BlockHeader,
@@ -60,7 +59,7 @@ export async function syncState(
60
59
  }
61
60
 
62
61
  const noteService = new NoteService(noteStore, aztecNode, anchorBlockHeader, jobId);
63
- const scopes: AccessScopes = [scope];
62
+ const scopes: AztecAddress[] = [scope];
64
63
 
65
64
  // Both sync_state and syncNoteNullifiers interact with the note store, but running them in parallel is safe
66
65
  // because note store is designed to handle concurrent operations.
@@ -1,9 +1,9 @@
1
1
  import type { FunctionCall } from '@aztec/stdlib/abi';
2
2
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
3
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
4
  import type { NoteDao } from '@aztec/stdlib/note';
4
5
  import type { ContractOverrides } from '@aztec/stdlib/tx';
5
6
 
6
- import type { AccessScopes } from '../access_scopes.js';
7
7
  import type { BlockSynchronizer } from '../block_synchronizer/block_synchronizer.js';
8
8
  import type { ContractFunctionSimulator } from '../contract_function_simulator/contract_function_simulator.js';
9
9
  import type { ContractSyncService } from '../contract_sync/contract_sync_service.js';
@@ -22,7 +22,7 @@ export class PXEDebugUtils {
22
22
  contractFunctionSimulator: ContractFunctionSimulator,
23
23
  call: FunctionCall,
24
24
  authWitnesses: AuthWitness[] | undefined,
25
- scopes: AccessScopes,
25
+ scopes: AztecAddress[],
26
26
  jobId: string,
27
27
  ) => Promise<any>;
28
28
 
@@ -41,7 +41,7 @@ export class PXEDebugUtils {
41
41
  contractFunctionSimulator: ContractFunctionSimulator,
42
42
  call: FunctionCall,
43
43
  authWitnesses: AuthWitness[] | undefined,
44
- scopes: AccessScopes,
44
+ scopes: AztecAddress[],
45
45
  jobId: string,
46
46
  ) => Promise<any>,
47
47
  ) {
@@ -1,4 +1,3 @@
1
- export * from '../../../access_scopes.js';
2
1
  export * from '../../../notes_filter.js';
3
2
  export * from '../../../pxe.js';
4
3
  export * from '../../../config/index.js';
@@ -1,4 +1,3 @@
1
- import { BBPrivateKernelProver } from '@aztec/bb-prover/client';
2
1
  import { BBBundlePrivateKernelProver } from '@aztec/bb-prover/client/bundle';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { createStore } from '@aztec/kv-store/indexeddb';
@@ -9,7 +8,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
9
8
  import type { PXEConfig } from '../../../config/index.js';
10
9
  import { PXE } from '../../../pxe.js';
11
10
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
12
- import type { PXECreationOptions } from '../../pxe_creation_options.js';
11
+ import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
13
12
 
14
13
  /**
15
14
  * Create and start an PXE instance with the given AztecNode.
@@ -44,7 +43,7 @@ export async function createPXE(
44
43
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
45
44
 
46
45
  let prover;
47
- if (options.proverOrOptions instanceof BBPrivateKernelProver) {
46
+ if (isPrivateKernelProver(options.proverOrOptions)) {
48
47
  prover = options.proverOrOptions;
49
48
  } else {
50
49
  prover = new BBBundlePrivateKernelProver(simulator, { ...options.proverOrOptions, logger: proverLogger });
@@ -1,4 +1,3 @@
1
- export * from '../../../access_scopes.js';
2
1
  export * from '../../../notes_filter.js';
3
2
  export * from '../../../pxe.js';
4
3
  export * from '../../../config/index.js';
@@ -1,4 +1,3 @@
1
- import { BBPrivateKernelProver } from '@aztec/bb-prover/client';
2
1
  import { BBLazyPrivateKernelProver } from '@aztec/bb-prover/client/lazy';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { createStore } from '@aztec/kv-store/indexeddb';
@@ -9,7 +8,7 @@ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
9
8
  import type { PXEConfig } from '../../../config/index.js';
10
9
  import { PXE } from '../../../pxe.js';
11
10
  import { PXE_DATA_SCHEMA_VERSION } from '../../../storage/metadata.js';
12
- import type { PXECreationOptions } from '../../pxe_creation_options.js';
11
+ import { type PXECreationOptions, isPrivateKernelProver } from '../../pxe_creation_options.js';
13
12
 
14
13
  /**
15
14
  * Create and start an PXE instance with the given AztecNode.
@@ -44,7 +43,7 @@ export async function createPXE(
44
43
  const proverLogger = loggers.prover ?? createLogger('pxe:bb:wasm:bundle', { actor });
45
44
 
46
45
  let prover;
47
- if (options.proverOrOptions instanceof BBPrivateKernelProver) {
46
+ if (isPrivateKernelProver(options.proverOrOptions)) {
48
47
  prover = options.proverOrOptions;
49
48
  } else {
50
49
  prover = new BBLazyPrivateKernelProver(simulator, { ...options.proverOrOptions, logger: proverLogger });
@@ -12,3 +12,10 @@ export type PXECreationOptions = {
12
12
  store?: AztecAsyncKVStore;
13
13
  simulator?: CircuitSimulator;
14
14
  };
15
+
16
+ /** Checks if the given value implements the PrivateKernelProver interface via duck-typing. */
17
+ export function isPrivateKernelProver(value: unknown): value is PrivateKernelProver {
18
+ return (
19
+ typeof value === 'object' && value !== null && typeof (value as PrivateKernelProver).createChonkProof === 'function'
20
+ );
21
+ }
@@ -1,4 +1,3 @@
1
- export * from '../../access_scopes.js';
2
1
  export * from '../../notes_filter.js';
3
2
  export * from '../../pxe.js';
4
3
  export * from '../../config/index.js';
@@ -6,7 +5,7 @@ export * from '../../error_enriching.js';
6
5
  export * from '../../storage/index.js';
7
6
  export * from './utils.js';
8
7
  export { NoteService } from '../../notes/note_service.js';
9
- export { ORACLE_VERSION } from '../../oracle_version.js';
8
+ export { ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR } from '../../oracle_version.js';
10
9
  export { type PXECreationOptions } from '../pxe_creation_options.js';
11
10
  export { JobCoordinator } from '../../job_coordinator/job_coordinator.js';
12
11
  export { ContractSyncService } from '../../contract_sync/contract_sync_service.js';