@aztec/pxe 0.0.1-commit.29c6b1a3 → 0.0.1-commit.2eb6648a

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 (124) hide show
  1. package/dest/block_synchronizer/block_synchronizer.d.ts +5 -3
  2. package/dest/block_synchronizer/block_synchronizer.d.ts.map +1 -1
  3. package/dest/block_synchronizer/block_synchronizer.js +9 -3
  4. package/dest/contract_function_simulator/contract_function_simulator.d.ts +4 -4
  5. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  6. package/dest/contract_function_simulator/contract_function_simulator.js +9 -9
  7. package/dest/contract_function_simulator/oracle/interfaces.d.ts +8 -8
  8. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  9. package/dest/contract_function_simulator/oracle/oracle.d.ts +4 -4
  10. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  11. package/dest/contract_function_simulator/oracle/oracle.js +29 -17
  12. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +4 -3
  13. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts.map +1 -1
  14. package/dest/contract_function_simulator/oracle/private_execution_oracle.js +7 -7
  15. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +19 -15
  16. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts.map +1 -1
  17. package/dest/contract_function_simulator/oracle/utility_execution_oracle.js +42 -36
  18. package/dest/contract_sync/contract_sync_service.d.ts +41 -0
  19. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -0
  20. package/dest/contract_sync/contract_sync_service.js +82 -0
  21. package/dest/contract_sync/helpers.d.ts +28 -0
  22. package/dest/contract_sync/helpers.d.ts.map +1 -0
  23. package/dest/contract_sync/{index.js → helpers.js} +13 -12
  24. package/dest/debug/pxe_debug_utils.d.ts +12 -7
  25. package/dest/debug/pxe_debug_utils.d.ts.map +1 -1
  26. package/dest/debug/pxe_debug_utils.js +16 -12
  27. package/dest/entrypoints/client/bundle/utils.d.ts +1 -1
  28. package/dest/entrypoints/client/bundle/utils.d.ts.map +1 -1
  29. package/dest/entrypoints/client/bundle/utils.js +12 -6
  30. package/dest/entrypoints/client/lazy/utils.d.ts +2 -2
  31. package/dest/entrypoints/client/lazy/utils.d.ts.map +1 -1
  32. package/dest/entrypoints/client/lazy/utils.js +13 -7
  33. package/dest/entrypoints/pxe_creation_options.d.ts +3 -2
  34. package/dest/entrypoints/pxe_creation_options.d.ts.map +1 -1
  35. package/dest/entrypoints/server/index.d.ts +2 -2
  36. package/dest/entrypoints/server/index.d.ts.map +1 -1
  37. package/dest/entrypoints/server/index.js +1 -1
  38. package/dest/entrypoints/server/utils.d.ts +1 -1
  39. package/dest/entrypoints/server/utils.d.ts.map +1 -1
  40. package/dest/entrypoints/server/utils.js +19 -8
  41. package/dest/events/event_service.d.ts +4 -5
  42. package/dest/events/event_service.d.ts.map +1 -1
  43. package/dest/events/event_service.js +5 -6
  44. package/dest/job_coordinator/job_coordinator.d.ts +3 -2
  45. package/dest/job_coordinator/job_coordinator.d.ts.map +1 -1
  46. package/dest/job_coordinator/job_coordinator.js +3 -2
  47. package/dest/logs/log_service.d.ts +6 -5
  48. package/dest/logs/log_service.d.ts.map +1 -1
  49. package/dest/logs/log_service.js +10 -15
  50. package/dest/notes/note_service.d.ts +4 -5
  51. package/dest/notes/note_service.d.ts.map +1 -1
  52. package/dest/notes/note_service.js +6 -7
  53. package/dest/oracle_version.d.ts +3 -3
  54. package/dest/oracle_version.d.ts.map +1 -1
  55. package/dest/oracle_version.js +2 -2
  56. package/dest/private_kernel/private_kernel_execution_prover.d.ts +3 -2
  57. package/dest/private_kernel/private_kernel_execution_prover.d.ts.map +1 -1
  58. package/dest/private_kernel/private_kernel_execution_prover.js +2 -2
  59. package/dest/pxe.d.ts +2 -1
  60. package/dest/pxe.d.ts.map +1 -1
  61. package/dest/pxe.js +27 -14
  62. package/dest/storage/address_store/address_store.d.ts +1 -1
  63. package/dest/storage/address_store/address_store.d.ts.map +1 -1
  64. package/dest/storage/address_store/address_store.js +12 -11
  65. package/dest/storage/anchor_block_store/anchor_block_store.d.ts +9 -1
  66. package/dest/storage/anchor_block_store/anchor_block_store.d.ts.map +1 -1
  67. package/dest/storage/anchor_block_store/anchor_block_store.js +8 -1
  68. package/dest/storage/capsule_store/capsule_store.js +6 -8
  69. package/dest/storage/contract_store/contract_store.d.ts +1 -1
  70. package/dest/storage/contract_store/contract_store.d.ts.map +1 -1
  71. package/dest/storage/contract_store/contract_store.js +22 -13
  72. package/dest/storage/metadata.d.ts +1 -1
  73. package/dest/storage/metadata.js +1 -1
  74. package/dest/storage/note_store/note_store.d.ts +11 -1
  75. package/dest/storage/note_store/note_store.d.ts.map +1 -1
  76. package/dest/storage/note_store/note_store.js +146 -105
  77. package/dest/storage/private_event_store/private_event_store.d.ts +1 -1
  78. package/dest/storage/private_event_store/private_event_store.d.ts.map +1 -1
  79. package/dest/storage/private_event_store/private_event_store.js +84 -61
  80. package/dest/storage/private_event_store/stored_private_event.js +1 -1
  81. package/dest/storage/tagging_store/recipient_tagging_store.d.ts +1 -1
  82. package/dest/storage/tagging_store/recipient_tagging_store.d.ts.map +1 -1
  83. package/dest/storage/tagging_store/recipient_tagging_store.js +31 -19
  84. package/dest/storage/tagging_store/sender_address_book_store.d.ts +1 -1
  85. package/dest/storage/tagging_store/sender_address_book_store.d.ts.map +1 -1
  86. package/dest/storage/tagging_store/sender_address_book_store.js +20 -14
  87. package/dest/storage/tagging_store/sender_tagging_store.d.ts +1 -1
  88. package/dest/storage/tagging_store/sender_tagging_store.d.ts.map +1 -1
  89. package/dest/storage/tagging_store/sender_tagging_store.js +183 -113
  90. package/package.json +25 -16
  91. package/src/block_synchronizer/block_synchronizer.ts +21 -12
  92. package/src/contract_function_simulator/contract_function_simulator.ts +9 -6
  93. package/src/contract_function_simulator/oracle/interfaces.ts +9 -9
  94. package/src/contract_function_simulator/oracle/oracle.ts +32 -15
  95. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +7 -10
  96. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +47 -38
  97. package/src/contract_sync/contract_sync_service.ts +129 -0
  98. package/src/contract_sync/{index.ts → helpers.ts} +15 -20
  99. package/src/debug/pxe_debug_utils.ts +46 -13
  100. package/src/entrypoints/client/bundle/utils.ts +7 -14
  101. package/src/entrypoints/client/lazy/utils.ts +8 -14
  102. package/src/entrypoints/pxe_creation_options.ts +2 -1
  103. package/src/entrypoints/server/index.ts +1 -1
  104. package/src/entrypoints/server/utils.ts +15 -19
  105. package/src/events/event_service.ts +4 -6
  106. package/src/job_coordinator/job_coordinator.ts +4 -3
  107. package/src/logs/log_service.ts +14 -14
  108. package/src/notes/note_service.ts +5 -7
  109. package/src/oracle_version.ts +2 -2
  110. package/src/private_kernel/private_kernel_execution_prover.ts +6 -3
  111. package/src/pxe.ts +46 -18
  112. package/src/storage/address_store/address_store.ts +15 -15
  113. package/src/storage/anchor_block_store/anchor_block_store.ts +8 -0
  114. package/src/storage/capsule_store/capsule_store.ts +8 -8
  115. package/src/storage/contract_store/contract_store.ts +22 -11
  116. package/src/storage/metadata.ts +1 -1
  117. package/src/storage/note_store/note_store.ts +163 -129
  118. package/src/storage/private_event_store/private_event_store.ts +102 -81
  119. package/src/storage/private_event_store/stored_private_event.ts +1 -1
  120. package/src/storage/tagging_store/recipient_tagging_store.ts +31 -21
  121. package/src/storage/tagging_store/sender_address_book_store.ts +20 -14
  122. package/src/storage/tagging_store/sender_tagging_store.ts +210 -126
  123. package/dest/contract_sync/index.d.ts +0 -23
  124. package/dest/contract_sync/index.d.ts.map +0 -1
