@aztec/pxe 0.0.1-commit.88c5703d4 → 0.0.1-commit.88e6f9396

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 (35) hide show
  1. package/dest/contract_function_simulator/contract_function_simulator.d.ts +1 -1
  2. package/dest/contract_function_simulator/contract_function_simulator.d.ts.map +1 -1
  3. package/dest/contract_function_simulator/contract_function_simulator.js +17 -2
  4. package/dest/contract_function_simulator/oracle/interfaces.d.ts +2 -1
  5. package/dest/contract_function_simulator/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts +1 -1
  7. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.d.ts.map +1 -1
  8. package/dest/contract_function_simulator/oracle/legacy_oracle_mappings.js +22 -32
  9. package/dest/contract_function_simulator/oracle/oracle.d.ts +3 -2
  10. package/dest/contract_function_simulator/oracle/oracle.d.ts.map +1 -1
  11. package/dest/contract_function_simulator/oracle/oracle.js +24 -4
  12. package/dest/contract_function_simulator/oracle/private_execution_oracle.d.ts +1 -4
  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 +0 -2
  15. package/dest/contract_function_simulator/oracle/utility_execution_oracle.d.ts +9 -1
  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 +11 -0
  18. package/dest/contract_sync/contract_sync_service.d.ts +5 -3
  19. package/dest/contract_sync/contract_sync_service.d.ts.map +1 -1
  20. package/dest/contract_sync/contract_sync_service.js +47 -30
  21. package/dest/oracle_version.d.ts +2 -2
  22. package/dest/oracle_version.js +2 -2
  23. package/dest/pxe.d.ts +5 -3
  24. package/dest/pxe.d.ts.map +1 -1
  25. package/dest/pxe.js +11 -8
  26. package/package.json +16 -16
  27. package/src/contract_function_simulator/contract_function_simulator.ts +24 -2
  28. package/src/contract_function_simulator/oracle/interfaces.ts +1 -0
  29. package/src/contract_function_simulator/oracle/legacy_oracle_mappings.ts +103 -45
  30. package/src/contract_function_simulator/oracle/oracle.ts +24 -4
  31. package/src/contract_function_simulator/oracle/private_execution_oracle.ts +0 -4
  32. package/src/contract_function_simulator/oracle/utility_execution_oracle.ts +15 -0
  33. package/src/contract_sync/contract_sync_service.ts +67 -38
  34. package/src/oracle_version.ts +2 -2
  35. package/src/pxe.ts +24 -9