@@ -138,29 +138,39 @@ export class Oracle {
138
138
  }
139
139
 
140
140
  async utilityGetNoteHashMembershipWitness(
141
- [blockHash]: ACVMField[],
142
- [leafValue]: ACVMField[],
141
+ [anchorBlockHash]: ACVMField[],
142
+ [noteHash]: ACVMField[],
143
143
  ): Promise<(ACVMField | ACVMField[])[]> {
144
- const parsedBlockHash = BlockHash.fromString(blockHash);
145
- const parsedLeafValue = Fr.fromString(leafValue);
144
+ const parsedAnchorBlockHash = BlockHash.fromString(anchorBlockHash);
145
+ const parsedNoteHash = Fr.fromString(noteHash);
146
146
 
147
- const witness = await this.handlerAsUtility().utilityGetNoteHashMembershipWitness(parsedBlockHash, parsedLeafValue);
147
+ const witness = await this.handlerAsUtility().utilityGetNoteHashMembershipWitness(
148
+ parsedAnchorBlockHash,
149
+ parsedNoteHash,
150
+ );
148
151
  if (!witness) {
149
- throw new Error(`Leaf ${leafValue} not found in the note hash tree at block hash ${parsedBlockHash.toString()}.`);
152
+ throw new Error(
153
+ `Note hash ${noteHash} not found in the note hash tree at anchor block hash ${parsedAnchorBlockHash.toString()}.`,
154
+ );
150
155
  }
151
156
  return witness.toNoirRepresentation();
152
157
  }
153
158
 