@@ -11,39 +11,101 @@ import type { Oracle } from './oracle.js';
11
11
  export function buildLegacyOracleCallbacks(oracle: Oracle): ACIRCallback {
12
12
  return {
13
13
  // Simple prefix renames (privateXxx/utilityXxx → aztec_prv_/aztec_utl_)
14
- utilityLog: (...args: ACVMField[][]) => oracle.aztec_utl_log(args[0], args[1], args[2], args[3]),
15
- utilityAssertCompatibleOracleVersion: (...args: ACVMField[][]) =>
16
- oracle.aztec_utl_assertCompatibleOracleVersion(args[0]),
17
- utilityLoadCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_loadCapsule(args[0], args[1], args[2]),
18
- privateStoreInExecutionCache: (...args: ACVMField[][]) => oracle.aztec_prv_storeInExecutionCache(args[0], args[1]),
19
- privateLoadFromExecutionCache: (...args: ACVMField[][]) => oracle.aztec_prv_loadFromExecutionCache(args[0]),
20
- privateCallPrivateFunction: (...args: ACVMField[][]) =>
21
- oracle.aztec_prv_callPrivateFunction(args[0], args[1], args[2], args[3], args[4]),
22
- privateIsNullifierPending: (...args: ACVMField[][]) => oracle.aztec_prv_isNullifierPending(args[0], args[1]),
23
- privateNotifyCreatedNullifier: (...args: ACVMField[][]) => oracle.aztec_prv_notifyCreatedNullifier(args[0]),
24
- privateNotifyCreatedContractClassLog: (...args: ACVMField[][]) =>
25
- oracle.aztec_prv_notifyCreatedContractClassLog(args[0], args[1], args[2], args[3]),
26
- privateGetNextAppTagAsSender: (...args: ACVMField[][]) => oracle.aztec_prv_getNextAppTagAsSender(args[0], args[1]),
27
- privateGetSenderForTags: () => oracle.aztec_prv_getSenderForTags(),
28
- privateSetSenderForTags: (...args: ACVMField[][]) => oracle.aztec_prv_setSenderForTags(args[0]),
29
- utilityGetUtilityContext: () => oracle.aztec_utl_getUtilityContext(),
30
- utilityStorageRead: (...args: ACVMField[][]) => oracle.aztec_utl_storageRead(args[0], args[1], args[2], args[3]),
31
- utilityStoreCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_storeCapsule(args[0], args[1], args[2]),
32
- utilityCopyCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_copyCapsule(args[0], args[1], args[2], args[3]),
33
- utilityDeleteCapsule: (...args: ACVMField[][]) => oracle.aztec_utl_deleteCapsule(args[0], args[1]),
34
- utilityAes128Decrypt: (...args: ACVMField[][]) =>
35
- oracle.aztec_utl_aes128Decrypt(args[0], args[1], args[2], args[3]),
36
- utilityGetSharedSecret: (...args: ACVMField[][]) =>
37
- oracle.aztec_utl_getSharedSecret(args[0], args[1], args[2], args[3]),
38
- utilityFetchTaggedLogs: (...args: ACVMField[][]) => oracle.aztec_utl_fetchTaggedLogs(args[0]),
39
- utilityBulkRetrieveLogs: (...args: ACVMField[][]) => oracle.aztec_utl_bulkRetrieveLogs(args[0], args[1], args[2]),
14
+ utilityLog: (
15
+ level: ACVMField[],
16
+ message: ACVMField[],
17
+ _ignoredFieldsSize: ACVMField[],
18
+ fields: ACVMField[],
19
+ ): Promise<ACVMField[]> => oracle.aztec_utl_log(level, message, _ignoredFieldsSize, fields),
20
+ utilityAssertCompatibleOracleVersion: (version: ACVMField[]): Promise<ACVMField[]> =>
21
+ oracle.aztec_utl_assertCompatibleOracleVersion(version),
22
+ utilityLoadCapsule: (
23
+ contractAddress: ACVMField[],
24
+ slot: ACVMField[],
25
+ tSize: ACVMField[],
26
+ ): Promise<(ACVMField | ACVMField[])[]> => oracle.aztec_utl_loadCapsule(contractAddress, slot, tSize),
27
+ privateStoreInExecutionCache: (values: ACVMField[], hash: ACVMField[]): Promise<ACVMField[]> =>
28
+ oracle.aztec_prv_storeInExecutionCache(values, hash),
29
+ privateLoadFromExecutionCache: (returnsHash: ACVMField[]): Promise<ACVMField[][]> =>
30
+ oracle.aztec_prv_loadFromExecutionCache(returnsHash),
31
+ privateCallPrivateFunction: (
32
+ contractAddress: ACVMField[],
33
+ functionSelector: ACVMField[],
34
+ argsHash: ACVMField[],
35
+ sideEffectCounter: ACVMField[],
36
+ isStaticCall: ACVMField[],
37
+ ): Promise<ACVMField[][]> =>
38
+ oracle.aztec_prv_callPrivateFunction(
39
+ contractAddress,
40
+ functionSelector,
41
+ argsHash,
42
+ sideEffectCounter,
43
+ isStaticCall,
44
+ ),
45
+ privateIsNullifierPending: (innerNullifier: ACVMField[], contractAddress: ACVMField[]): Promise<ACVMField[]> =>
46
+ oracle.aztec_prv_isNullifierPending(innerNullifier, contractAddress),
47
+ privateNotifyCreatedNullifier: (innerNullifier: ACVMField[]): Promise<ACVMField[]> =>
48
+ oracle.aztec_prv_notifyCreatedNullifier(innerNullifier),
49
+ privateNotifyCreatedContractClassLog: (
50
+ contractAddress: ACVMField[],
51
+ message: ACVMField[],
52
+ length: ACVMField[],
53
+ counter: ACVMField[],
54
+ ): Promise<ACVMField[]> =>
55
+ oracle.aztec_prv_notifyCreatedContractClassLog(contractAddress, message, length, counter),
56
+ utilityGetUtilityContext: (): Promise<(ACVMField | ACVMField[])[]> => oracle.aztec_utl_getUtilityContext(),
57
+ utilityStorageRead: (
58
+ blockHash: ACVMField[],
59
+ contractAddress: ACVMField[],
60
+ startStorageSlot: ACVMField[],
61
+ numberOfElements: ACVMField[],
62
+ ): Promise<ACVMField[][]> =>
63
+ oracle.aztec_utl_storageRead(blockHash, contractAddress, startStorageSlot, numberOfElements),
64
+ utilityStoreCapsule: (
65
+ contractAddress: ACVMField[],
66
+ slot: ACVMField[],
67
+ capsule: ACVMField[],
68
+ ): Promise<ACVMField[]> => oracle.aztec_utl_storeCapsule(contractAddress, slot, capsule),
69
+ utilityCopyCapsule: (
70
+ contractAddress: ACVMField[],
71
+ srcSlot: ACVMField[],
72
+ dstSlot: ACVMField[],
73
+ numEntries: ACVMField[],
74
+ ): Promise<ACVMField[]> => oracle.aztec_utl_copyCapsule(contractAddress, srcSlot, dstSlot, numEntries),
75
+ utilityDeleteCapsule: (contractAddress: ACVMField[], slot: ACVMField[]): Promise<ACVMField[]> =>
76
+ oracle.aztec_utl_deleteCapsule(contractAddress, slot),
77
+ utilityGetSharedSecret: (
78
+ address: ACVMField[],
79
+ ephPKField0: ACVMField[],
80
+ ephPKField1: ACVMField[],
81
+ ephPKField2: ACVMField[],
82
+ ): Promise<ACVMField[]> => oracle.aztec_utl_getSharedSecret(address, ephPKField0, ephPKField1, ephPKField2),
83
+ utilityFetchTaggedLogs: (pendingTaggedLogArrayBaseSlot: ACVMField[]): Promise<ACVMField[]> =>
84
+ oracle.aztec_utl_fetchTaggedLogs(pendingTaggedLogArrayBaseSlot),
85
+ utilityBulkRetrieveLogs: (
86
+ contractAddress: ACVMField[],
87
+ logRetrievalRequestsArrayBaseSlot: ACVMField[],
88
+ logRetrievalResponsesArrayBaseSlot: ACVMField[],
89
+ ): Promise<ACVMField[]> =>
90
+ oracle.aztec_utl_bulkRetrieveLogs(
91
+ contractAddress,
92
+ logRetrievalRequestsArrayBaseSlot,
93
+ logRetrievalResponsesArrayBaseSlot,
94
+ ),
95
+ utilityGetL1ToL2MembershipWitness: (
96
+ contractAddress: ACVMField[],
97
+ messageHash: ACVMField[],
98
+ secret: ACVMField[],
99
+ ): Promise<(ACVMField | ACVMField[])[]> =>
100
+ oracle.aztec_utl_getL1ToL2MembershipWitness(contractAddress, messageHash, secret),
101
+ utilityEmitOffchainEffect: (data: ACVMField[]): Promise<ACVMField[]> => oracle.aztec_utl_emitOffchainEffect(data),
40
102
  // Adapter: old 3-param signature → new 5-param with injected constants.
41
103
  // Values derived from: MAX_MESSAGE_CONTENT_LEN(11) - RESERVED_FIELDS (3 for notes, 1 for events).
42
104
  utilityValidateAndStoreEnqueuedNotesAndEvents: (
43
105
  contractAddress: ACVMField[],
44
106
  noteValidationRequestsArrayBaseSlot: ACVMField[],
45
107
  eventValidationRequestsArrayBaseSlot: ACVMField[],
46
- ) =>
108
+ ): Promise<ACVMField[]> =>
47
109
  oracle.aztec_utl_validateAndStoreEnqueuedNotesAndEvents(
48
110
  contractAddress,
49
111
  noteValidationRequestsArrayBaseSlot,
@@ -51,27 +113,23 @@ export function buildLegacyOracleCallbacks(oracle: Oracle): ACIRCallback {
51
113
  [new Fr(8).toString()],
52
114
  [new Fr(10).toString()],
53
115
  ),
54
- utilityGetL1ToL2MembershipWitness: (...args: ACVMField[][]) =>
55
- oracle.aztec_utl_getL1ToL2MembershipWitness(args[0], args[1], args[2]),
56
- utilityCheckNullifierExists: (...args: ACVMField[][]) => oracle.aztec_utl_checkNullifierExists(args[0]),
57
- utilityGetRandomField: () => oracle.aztec_utl_getRandomField(),
58
- utilityEmitOffchainEffect: (...args: ACVMField[][]) => oracle.aztec_utl_emitOffchainEffect(args[0]),
59
116
  // Renames (same signature, different oracle name)
60
- privateNotifySetMinRevertibleSideEffectCounter: (...args: ACVMField[][]) =>
61
- oracle.aztec_prv_notifyRevertiblePhaseStart(args[0]),
62
- privateIsSideEffectCounterRevertible: (...args: ACVMField[][]) => oracle.aztec_prv_inRevertiblePhase(args[0]),
117
+ privateNotifySetMinRevertibleSideEffectCounter: (counter: ACVMField[]): Promise<ACVMField[]> =>
118
+ oracle.aztec_prv_notifyRevertiblePhaseStart(counter),
119
+ privateIsSideEffectCounterRevertible: (sideEffectCounter: ACVMField[]): Promise<ACVMField[]> =>
120
+ oracle.aztec_prv_inRevertiblePhase(sideEffectCounter),
63
121
  // Signature changes: old 4-param oracles → new 1-param validatePublicCalldata
64
122
  privateNotifyEnqueuedPublicFunctionCall: (
65
- [_contractAddress]: ACVMField[],
66
- [calldataHash]: ACVMField[],
67
- [_sideEffectCounter]: ACVMField[],
68
- [_isStaticCall]: ACVMField[],
69
- ) => oracle.aztec_prv_validatePublicCalldata([calldataHash]),
123
+ _contractAddress: ACVMField[],
124
+ calldataHash: ACVMField[],
125
+ _sideEffectCounter: ACVMField[],
126
+ _isStaticCall: ACVMField[],
127
+ ): Promise<ACVMField[]> => oracle.aztec_prv_validatePublicCalldata(calldataHash),
70
128
  privateNotifySetPublicTeardownFunctionCall: (
71
- [_contractAddress]: ACVMField[],
72
- [calldataHash]: ACVMField[],
73
- [_sideEffectCounter]: ACVMField[],
74
- [_isStaticCall]: ACVMField[],
75
- ) => oracle.aztec_prv_validatePublicCalldata([calldataHash]),
129
+ _contractAddress: ACVMField[],
130
+ calldataHash: ACVMField[],
131
+ _sideEffectCounter: ACVMField[],
132
+ _isStaticCall: ACVMField[],
133
+ ): Promise<ACVMField[]> => oracle.aztec_prv_validatePublicCalldata(calldataHash),
76
134
  };
77
135
  }
@@ -604,9 +604,8 @@ export class Oracle {
604
604
  return [];
605
605
  }
606
606
 
607
- // TODO(F-452): Return Option and wrap in try/catch so BB exceptions don't crash PXE.
608
607
  // eslint-disable-next-line camelcase
609
- async aztec_utl_aes128Decrypt(
608
+ async aztec_utl_tryAes128Decrypt(
610
609
  ciphertextBVecStorage: ACVMField[],
611
610
  [ciphertextLength]: ACVMField[],
612
611
  iv: ACVMField[],
@@ -616,8 +615,15 @@ export class Oracle {
616
615
  const ivBuffer = fromUintArray(iv, 8);
617
616
  const symKeyBuffer = fromUintArray(symKey, 8);
618
617
 
619
- const plaintext = await this.handlerAsUtility().aes128Decrypt(ciphertext, ivBuffer, symKeyBuffer);
620
- return bufferToBoundedVec(plaintext, ciphertextBVecStorage.length);
618
+ // Noir Option<BoundedVec> is encoded as [is_some: Field, storage: Field[], length: Field].
619
+ try {
620
+ const plaintext = await this.handlerAsUtility().aes128Decrypt(ciphertext, ivBuffer, symKeyBuffer);
621
+ const [storage, length] = bufferToBoundedVec(plaintext, ciphertextBVecStorage.length);
622
+ return [toACVMField(1), storage, length];
623
+ } catch {
624
+ const zeroStorage = Array(ciphertextBVecStorage.length).fill(toACVMField(0));
625
+ return [toACVMField(0), zeroStorage, toACVMField(0)];
626
+ }
621
627
  }
622
628
 
623
629
  // eslint-disable-next-line camelcase
@@ -634,6 +640,20 @@ export class Oracle {
634
640
  return secret.toFields().map(toACVMField);
635
641
  }
636
642
 
643
+ // eslint-disable-next-line camelcase
644
+ aztec_utl_invalidateContractSyncCache(
645
+ [contractAddress]: ACVMField[],
646
+ scopes: ACVMField[],
647
+ [scopeCount]: ACVMField[],
648
+ ): Promise<ACVMField[]> {
649
+ const scopeAddresses = scopes.slice(0, +scopeCount).map(s => AztecAddress.fromField(Fr.fromString(s)));
650
+ this.handlerAsUtility().invalidateContractSyncCache(
651
+ AztecAddress.fromField(Fr.fromString(contractAddress)),
652
+ scopeAddresses,
653
+ );
654
+ return Promise.resolve([]);
655
+ }
656
+
637
657
  // eslint-disable-next-line camelcase
638
658
  async aztec_utl_emitOffchainEffect(data: ACVMField[]) {
639
659
  await this.handlerAsUtility().emitOffchainEffect(data.map(Fr.fromString));
@@ -26,7 +26,6 @@ import {
26
26
  } from '@aztec/stdlib/tx';
27
27
 
28
28
  import type { AccessScopes } from '../../access_scopes.js';
29
- import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
30
29
  import { NoteService } from '../../notes/note_service.js';
31
30
  import type { SenderTaggingStore } from '../../storage/tagging_store/sender_tagging_store.js';
32
31
  import { syncSenderTaggingIndexes } from '../../tagging/index.js';
@@ -49,7 +48,6 @@ export type PrivateExecutionOracleArgs = Omit<UtilityExecutionOracleArgs, 'contr
49
48
  noteCache: ExecutionNoteCache;
50
49
  taggingIndexCache: ExecutionTaggingIndexCache;
51
50
  senderTaggingStore: SenderTaggingStore;
52
- contractSyncService: ContractSyncService;
53
51
  totalPublicCalldataCount?: number;
54
52
  sideEffectCounter?: number;
55
53
  senderForTags?: AztecAddress;
@@ -83,7 +81,6 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
83
81
  private readonly noteCache: ExecutionNoteCache;
84
82
  private readonly taggingIndexCache: ExecutionTaggingIndexCache;
85
83
  private readonly senderTaggingStore: SenderTaggingStore;
86
- private readonly contractSyncService: ContractSyncService;
87
84
  private totalPublicCalldataCount: number;
88
85
  protected sideEffectCounter: number;
89
86
  private senderForTags?: AztecAddress;
@@ -103,7 +100,6 @@ export class PrivateExecutionOracle extends UtilityExecutionOracle implements IP
103
100
  this.noteCache = args.noteCache;
104
101
  this.taggingIndexCache = args.taggingIndexCache;
105
102
  this.senderTaggingStore = args.senderTaggingStore;
106
- this.contractSyncService = args.contractSyncService;
107
103
  this.totalPublicCalldataCount = args.totalPublicCalldataCount ?? 0;
108
104
  this.sideEffectCounter = args.sideEffectCounter ?? 0;
109
105
  this.senderForTags = args.senderForTags;
@@ -23,6 +23,7 @@ import type { BlockHeader, Capsule, OffchainEffect } from '@aztec/stdlib/tx';
23
23
 
24
24
  import type { AccessScopes } from '../../access_scopes.js';
25
25
  import { createContractLogger, logContractMessage } from '../../contract_logging.js';
26
+ import type { ContractSyncService } from '../../contract_sync/contract_sync_service.js';
26
27
  import { EventService } from '../../events/event_service.js';
27
28
  import { LogService } from '../../logs/log_service.js';
28
29
  import { MessageContextService } from '../../messages/message_context_service.js';
@@ -62,6 +63,7 @@ export type UtilityExecutionOracleArgs = {
62
63
  capsuleStore: CapsuleStore;
63
64
  privateEventStore: PrivateEventStore;
64
65
  messageContextService: MessageContextService;
66
+ contractSyncService: ContractSyncService;
65
67
  jobId: string;
66
68
  log?: ReturnType<typeof createLogger>;
67
69
  scopes: AccessScopes;
@@ -91,6 +93,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
91
93
  protected readonly capsuleStore: CapsuleStore;
92
94
  protected readonly privateEventStore: PrivateEventStore;
93
95
  protected readonly messageContextService: MessageContextService;
96
+ protected readonly contractSyncService: ContractSyncService;
94
97
  protected readonly jobId: string;
95
98
  protected logger: ReturnType<typeof createLogger>;
96
99
  protected readonly scopes: AccessScopes;
@@ -110,6 +113,7 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
110
113
  this.capsuleStore = args.capsuleStore;
111
114
  this.privateEventStore = args.privateEventStore;
112
115
  this.messageContextService = args.messageContextService;
116
+ this.contractSyncService = args.contractSyncService;
113
117
  this.jobId = args.jobId;
114
118
  this.logger = args.log ?? createLogger('simulator:client_view_context');
115
119
  this.scopes = args.scopes;
@@ -651,6 +655,17 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
651
655
  return this.capsuleStore.copyCapsule(this.contractAddress, srcSlot, dstSlot, numEntries, this.jobId);
652
656
  }
653
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
+
654
669
  // TODO(#11849): consider replacing this oracle with a pure Noir implementation of aes decryption.
655
670
  public aes128Decrypt(ciphertext: Buffer, iv: Buffer, symKey: Buffer): Promise<Buffer> {
656
671
  const aes128 = new Aes128();
@@ -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) {
@@ -4,9 +4,9 @@
4
4
  ///
5
5
  /// @dev Whenever a contract function or Noir test is run, the `aztec_utl_assertCompatibleOracleVersion` oracle is called
6
6
  /// and if the oracle version is incompatible an error is thrown.
7
- export const ORACLE_VERSION = 16;
7
+ export const ORACLE_VERSION = 18;
8
8
 
9
9
  /// This hash is computed as by hashing the Oracle interface and it is used to detect when the Oracle interface changes,
10
10
  /// which in turn implies that you need to update the ORACLE_VERSION constant in this file and in
11
11
  /// `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
12
- export const ORACLE_INTERFACE_HASH = '73ccb2a24bc9fe7514108be9ff98d7ca8734bc316fb7c1ec4329d1d32f412a55';
12
+ export const ORACLE_INTERFACE_HASH = '57e5b07c6d55fb167ef90f8d0f410f9bdb5b154a31159c624a061be40b02a2c2';
package/src/pxe.ts CHANGED
@@ -107,7 +107,9 @@ export type SimulateTxOpts = {
107
107
  skipTxValidation?: boolean;
108
108
  /** If false, fees are enforced. */
109
109
  skipFeeEnforcement?: boolean;
110
- /** State overrides for the simulation, such as contract instances and artifacts. */
110
+ /** If true, kernel logic is emulated in TS for simulation */
111
+ skipKernels?: boolean;
112
+ /** State overrides for the simulation, such as contract instances and artifacts. Requires skipKernels: true */
111
113
  overrides?: SimulationOverrides;
112
114
  /** Addresses whose private state and keys are accessible during private execution */
113
115
  scopes: AccessScopes;
@@ -567,6 +569,9 @@ export class PXE {
567
569
 
568
570
  if (wasAdded) {
569
571
  this.log.info(`Added sender:\n ${sender.toString()}`);
572
+ // Wipe the entire sync cache: the new sender's tagged logs could contain notes/events for any contract, so
573
+ // all contracts must re-sync to discover them.
574
+ this.contractSyncService.wipe();
570
575
  } else {
571
576
  this.log.info(`Sender:\n "${sender.toString()}"\n already registered.`);
572
577
  }
@@ -896,7 +901,14 @@ export class PXE {
896
901
  */
897
902
  public simulateTx(
898
903
  txRequest: TxExecutionRequest,
899
- { simulatePublic, skipTxValidation = false, skipFeeEnforcement = false, overrides, scopes }: SimulateTxOpts,
904
+ {
905
+ simulatePublic,
906
+ skipTxValidation = false,
907
+ skipFeeEnforcement = false,
908
+ skipKernels = true,
909
+ overrides,
910
+ scopes,
911
+ }: SimulateTxOpts,
900
912
  ): Promise<TxSimulationResult> {
901
913
  // We disable concurrent simulations since those might execute oracles which read and write to the PXE stores (e.g.
902
914
  // to the capsules), and we need to prevent concurrent runs from interfering with one another (e.g. attempting to
@@ -920,17 +932,20 @@ export class PXE {
920
932
  await this.blockStateSynchronizer.sync();
921
933
  const syncTime = syncTimer.ms();
922
934
 
923
- const contractFunctionSimulator = this.#getSimulatorForTx(overrides);
924
- // Temporary: in case there are overrides, we have to skip the kernels or validations
925
- // will fail. Consider handing control to the user/wallet on whether they want to run them
926
- // or not.
927
935
  const overriddenContracts = overrides?.contracts ? new Set(Object.keys(overrides.contracts)) : undefined;
928
936
  const hasOverriddenContracts = overriddenContracts !== undefined && overriddenContracts.size > 0;
929
- const skipKernels = hasOverriddenContracts;
930
937
 
931
- // Set overridden contracts on the sync service so it knows to skip syncing them
938
+ if (hasOverriddenContracts && !skipKernels) {
939
+ throw new Error(
940
+ 'Simulating with overridden contracts is not compatible with kernel execution. Please set skipKernels to true when simulating with overridden contracts.',
941
+ );
942
+ }
943
+ const contractFunctionSimulator = this.#getSimulatorForTx(overrides);
944
+
932
945
  if (hasOverriddenContracts) {
933
- this.contractSyncService.setOverriddenContracts(jobId, overriddenContracts);
946
+ // Overridden contracts don't have a sync function, so calling sync on them would fail.
947
+ // We exclude them so the sync service skips them entirely.
948
+ this.contractSyncService.setExcludedFromSync(jobId, overriddenContracts);
934
949
  }
935
950
 
936
951
  // Execution of private functions only; no proving, and no kernel logic.