154
- async utilityGetArchiveMembershipWitness(
159
+ async utilityGetBlockHashMembershipWitness(
160
+ [anchorBlockHash]: ACVMField[],
155
161
  [blockHash]: ACVMField[],
156
- [leafValue]: ACVMField[],
157
162
  ): Promise<(ACVMField | ACVMField[])[]> {
163
+ const parsedAnchorBlockHash = BlockHash.fromString(anchorBlockHash);
158
164
  const parsedBlockHash = BlockHash.fromString(blockHash);
159
- const parsedLeafValue = Fr.fromString(leafValue);
160
165
 
161
- const witness = await this.handlerAsUtility().utilityGetArchiveMembershipWitness(parsedBlockHash, parsedLeafValue);
166
+ const witness = await this.handlerAsUtility().utilityGetBlockHashMembershipWitness(
167
+ parsedAnchorBlockHash,
168
+ parsedBlockHash,
169
+ );
162
170
  if (!witness) {
163
- throw new Error(`Leaf ${leafValue} not found in the archive tree at block hash ${parsedBlockHash.toString()}.`);
171
+ throw new Error(
172
+ `Block hash ${parsedBlockHash.toString()} not found in the archive tree at anchor block ${parsedAnchorBlockHash.toString()}.`,
173
+ );
164
174
  }
165
175
  return witness.toNoirRepresentation();
166
176
  }
@@ -238,12 +248,19 @@ export class Oracle {
238
248
  return [witness.map(toACVMField)];
239
249
  }
240
250
 
241
- async utilityGetPublicKeysAndPartialAddress([address]: ACVMField[]): Promise<ACVMField[][]> {
251
+ async utilityTryGetPublicKeysAndPartialAddress([address]: ACVMField[]): Promise<(ACVMField | ACVMField[])[]> {
242
252
  const parsedAddress = AztecAddress.fromField(Fr.fromString(address));
243
- const { publicKeys, partialAddress } =
244
- await this.handlerAsUtility().utilityGetPublicKeysAndPartialAddress(parsedAddress);
253
+ const result = await this.handlerAsUtility().utilityTryGetPublicKeysAndPartialAddress(parsedAddress);
245
254
 
246
- return [[...publicKeys.toFields(), partialAddress].map(toACVMField)];
255
+ // We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct
256
+ // with two fields: `some` (a boolean) and `value` (a field array in this case).
257
+ if (result === undefined) {
258
+ // No data was found so we set `some` to 0 and pad `value` with zeros get the correct return size.
259
+ return [toACVMField(0), Array(13).fill(toACVMField(0))];
260
+ } else {
261
+ // Data was found so we set `some` to 1 and return it along with `value`.
262
+ return [toACVMField(1), [...result.publicKeys.toFields(), result.partialAddress].map(toACVMField)];
263
+ }
247
264
  }
248
265
 
249
266
  async utilityGetNotes(
@@ -30,10 +30,9 @@ import {
30
30
  type TxContext,
31
31
  } from '@aztec/stdlib/tx';
32
32
 
33
- import { ensureContractSynced } from '../../contract_sync/index.js';
33
+ import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
34
34
  import { NoteService } from '../../notes/note_service.js';
35
35
  import type { AddressStore } from '../../storage/address_store/address_store.js';
36
- import type { AnchorBlockStore } from '../../storage/anchor_block_store/anchor_block_store.js';
37
36
  import type { CapsuleStore } from '../../storage/capsule_store/capsule_store.js';
38
37
  import type { ContractStore } from '../../storage/contract_store/contract_store.js';
39
38
  import type { NoteStore } from '../../storage/note_store/note_store.js';
@@ -89,12 +88,12 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
89
88
  keyStore: KeyStore,
90
89
  addressStore: AddressStore,
91
90
  aztecNode: AztecNode,
92
- anchorBlockStore: AnchorBlockStore,
93
91
  private readonly senderTaggingStore: SenderTaggingStore,
94
92
  recipientTaggingStore: RecipientTaggingStore,
95
93
  senderAddressBookStore: SenderAddressBookStore,
96
94
  capsuleStore: CapsuleStore,
97
95
  privateEventStore: PrivateEventStore,
96
+ private readonly contractSyncService: ContractSyncService,
98
97
  jobId: string,
99
98
  private totalPublicCalldataCount: number = 0,
100
99
  protected sideEffectCounter: number = 0,
@@ -113,7 +112,6 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
113
112
  keyStore,
114
113
  addressStore,
115
114
  aztecNode,
116
- anchorBlockStore,
117
115
  recipientTaggingStore,
118
116
  senderAddressBookStore,
119
117
  capsuleStore,
@@ -245,7 +243,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
245
243
  sender: AztecAddress,
246
244
  recipient: AztecAddress,
247
245
  ) {
248
- const senderCompleteAddress = await this.getCompleteAddress(sender);
246
+ const senderCompleteAddress = await this.getCompleteAddressOrFail(sender);
249
247
  const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
250
248
  return DirectionalAppTaggingSecret.compute(
251
249
  senderCompleteAddress,
@@ -364,7 +362,7 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
364
362
 
365
363
  const pendingNullifiers = this.noteCache.getNullifiers(this.callContext.contractAddress);
366
364
 
367
- const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockStore, this.jobId);
365
+ const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
368
366
  const dbNotes = await noteService.getNotes(
369
367
  this.callContext.contractAddress,
370
368
  owner,
@@ -540,13 +538,12 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
540
538
 
541
539
  isStaticCall = isStaticCall || this.callContext.isStaticCall;
542
540
 
543
- await ensureContractSynced(
541
+ await this.contractSyncService.ensureContractSynced(
544
542
  targetContractAddress,
545
543
  functionSelector,
546
544
  this.utilityExecutor,
547
- this.aztecNode,
548
- this.contractStore,
549
545
  this.anchorBlockHeader,
546
+ this.jobId,
550
547
  );
551
548
 
552
549
  const targetArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(
@@ -574,12 +571,12 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
574
571
  this.keyStore,
575
572
  this.addressStore,
576
573
  this.aztecNode,
577
- this.anchorBlockStore,
578
574
  this.senderTaggingStore,
579
575
  this.recipientTaggingStore,
580
576
  this.senderAddressBookStore,
581
577
  this.capsuleStore,
582
578
  this.privateEventStore,
579
+ this.contractSyncService,
583
580
  this.jobId,
584
581
  this.totalPublicCalldataCount,
585
582
  sideEffectCounter,
@@ -25,7 +25,6 @@ import { LogService } from '../../logs/log_service.js';
25
25
  import { NoteService } from '../../notes/note_service.js';
26
26
  import { ORACLE_VERSION } from '../../oracle_version.js';
27
27
  import type { AddressStore } from '../../storage/address_store/address_store.js';
28
- import type { AnchorBlockStore } from '../../storage/anchor_block_store/anchor_block_store.js';
29
28
  import type { CapsuleStore } from '../../storage/capsule_store/capsule_store.js';
30
29
  import type { ContractStore } from '../../storage/contract_store/contract_store.js';
31
30
  import type { NoteStore } from '../../storage/note_store/note_store.js';
@@ -61,7 +60,6 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
61
60
  protected readonly keyStore: KeyStore,
62
61
  protected readonly addressStore: AddressStore,
63
62
  protected readonly aztecNode: AztecNode,
64
- protected readonly anchorBlockStore: AnchorBlockStore,
65
63
  protected readonly recipientTaggingStore: RecipientTaggingStore,
66
64
  protected readonly senderAddressBookStore: SenderAddressBookStore,
67
65
  protected readonly capsuleStore: CapsuleStore,
@@ -90,35 +88,49 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
90
88
  * @param pkMHash - The master public key hash.
91
89
  * @returns A Promise that resolves to nullifier keys.
92
90
  * @throws If the keys are not registered in the key store.
91
+ * @throws If scopes are defined and the account is not in the scopes.
93
92
  */
94
- public utilityGetKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
93
+ public async utilityGetKeyValidationRequest(pkMHash: Fr): Promise<KeyValidationRequest> {
94
+ // If scopes are defined, check that the key belongs to an account in the scopes
95
+ if (this.scopes && this.scopes.length > 0) {
96
+ const [, account] = await this.keyStore.getKeyPrefixAndAccount(pkMHash);
97
+ if (!this.scopes.some(scope => scope.equals(account))) {
98
+ throw new Error(`Key validation request denied: account ${account.toString()} is not in the allowed scopes.`);
99
+ }
100
+ }
95
101
  return this.keyStore.getKeyValidationRequest(pkMHash, this.contractAddress);
96
102
  }
97
103
 
98
104
  /**
99
105
  * Fetches the index and sibling path of a leaf at a given block from the note hash tree.
100
- * @param blockHash - The block hash at which to get the membership witness.
101
- * @param leafValue - The leaf value
106
+ * @param anchorBlockHash - The hash of a block that contains the note hash tree root in which to find the membership
107
+ * witness.
108
+ * @param noteHash - The note hash to find in the note hash tree.
102
109
  * @returns The membership witness containing the leaf index and sibling path
103
110
  */
104
111
  public utilityGetNoteHashMembershipWitness(
105
- blockHash: BlockHash,
106
- leafValue: Fr,
112
+ anchorBlockHash: BlockHash,
113
+ noteHash: Fr,
107
114
  ): Promise<MembershipWitness<typeof NOTE_HASH_TREE_HEIGHT> | undefined> {
108
- return this.aztecNode.getNoteHashMembershipWitness(blockHash, leafValue);
115
+ return this.aztecNode.getNoteHashMembershipWitness(anchorBlockHash, noteHash);
109
116
  }
110
117
 
111
118
  /**
112
- * Fetches the index and sibling path of a leaf at a given block from the archive tree.
113
- * @param blockHash - The block hash at which to get the membership witness.
114
- * @param leafValue - The leaf value
119
+ * Fetches the index and sibling path of a block hash in the archive tree.
120
+ *
121
+ * Block hashes are the leaves of the archive tree. Each time a new block is added to the chain,
122
+ * its block hash is appended as a new leaf to the archive tree.
123
+ *
124
+ * @param anchorBlockHash - The hash of a block that contains the archive tree root in which to find the membership
125
+ * witness.
126
+ * @param blockHash - The block hash to find in the archive tree.
115
127
  * @returns The membership witness containing the leaf index and sibling path
116
128
  */
117
- public utilityGetArchiveMembershipWitness(
129
+ public utilityGetBlockHashMembershipWitness(
130
+ anchorBlockHash: BlockHash,
118
131
  blockHash: BlockHash,
119
- leafValue: Fr,
120
132
  ): Promise<MembershipWitness<typeof ARCHIVE_HEIGHT> | undefined> {
121
- return this.aztecNode.getArchiveMembershipWitness(blockHash, leafValue);
133
+ return this.aztecNode.getBlockHashMembershipWitness(anchorBlockHash, blockHash);
122
134
  }
123
135
 
124
136
  /**
@@ -166,7 +178,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
166
178
  * @returns Block extracted from a block with block number `blockNumber`.
167
179
  */
168
180
  public async utilityGetBlockHeader(blockNumber: BlockNumber): Promise<BlockHeader | undefined> {
169
- const anchorBlockNumber = (await this.anchorBlockStore.getBlockHeader()).getBlockNumber();
181
+ const anchorBlockNumber = this.anchorBlockHeader.getBlockNumber();
170
182
  if (blockNumber > anchorBlockNumber) {
171
183
  throw new Error(`Block number ${blockNumber} is higher than current block ${anchorBlockNumber}`);
172
184
  }
@@ -178,14 +190,13 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
178
190
  /**
179
191
  * Retrieve the complete address associated to a given address.
180
192
  * @param account - The account address.
181
- * @returns A complete address associated with the input address.
182
- * @throws An error if the account is not registered in the database.
193
+ * @returns A complete address associated with the input address, or `undefined` if not registered.
183
194
  */
184
- public utilityGetPublicKeysAndPartialAddress(account: AztecAddress): Promise<CompleteAddress> {
185
- return this.getCompleteAddress(account);
195
+ public utilityTryGetPublicKeysAndPartialAddress(account: AztecAddress): Promise<CompleteAddress | undefined> {
196
+ return this.addressStore.getCompleteAddress(account);
186
197
  }
187
198
 
188
- protected async getCompleteAddress(account: AztecAddress): Promise<CompleteAddress> {
199
+ protected async getCompleteAddressOrFail(account: AztecAddress): Promise<CompleteAddress> {
189
200
  const completeAddress = await this.addressStore.getCompleteAddress(account);
190
201
  if (!completeAddress) {
191
202
  throw new Error(
@@ -262,7 +273,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
262
273
  offset: number,
263
274
  status: NoteStatus,
264
275
  ): Promise<NoteData[]> {
265
- const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockStore, this.jobId);
276
+ const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
266
277
 
267
278
  const dbNotes = await noteService.getNotes(this.contractAddress, owner, storageSlot, status, this.scopes);
268
279
  return pickNotes<NoteData>(dbNotes, {
@@ -286,8 +297,13 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
286
297
  * @returns A boolean indicating whether the nullifier exists in the tree or not.
287
298
  */
288
299
  public async utilityCheckNullifierExists(innerNullifier: Fr) {
289
- const nullifier = await siloNullifier(this.contractAddress, innerNullifier!);
290
- const [leafIndex] = await this.aztecNode.findLeavesIndexes('latest', MerkleTreeId.NULLIFIER_TREE, [nullifier]);
300
+ const [nullifier, anchorBlockHash] = await Promise.all([
301
+ siloNullifier(this.contractAddress, innerNullifier!),
302
+ this.anchorBlockHeader.hash(),
303
+ ]);
304
+ const [leafIndex] = await this.aztecNode.findLeavesIndexes(anchorBlockHash, MerkleTreeId.NULLIFIER_TREE, [
305
+ nullifier,
306
+ ]);
291
307
  return leafIndex?.data !== undefined;
292
308
  }
293
309
 
@@ -349,25 +365,17 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
349
365
  public async utilityFetchTaggedLogs(pendingTaggedLogArrayBaseSlot: Fr) {
350
366
  const logService = new LogService(
351
367
  this.aztecNode,
352
- this.anchorBlockStore,
368
+ this.anchorBlockHeader,
353
369
  this.keyStore,
354
370
  this.capsuleStore,
355
371
  this.recipientTaggingStore,
356
372
  this.senderAddressBookStore,
357
373
  this.addressStore,
358
374
  this.jobId,
375
+ this.log.getBindings(),
359
376
  );
360
377
 
361
- const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockStore, this.jobId);
362
-
363
- // It is acceptable to run the following operations in parallel for several reasons:
364
- // 1. syncTaggedLogs does not write to the note store — it only stores the pending tagged logs in a capsule array,
365
- // which is then processed in Noir after this handler returns.
366
- // 2. Even if syncTaggedLogs did write to the note store, it would not cause inconsistent state.
367
- await Promise.all([
368
- logService.syncTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes),
369
- noteService.syncNoteNullifiers(this.contractAddress),
370
- ]);
378
+ await logService.fetchTaggedLogs(this.contractAddress, pendingTaggedLogArrayBaseSlot, this.scopes);
371
379
  }
372
380
 
373
381
  /**
@@ -400,7 +408,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
400
408
  await this.capsuleStore.readCapsuleArray(contractAddress, eventValidationRequestsArrayBaseSlot, this.jobId)
401
409
  ).map(EventValidationRequest.fromFields);
402
410
 
403
- const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockStore, this.jobId);
411
+ const noteService = new NoteService(this.noteStore, this.aztecNode, this.anchorBlockHeader, this.jobId);
404
412
  const noteStorePromises = noteValidationRequests.map(request =>
405
413
  noteService.validateAndStoreNote(
406
414
  request.contractAddress,
@@ -416,7 +424,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
416
424
  ),
417
425
  );
418
426
 
419
- const eventService = new EventService(this.anchorBlockStore, this.aztecNode, this.privateEventStore, this.jobId);
427
+ const eventService = new EventService(this.anchorBlockHeader, this.aztecNode, this.privateEventStore, this.jobId);
420
428
  const eventStorePromises = eventValidationRequests.map(request =>
421
429
  eventService.validateAndStoreEvent(
422
430
  request.contractAddress,
@@ -454,13 +462,14 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
454
462
 
455
463
  const logService = new LogService(
456
464
  this.aztecNode,
457
- this.anchorBlockStore,
465
+ this.anchorBlockHeader,
458
466
  this.keyStore,
459
467
  this.capsuleStore,
460
468
  this.recipientTaggingStore,
461
469
  this.senderAddressBookStore,
462
470
  this.addressStore,
463
471
  this.jobId,
472
+ this.log.getBindings(),
464
473
  );
465
474
 
466
475
  const maybeLogRetrievalResponses = await logService.bulkRetrieveLogs(logRetrievalRequests);
@@ -538,7 +547,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
538
547
 
539
548
  protected async getSharedSecret(address: AztecAddress, ephPk: Point): Promise<Point> {
540
549
  // TODO(#12656): return an app-siloed secret
541
- const recipientCompleteAddress = await this.getCompleteAddress(address);
550
+ const recipientCompleteAddress = await this.getCompleteAddressOrFail(address);
542
551
  const ivskM = await this.keyStore.getMasterSecretKey(
543
552
  recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey,
544
553
  );
@@ -0,0 +1,129 @@
1
+ import type { Logger } from '@aztec/foundation/log';
2
+ import type { FunctionCall, FunctionSelector } from '@aztec/stdlib/abi';
3
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
+ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
5
+ import type { BlockHeader } from '@aztec/stdlib/tx';
6
+
7
+ import type { StagedStore } from '../job_coordinator/job_coordinator.js';
8
+ import type { ContractStore } from '../storage/contract_store/contract_store.js';
9
+ import type { NoteStore } from '../storage/note_store/note_store.js';
10
+ import { syncState, verifyCurrentClassId } from './helpers.js';
11
+
12
+ /**
13
+ * Service for syncing the private state of contracts and verifying that the PXE holds the current class artifact.
14
+ * It uses a cache to avoid redundant sync operations - the cache is wiped when the anchor block changes.
15
+ *
16
+ * TODO: The StagedStore naming is broken here. Figure out a better name.
17
+ */
18
+ export class ContractSyncService implements StagedStore {
19
+ readonly storeName = 'contract_sync';
20
+
21
+ // Tracks contracts synced since last wipe. Key is contract address string, value is a promise that resolves when
22
+ // the contract is synced.
23
+ private syncedContracts: Map<string, Promise<void>> = new Map();
24
+
25
+ // Per-job overridden contract addresses - these contracts should not be synced.
26
+ private overriddenContracts: Map<string, Set<string>> = new Map();
27
+
28
+ constructor(
29
+ private aztecNode: AztecNode,
30
+ private contractStore: ContractStore,
31
+ private noteStore: NoteStore,
32
+ private log: Logger,
33
+ ) {}
34
+
35
+ /** Sets contracts that should be skipped during sync for a specific job. */
36
+ setOverriddenContracts(jobId: string, addresses: Set<string>): void {
37
+ this.overriddenContracts.set(jobId, addresses);
38
+ }
39
+
40
+ /**
41
+ * Ensures a contract's private state is synchronized and that the PXE holds the current class artifact.
42
+ * Uses a cache to avoid redundant sync operations - the cache is wiped when the anchor block changes.
43
+ * @param contractAddress - The address of the contract to sync.
44
+ * @param functionToInvokeAfterSync - The function selector that will be called after sync (used to validate it's
45
+ * not sync_state itself).
46
+ * @param utilityExecutor - Executor function for running the sync_state utility function.
47
+ */
48
+ async ensureContractSynced(
49
+ contractAddress: AztecAddress,
50
+ functionToInvokeAfterSync: FunctionSelector | null,
51
+ utilityExecutor: (call: FunctionCall) => Promise<any>,
52
+ anchorBlockHeader: BlockHeader,
53
+ jobId: string,
54
+ ): Promise<void> {
55
+ const key = contractAddress.toString();
56
+
57
+ // Skip sync if this contract has an override for this job
58
+ const overrides = this.overriddenContracts.get(jobId);
59
+ if (overrides?.has(key)) {
60
+ return;
61
+ }
62
+
63
+ const existing = this.syncedContracts.get(key);
64
+ if (existing) {
65
+ return existing;
66
+ }
67
+
68
+ const syncPromise = this.#doSync(
69
+ contractAddress,
70
+ functionToInvokeAfterSync,
71
+ utilityExecutor,
72
+ anchorBlockHeader,
73
+ jobId,
74
+ );
75
+ this.syncedContracts.set(key, syncPromise);
76
+
77
+ try {
78
+ await syncPromise;
79
+ } catch (err) {
80
+ // There was an error syncing the contract, so we remove it from the cache so that it can be retried.
81
+ this.syncedContracts.delete(key);
82
+ throw err;
83
+ }
84
+ }
85
+
86
+ async #doSync(
87
+ contractAddress: AztecAddress,
88
+ functionToInvokeAfterSync: FunctionSelector | null,
89
+ utilityExecutor: (call: FunctionCall) => Promise<any>,
90
+ anchorBlockHeader: BlockHeader,
91
+ jobId: string,
92
+ ): Promise<void> {
93
+ this.log.debug(`Syncing contract ${contractAddress}`);
94
+ await Promise.all([
95
+ syncState(
96
+ contractAddress,
97
+ this.contractStore,
98
+ functionToInvokeAfterSync,
99
+ utilityExecutor,
100
+ this.noteStore,
101
+ this.aztecNode,
102
+ anchorBlockHeader,
103
+ jobId,
104
+ ),
105
+ verifyCurrentClassId(contractAddress, this.aztecNode, this.contractStore, anchorBlockHeader),
106
+ ]);
107
+ this.log.debug(`Contract ${contractAddress} synced`);
108
+ }
109
+
110
+ /** Clears sync cache. Called by BlockSynchronizer when anchor block changes. */
111
+ wipe(): void {
112
+ this.log.debug(`Wiping contract sync cache (${this.syncedContracts.size} entries)`);
113
+ this.syncedContracts.clear();
114
+ }
115
+
116
+ commit(jobId: string): Promise<void> {
117
+ // Clear overridden contracts for this job
118
+ this.overriddenContracts.delete(jobId);
119
+ return Promise.resolve();
120
+ }
121
+
122
+ discardStaged(jobId: string): Promise<void> {
123
+ // We clear the synced contracts cache here because, when the job is discarded, any associated database writes from
124
+ // the sync are also undone.
125
+ this.syncedContracts.clear();
126
+ this.overriddenContracts.delete(jobId);
127
+ return Promise.resolve();
128
+ }
129
+ }
@@ -6,7 +6,9 @@ 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 { NoteService } from '../notes/note_service.js';
9
10
  import type { ContractStore } from '../storage/contract_store/contract_store.js';
11
+ import type { NoteStore } from '../storage/note_store/note_store.js';
10
12
 
11
13
  /**
12
14
  * Read the current class id of a contract from the execution data provider or AztecNode. If not found, class id
@@ -41,6 +43,10 @@ export async function syncState(
41
43
  contractStore: ContractStore,
42
44
  functionToInvokeAfterSync: FunctionSelector | null,
43
45
  utilityExecutor: (privateSyncCall: FunctionCall) => Promise<any>,
46
+ noteStore: NoteStore,
47
+ aztecNode: AztecNode,
48
+ anchorBlockHeader: BlockHeader,
49
+ jobId: string,
44
50
  ) {
45
51
  // Protocol contracts don't have private state to sync
46
52
  if (!isProtocolContract(contractAddress)) {
@@ -51,16 +57,23 @@ export async function syncState(
51
57
  );
52
58
  }
53
59
 
54
- return utilityExecutor(syncStateFunctionCall);
60
+ const noteService = new NoteService(noteStore, aztecNode, anchorBlockHeader, jobId);
61
+
62
+ // Both sync_state and syncNoteNullifiers interact with the note store, but running them in parallel is safe
63
+ // because note store is designed to handle concurrent operations.
64
+ await Promise.all([utilityExecutor(syncStateFunctionCall), noteService.syncNoteNullifiers(contractAddress)]);
55
65
  }
56
66
  }
57
67
 
58
68
  /**
59
69
  * Verify that the current class id of a contract obtained from AztecNode is the same as the one in contract data
60
70
  * provider (i.e. PXE's own storage).
71
+ * @param contractAddress - The address of the contract to verify.
72
+ * @param aztecNode - The Aztec node to query for storage.
73
+ * @param contractStore - The contract store to fetch the local instance from.
61
74
  * @param header - The header of the block at which to verify the current class id.
62
75
  */
63
- async function verifyCurrentClassId(
76
+ export async function verifyCurrentClassId(
64
77
  contractAddress: AztecAddress,
65
78
  aztecNode: AztecNode,
66
79
  contractStore: ContractStore,
@@ -78,21 +91,3 @@ async function verifyCurrentClassId(
78
91
  );
79
92
  }
80
93
  }
81
-
82
- /**
83
- * Ensures the contract's private state is synchronized and that the PXE holds the current class artifact for
84
- * the contract.
85
- */
86
- export async function ensureContractSynced(
87
- contractAddress: AztecAddress,
88
- functionToInvokeAfterSync: FunctionSelector | null,
89
- utilityExecutor: (call: FunctionCall) => Promise<any>,
90
- aztecNode: AztecNode,
91
- contractStore: ContractStore,
92
- header: BlockHeader,
93
- ): Promise<void> {
94
- await Promise.all([
95
- syncState(contractAddress, contractStore, functionToInvokeAfterSync, utilityExecutor),
96
- verifyCurrentClassId(contractAddress, aztecNode, contractStore, header),
97
- ]);
98
- }
@@ -1,10 +1,12 @@
1
- import { randomBytes } from '@aztec/foundation/crypto/random';
1
+ import type { FunctionCall } from '@aztec/stdlib/abi';
2
+ import type { AuthWitness } from '@aztec/stdlib/auth-witness';
3
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
2
4
  import type { NoteDao, NotesFilter } from '@aztec/stdlib/note';
3
- import type { BlockHeader } from '@aztec/stdlib/tx';
5
+ import type { BlockHeader, ContractOverrides } from '@aztec/stdlib/tx';
4
6
 
5
7
  import type { BlockSynchronizer } from '../block_synchronizer/block_synchronizer.js';
6
- import type { PXE } from '../pxe.js';
7
- import type { ContractStore } from '../storage/contract_store/contract_store.js';
8
+ import type { ContractFunctionSimulator } from '../contract_function_simulator/contract_function_simulator.js';
9
+ import type { ContractSyncService } from '../contract_sync/contract_sync_service.js';
8
10
  import type { AnchorBlockStore } from '../storage/index.js';
9
11
  import type { NoteStore } from '../storage/note_store/note_store.js';
10
12
 
@@ -13,20 +15,38 @@ import type { NoteStore } from '../storage/note_store/note_store.js';
13
15
  * No backwards compatibility or API stability should be expected. Use at your own risk.
14
16
  */
15
17
  export class PXEDebugUtils {
16
- #pxe!: PXE;
17
18
  #putJobInQueue!: <T>(job: (jobId: string) => Promise<T>) => Promise<T>;
19
+ #getSimulatorForTx!: (overrides?: { contracts?: ContractOverrides }) => ContractFunctionSimulator;
20
+ #simulateUtility!: (
21
+ contractFunctionSimulator: ContractFunctionSimulator,
22
+ call: FunctionCall,
23
+ authWitnesses: AuthWitness[] | undefined,
24
+ scopes: AztecAddress[] | undefined,
25
+ jobId: string,
26
+ ) => Promise<any>;
18
27
 
19
28
  constructor(
20
- private contractStore: ContractStore,
29
+ private contractSyncService: ContractSyncService,
21
30
  private noteStore: NoteStore,
22
31
  private blockStateSynchronizer: BlockSynchronizer,
23
32
  private anchorBlockStore: AnchorBlockStore,
24
33
  ) {}
25
34
 
26
35
  /** Not injected through constructor since they're are co-dependant */
27
- public setPXE(pxe: PXE, putJobInQueue: <T>(job: (jobId: string) => Promise<T>) => Promise<T>) {
28
- this.#pxe = pxe;
36
+ public setPXEHelpers(
37
+ putJobInQueue: <T>(job: (jobId: string) => Promise<T>) => Promise<T>,
38
+ getSimulatorForTx: (overrides?: { contracts?: ContractOverrides }) => ContractFunctionSimulator,
39
+ simulateUtility: (
40
+ contractFunctionSimulator: ContractFunctionSimulator,
41
+ call: FunctionCall,
42
+ authWitnesses: AuthWitness[] | undefined,
43
+ scopes: AztecAddress[] | undefined,
44
+ jobId: string,
45
+ ) => Promise<any>,
46
+ ) {
29
47
  this.#putJobInQueue = putJobInQueue;
48
+ this.#getSimulatorForTx = getSimulatorForTx;
49
+ this.#simulateUtility = simulateUtility;
30
50
  }
31
51
 
32
52
  /**
@@ -40,12 +60,25 @@ export class PXEDebugUtils {
40
60
  * @param filter - The filter to apply to the notes.
41
61
  * @returns The requested notes.
42
62
  */
43
- public async getNotes(filter: NotesFilter): Promise<NoteDao[]> {
44
- // We need to manually trigger private state sync to have a guarantee that all the notes are available.
45
- const call = await this.contractStore.getFunctionCall('sync_state', [], filter.contractAddress);
46
- await this.#pxe.simulateUtility(call);
63
+ public getNotes(filter: NotesFilter): Promise<NoteDao[]> {
64
+ return this.#putJobInQueue(async (jobId: string) => {
65
+ await this.blockStateSynchronizer.sync();
47
66
 
48
- return this.noteStore.getNotes(filter, randomBytes(8).toString('hex'));
67
+ const anchorBlockHeader = await this.anchorBlockStore.getBlockHeader();
68
+
69
+ const contractFunctionSimulator = this.#getSimulatorForTx();
70
+
71
+ await this.contractSyncService.ensureContractSynced(
72
+ filter.contractAddress,
73
+ null,
74
+ async privateSyncCall =>
75
+ await this.#simulateUtility(contractFunctionSimulator, privateSyncCall, [], undefined, jobId),
76
+ anchorBlockHeader,
77
+ jobId,
78
+ );
79
+
80
+ return this.noteStore.getNotes(filter, jobId);
81
+ });
49
82
  }
50
83
 
51
84
  /** Returns the block header up to which the PXE has synced. */