@aztec/txe 0.0.1-commit.d431d1c → 0.0.1-commit.dbf9cec

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 (55) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/index.js +82 -50
  4. package/dest/oracle/interfaces.d.ts +6 -5
  5. package/dest/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_public_context.d.ts +5 -5
  7. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_public_context.js +6 -6
  9. package/dest/oracle/txe_oracle_top_level_context.d.ts +7 -8
  10. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  11. package/dest/oracle/txe_oracle_top_level_context.js +105 -34
  12. package/dest/rpc_translator.d.ts +16 -10
  13. package/dest/rpc_translator.d.ts.map +1 -1
  14. package/dest/rpc_translator.js +74 -38
  15. package/dest/state_machine/archiver.d.ts +2 -2
  16. package/dest/state_machine/archiver.d.ts.map +1 -1
  17. package/dest/state_machine/archiver.js +7 -6
  18. package/dest/state_machine/dummy_p2p_client.d.ts +16 -12
  19. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  20. package/dest/state_machine/dummy_p2p_client.js +28 -16
  21. package/dest/state_machine/index.d.ts +7 -7
  22. package/dest/state_machine/index.d.ts.map +1 -1
  23. package/dest/state_machine/index.js +31 -17
  24. package/dest/state_machine/mock_epoch_cache.d.ts +6 -2
  25. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  26. package/dest/state_machine/mock_epoch_cache.js +6 -1
  27. package/dest/state_machine/synchronizer.d.ts +3 -3
  28. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  29. package/dest/txe_session.d.ts +9 -6
  30. package/dest/txe_session.d.ts.map +1 -1
  31. package/dest/txe_session.js +83 -23
  32. package/dest/util/txe_public_contract_data_source.d.ts +2 -3
  33. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  34. package/dest/util/txe_public_contract_data_source.js +5 -22
  35. package/dest/utils/block_creation.d.ts +5 -5
  36. package/dest/utils/block_creation.d.ts.map +1 -1
  37. package/dest/utils/block_creation.js +7 -5
  38. package/package.json +15 -15
  39. package/src/index.ts +83 -49
  40. package/src/oracle/interfaces.ts +8 -3
  41. package/src/oracle/txe_oracle_public_context.ts +8 -10
  42. package/src/oracle/txe_oracle_top_level_context.ts +134 -81
  43. package/src/rpc_translator.ts +78 -31
  44. package/src/state_machine/archiver.ts +6 -8
  45. package/src/state_machine/dummy_p2p_client.ts +40 -22
  46. package/src/state_machine/index.ts +49 -19
  47. package/src/state_machine/mock_epoch_cache.ts +7 -1
  48. package/src/state_machine/synchronizer.ts +2 -2
  49. package/src/txe_session.ts +98 -82
  50. package/src/util/txe_public_contract_data_source.ts +10 -36
  51. package/src/utils/block_creation.ts +8 -6
  52. package/dest/util/txe_contract_store.d.ts +0 -12
  53. package/dest/util/txe_contract_store.d.ts.map +0 -1
  54. package/dest/util/txe_contract_store.js +0 -22
  55. package/src/util/txe_contract_store.ts +0 -36
package/src/index.ts CHANGED
@@ -9,9 +9,12 @@ import { Fr } from '@aztec/aztec.js/fields';
9
9
  import { PublicKeys, deriveKeys } from '@aztec/aztec.js/keys';
10
10
  import { createSafeJsonRpcServer } from '@aztec/foundation/json-rpc/server';
11
11
  import type { Logger } from '@aztec/foundation/log';
12
- import { type ProtocolContract, protocolContractNames } from '@aztec/protocol-contracts';
12
+ import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
13
+ import { protocolContractNames } from '@aztec/protocol-contracts';
13
14
  import { BundledProtocolContractsProvider } from '@aztec/protocol-contracts/providers/bundle';
15
+ import { ContractStore } from '@aztec/pxe/server';
14
16
  import { computeArtifactHash } from '@aztec/stdlib/contract';
17
+ import type { ContractArtifactWithHash } from '@aztec/stdlib/contract';
15
18
  import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
16
19
  import { zodFor } from '@aztec/stdlib/schemas';
17
20
 
@@ -33,18 +36,24 @@ import {
33
36
  fromSingle,
34
37
  toSingle,
35
38
  } from './util/encoding.js';
36
- import type { ContractArtifactWithHash } from './util/txe_contract_store.js';
37
39
 
38
40
  const sessions = new Map<number, TXESession>();
39
41
 
40
42
  /*
41
43
  * TXE typically has to load the same contract artifacts over and over again for multiple tests,
42
- * so we cache them here to avoid both loading them from disk repeatedly and computing their artifact hashes
44
+ * so we cache them here to avoid loading from disk repeatedly.
45
+ *
46
+ * The in-flight map coalesces concurrent requests for the same cache key so that
47
+ * computeArtifactHash (very expensive) is only run once even under parallelism.
43
48
  */
44
49
  const TXEArtifactsCache = new Map<
45
50
  string,
46
51
  { artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }
47
52
  >();
53
+ const TXEArtifactsCacheInFlight = new Map<
54
+ string,
55
+ Promise<{ artifact: ContractArtifactWithHash; instance: ContractInstanceWithAddress }>
56
+ >();
48
57
 
49
58
  type TXEForeignCallInput = {
50
59
  session_id: number;
@@ -68,7 +77,7 @@ const TXEForeignCallInputSchema = zodFor<TXEForeignCallInput>()(
68
77
  );
69
78
 
70
79
  class TXEDispatcher {
71
- private protocolContracts!: ProtocolContract[];
80
+ private contractStore!: ContractStore;
72
81
 
73
82
  constructor(private logger: Logger) {}
74
83
 
@@ -135,29 +144,36 @@ class TXEDispatcher {
135
144
  this.logger.debug(`Using cached artifact for ${cacheKey}`);
136
145
  ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
137
146
  } else {
138
- this.logger.debug(`Loading compiled artifact ${artifactPath}`);
139
- const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
140
- const artifactWithoutHash = loadContractArtifact(artifactJSON);
141
- artifact = {
142
- ...artifactWithoutHash,
143
- // Artifact hash is *very* expensive to compute, so we do it here once
144
- // and the TXE contract data provider can cache it
145
- artifactHash: await computeArtifactHash(artifactWithoutHash),
146
- };
147
- this.logger.debug(
148
- `Deploy ${
149
- artifact.name
150
- } with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
151
- );
152
- instance = await getContractInstanceFromInstantiationParams(artifact, {
153
- constructorArgs: decodedArgs,
154
- skipArgsDecoding: true,
155
- salt: Fr.ONE,
156
- publicKeys,
157
- constructorArtifact: initializer ? initializer : undefined,
158
- deployer: AztecAddress.ZERO,
159
- });
160
- TXEArtifactsCache.set(cacheKey, { artifact, instance });
147
+ if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
148
+ this.logger.debug(`Loading compiled artifact ${artifactPath}`);
149
+ const compute = async () => {
150
+ const artifactJSON = JSON.parse(await readFile(artifactPath, 'utf-8')) as NoirCompiledContract;
151
+ const artifactWithoutHash = loadContractArtifact(artifactJSON);
152
+ const computedArtifact: ContractArtifactWithHash = {
153
+ ...artifactWithoutHash,
154
+ // Artifact hash is *very* expensive to compute, so we do it here once
155
+ // and the TXE contract data provider can cache it
156
+ artifactHash: await computeArtifactHash(artifactWithoutHash),
157
+ };
158
+ this.logger.debug(
159
+ `Deploy ${computedArtifact.name} with initializer ${initializer}(${decodedArgs}) and public keys hash ${publicKeysHash.toString()}`,
160
+ );
161
+ const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
162
+ constructorArgs: decodedArgs,
163
+ skipArgsDecoding: true,
164
+ salt: Fr.ONE,
165
+ publicKeys,
166
+ constructorArtifact: initializer ? initializer : undefined,
167
+ deployer: AztecAddress.ZERO,
168
+ });
169
+ const result = { artifact: computedArtifact, instance: computedInstance };
170
+ TXEArtifactsCache.set(cacheKey, result);
171
+ TXEArtifactsCacheInFlight.delete(cacheKey);
172
+ return result;
173
+ };
174
+ TXEArtifactsCacheInFlight.set(cacheKey, compute());
175
+ }
176
+ ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
161
177
  }
162
178
 
163
179
  inputs.splice(0, 1, artifact, instance, toSingle(secret));
@@ -175,23 +191,35 @@ class TXEDispatcher {
175
191
  this.logger.debug(`Using cached artifact for ${cacheKey}`);
176
192
  ({ artifact, instance } = TXEArtifactsCache.get(cacheKey)!);
177
193
  } else {
178
- const keys = await deriveKeys(secret);
179
- const args = [keys.publicKeys.masterIncomingViewingPublicKey.x, keys.publicKeys.masterIncomingViewingPublicKey.y];
180
- artifact = {
181
- ...SchnorrAccountContractArtifact,
182
- // Artifact hash is *very* expensive to compute, so we do it here once
183
- // and the TXE contract data provider can cache it
184
- artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
185
- };
186
- instance = await getContractInstanceFromInstantiationParams(artifact, {
187
- constructorArgs: args,
188
- skipArgsDecoding: true,
189
- salt: Fr.ONE,
190
- publicKeys: keys.publicKeys,
191
- constructorArtifact: 'constructor',
192
- deployer: AztecAddress.ZERO,
193
- });
194
- TXEArtifactsCache.set(cacheKey, { artifact, instance });
194
+ if (!TXEArtifactsCacheInFlight.has(cacheKey)) {
195
+ const compute = async () => {
196
+ const keys = await deriveKeys(secret);
197
+ const args = [
198
+ keys.publicKeys.masterIncomingViewingPublicKey.x,
199
+ keys.publicKeys.masterIncomingViewingPublicKey.y,
200
+ ];
201
+ const computedArtifact: ContractArtifactWithHash = {
202
+ ...SchnorrAccountContractArtifact,
203
+ // Artifact hash is *very* expensive to compute, so we do it here once
204
+ // and the TXE contract data provider can cache it
205
+ artifactHash: await computeArtifactHash(SchnorrAccountContractArtifact),
206
+ };
207
+ const computedInstance = await getContractInstanceFromInstantiationParams(computedArtifact, {
208
+ constructorArgs: args,
209
+ skipArgsDecoding: true,
210
+ salt: Fr.ONE,
211
+ publicKeys: keys.publicKeys,
212
+ constructorArtifact: 'constructor',
213
+ deployer: AztecAddress.ZERO,
214
+ });
215
+ const result = { artifact: computedArtifact, instance: computedInstance };
216
+ TXEArtifactsCache.set(cacheKey, result);
217
+ TXEArtifactsCacheInFlight.delete(cacheKey);
218
+ return result;
219
+ };
220
+ TXEArtifactsCacheInFlight.set(cacheKey, compute());
221
+ }
222
+ ({ artifact, instance } = await TXEArtifactsCacheInFlight.get(cacheKey)!);
195
223
  }
196
224
 
197
225
  inputs.splice(0, 0, artifact, instance);
@@ -204,12 +232,18 @@ class TXEDispatcher {
204
232
 
205
233
  if (!sessions.has(sessionId)) {
206
234
  this.logger.debug(`Creating new session ${sessionId}`);
207
- if (!this.protocolContracts) {
208
- this.protocolContracts = await Promise.all(
209
- protocolContractNames.map(name => new BundledProtocolContractsProvider().getProtocolContractArtifact(name)),
210
- );
235
+ if (!this.contractStore) {
236
+ const kvStore = await openTmpStore('txe-contracts');
237
+ this.contractStore = new ContractStore(kvStore);
238
+ const provider = new BundledProtocolContractsProvider();
239
+ for (const name of protocolContractNames) {
240
+ const { instance, artifact } = await provider.getProtocolContractArtifact(name);
241
+ await this.contractStore.addContractArtifact(artifact);
242
+ await this.contractStore.addContractInstance(instance);
243
+ }
244
+ this.logger.debug('Registered protocol contracts in shared contract store');
211
245
  }
212
- sessions.set(sessionId, await TXESession.init(this.protocolContracts));
246
+ sessions.set(sessionId, await TXESession.init(this.contractStore));
213
247
  }
214
248
 
215
249
  switch (functionName) {
@@ -33,9 +33,9 @@ export interface IAvmExecutionOracle {
33
33
  avmOpcodeVersion(): Promise<Fr>;
34
34
  avmOpcodeEmitNullifier(nullifier: Fr): Promise<void>;
35
35
  avmOpcodeEmitNoteHash(noteHash: Fr): Promise<void>;
36
- avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean>;
36
+ avmOpcodeNullifierExists(siloedNullifier: Fr): Promise<boolean>;
37
37
  avmOpcodeStorageWrite(slot: Fr, value: Fr): Promise<void>;
38
- avmOpcodeStorageRead(slot: Fr): Promise<Fr>;
38
+ avmOpcodeStorageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr>;
39
39
  }
40
40
 
41
41
  /**
@@ -71,11 +71,13 @@ export interface ITxeExecutionOracle {
71
71
  args: Fr[],
72
72
  argsHash: Fr,
73
73
  isStaticCall: boolean,
74
+ jobId: string,
74
75
  ): Promise<Fr[]>;
75
- txeSimulateUtilityFunction(
76
+ txeExecuteUtilityFunction(
76
77
  targetContractAddress: AztecAddress,
77
78
  functionSelector: FunctionSelector,
78
79
  args: Fr[],
80
+ jobId: string,
79
81
  ): Promise<Fr[]>;
80
82
  txePublicCallNewFlow(
81
83
  from: AztecAddress,
@@ -83,4 +85,7 @@ export interface ITxeExecutionOracle {
83
85
  calldata: Fr[],
84
86
  isStaticCall: boolean,
85
87
  ): Promise<Fr[]>;
88
+ // TODO(F-335): Drop this from here as it's not a real oracle handler - it's only called from
89
+ // RPCTranslator::txeGetPrivateEvents and never from Noir.
90
+ syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string): Promise<void>;
86
91
  }
@@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { PublicDataWrite } from '@aztec/stdlib/avm';
5
5
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
6
- import type { L2BlockNew } from '@aztec/stdlib/block';
6
+ import type { L2Block } from '@aztec/stdlib/block';
7
7
  import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash';
8
8
  import {
9
9
  MerkleTreeId,
@@ -78,13 +78,11 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
78
78
  this.transientUniqueNoteHashes.push(siloedNoteHash);
79
79
  }
80
80
 
81
- async avmOpcodeNullifierExists(innerNullifier: Fr, targetAddress: AztecAddress): Promise<boolean> {
82
- const nullifier = await siloNullifier(targetAddress, innerNullifier!);
83
-
81
+ async avmOpcodeNullifierExists(siloedNullifier: Fr): Promise<boolean> {
84
82
  const treeIndex = (
85
- await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()])
83
+ await this.forkedWorldTrees.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [siloedNullifier.toBuffer()])
86
84
  )[0];
87
- const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(nullifier));
85
+ const transientIndex = this.transientSiloedNullifiers.find(n => n.equals(siloedNullifier));
88
86
 
89
87
  return treeIndex !== undefined || transientIndex !== undefined;
90
88
  }
@@ -101,8 +99,8 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
101
99
  ]);
102
100
  }
103
101
 
104
- async avmOpcodeStorageRead(slot: Fr): Promise<Fr> {
105
- const leafSlot = await computePublicDataTreeLeafSlot(this.contractAddress, slot);
102
+ async avmOpcodeStorageRead(slot: Fr, contractAddress: AztecAddress): Promise<Fr> {
103
+ const leafSlot = await computePublicDataTreeLeafSlot(contractAddress, slot);
106
104
 
107
105
  const lowLeafResult = await this.forkedWorldTrees.getPreviousValueIndex(
108
106
  MerkleTreeId.PUBLIC_DATA_TREE,
@@ -119,12 +117,12 @@ export class TXEOraclePublicContext implements IAvmExecutionOracle {
119
117
  )) as PublicDataTreeLeafPreimage
120
118
  ).leaf.value;
121
119
 
122
- this.logger.debug('AVM storage read', { slot, value });
120
+ this.logger.debug('AVM storage read', { slot, contractAddress, value });
123
121
 
124
122
  return value;
125
123
  }
126
124
 
127
- async close(): Promise<L2BlockNew> {
125
+ async close(): Promise<L2Block> {
128
126
  this.logger.debug('Exiting Public Context, building block with collected side effects', {
129
127
  blockNumber: this.globalVariables.blockNumber,
130
128
  });
@@ -12,9 +12,11 @@ import { Fr } from '@aztec/foundation/curves/bn254';
12
12
  import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
13
13
  import { TestDateProvider } from '@aztec/foundation/timer';
14
14
  import type { KeyStore } from '@aztec/key-store';
15
+ import type { AccessScopes } from '@aztec/pxe/client/lazy';
15
16
  import {
16
17
  AddressStore,
17
18
  CapsuleStore,
19
+ type ContractStore,
18
20
  NoteStore,
19
21
  ORACLE_VERSION,
20
22
  PrivateEventStore,
@@ -83,7 +85,6 @@ import { ForkCheckpoint } from '@aztec/world-state';
83
85
  import { DEFAULT_ADDRESS } from '../constants.js';
84
86
  import type { TXEStateMachine } from '../state_machine/index.js';
85
87
  import type { TXEAccountStore } from '../util/txe_account_store.js';
86
- import type { TXEContractStore } from '../util/txe_contract_store.js';
87
88
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
88
89
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
89
90
  import type { ITxeExecutionOracle } from './interfaces.js';
@@ -96,7 +97,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
96
97
 
97
98
  constructor(
98
99
  private stateMachine: TXEStateMachine,
99
- private contractStore: TXEContractStore,
100
+ private contractStore: ContractStore,
100
101
  private noteStore: NoteStore,
101
102
  private keyStore: KeyStore,
102
103
  private addressStore: AddressStore,
@@ -106,7 +107,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
106
107
  private senderAddressBookStore: SenderAddressBookStore,
107
108
  private capsuleStore: CapsuleStore,
108
109
  private privateEventStore: PrivateEventStore,
109
- private jobId: string,
110
110
  private nextBlockTimestamp: bigint,
111
111
  private version: Fr,
112
112
  private chainId: Fr,
@@ -131,13 +131,14 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
131
131
  }
132
132
 
133
133
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
134
- utilityDebugLog(level: number, message: string, fields: Fr[]): void {
134
+ utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
135
135
  if (!LogLevels[level]) {
136
- throw new Error(`Invalid debug log level: ${level}`);
136
+ throw new Error(`Invalid log level: ${level}`);
137
137
  }
138
138
  const levelName = LogLevels[level];
139
139
 
140
140
  this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
141
+ return Promise.resolve();
141
142
  }
142
143
 
143
144
  txeGetDefaultAddress(): AztecAddress {
@@ -170,6 +171,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
170
171
  return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
171
172
  }
172
173
 
174
+ async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
175
+ if (contractAddress.equals(DEFAULT_ADDRESS)) {
176
+ this.logger.debug(`Skipping sync in txeGetPrivateEvents because the events correspond to the default address.`);
177
+ return;
178
+ }
179
+
180
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
181
+ await this.stateMachine.contractSyncService.ensureContractSynced(
182
+ contractAddress,
183
+ null,
184
+ async (call, execScopes) => {
185
+ await this.executeUtilityCall(call, execScopes, jobId);
186
+ },
187
+ blockHeader,
188
+ jobId,
189
+ [scope],
190
+ );
191
+ }
192
+
173
193
  async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
174
194
  return (
175
195
  await this.privateEventStore.getPrivateEvents(selector, {
@@ -209,7 +229,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
209
229
  await this.txeAddAccount(artifact, instance, secret);
210
230
  } else {
211
231
  await this.contractStore.addContractInstance(instance);
212
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
232
+ await this.contractStore.addContractArtifact(artifact);
213
233
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
214
234
  }
215
235
  }
@@ -219,7 +239,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
219
239
 
220
240
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
221
241
  await this.contractStore.addContractInstance(instance);
222
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
242
+ await this.contractStore.addContractArtifact(artifact);
223
243
 
224
244
  const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
225
245
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -283,6 +303,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
283
303
  args: Fr[],
284
304
  argsHash: Fr = Fr.zero(),
285
305
  isStaticCall: boolean = false,
306
+ jobId: string,
286
307
  ) {
287
308
  this.logger.verbose(
288
309
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -296,12 +317,24 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
296
317
  throw new Error(message);
297
318
  }
298
319
 
320
+ // When `from` is the zero address (e.g. when deploying a new account contract), we return an
321
+ // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
322
+ const effectiveScopes = from.isZero() ? [] : [from];
323
+
299
324
  // Sync notes before executing private function to discover notes from previous transactions
300
- const utilityExecutor = async (call: FunctionCall) => {
301
- await this.executeUtilityCall(call);
325
+ const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
326
+ await this.executeUtilityCall(call, execScopes, jobId);
302
327
  };
303
328
 
304
- await this.contractStore.syncPrivateState(targetContractAddress, functionSelector, utilityExecutor);
329
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
330
+ await this.stateMachine.contractSyncService.ensureContractSynced(
331
+ targetContractAddress,
332
+ functionSelector,
333
+ utilityExecutor,
334
+ blockHeader,
335
+ jobId,
336
+ effectiveScopes,
337
+ );
305
338
 
306
339
  const blockNumber = await this.txeGetNextBlockNumber();
307
340
 
@@ -313,8 +346,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
313
346
 
314
347
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
315
348
 
316
- const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
317
-
318
349
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
319
350
  const noteCache = new ExecutionNoteCache(protocolNullifier);
320
351
  // In production, the account contract sets the min revertible counter before calling the app function.
@@ -326,43 +357,37 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
326
357
 
327
358
  const simulator = new WASMSimulator();
328
359
 
329
- const privateExecutionOracle = new PrivateExecutionOracle(
360
+ const privateExecutionOracle = new PrivateExecutionOracle({
330
361
  argsHash,
331
362
  txContext,
332
363
  callContext,
333
- /** Header of a block whose state is used during private execution (not the block the transaction is included in). */
334
- blockHeader,
364
+ anchorBlockHeader: blockHeader,
335
365
  utilityExecutor,
336
- /** List of transient auth witnesses to be used during this simulation */
337
- Array.from(this.authwits.values()),
338
- /** List of transient auth witnesses to be used during this simulation */
339
- [],
340
- HashedValuesCache.create([new HashedValues(args, argsHash)]),
366
+ authWitnesses: Array.from(this.authwits.values()),
367
+ capsules: [],
368
+ executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
341
369
  noteCache,
342
370
  taggingIndexCache,
343
- this.contractStore,
344
- this.noteStore,
345
- this.keyStore,
346
- this.addressStore,
347
- this.stateMachine.node,
348
- this.stateMachine.anchorBlockStore,
349
- this.senderTaggingStore,
350
- this.recipientTaggingStore,
351
- this.senderAddressBookStore,
352
- this.capsuleStore,
353
- this.privateEventStore,
354
- this.jobId,
355
- 0, // totalPublicArgsCount
356
- minRevertibleSideEffectCounter, // (start) sideEffectCounter
357
- undefined, // log
358
- undefined, // scopes
359
- /**
360
- * In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
361
- * contract would perform, including setting senderForTags.
362
- */
363
- from,
371
+ contractStore: this.contractStore,
372
+ noteStore: this.noteStore,
373
+ keyStore: this.keyStore,
374
+ addressStore: this.addressStore,
375
+ aztecNode: this.stateMachine.node,
376
+ senderTaggingStore: this.senderTaggingStore,
377
+ recipientTaggingStore: this.recipientTaggingStore,
378
+ senderAddressBookStore: this.senderAddressBookStore,
379
+ capsuleStore: this.capsuleStore,
380
+ privateEventStore: this.privateEventStore,
381
+ contractSyncService: this.stateMachine.contractSyncService,
382
+ jobId,
383
+ totalPublicCalldataCount: 0,
384
+ sideEffectCounter: minRevertibleSideEffectCounter,
385
+ scopes: effectiveScopes,
386
+ // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
387
+ // contract would perform, including setting senderForTags.
388
+ senderForTags: from,
364
389
  simulator,
365
- );
390
+ });
366
391
 
367
392
  // Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
368
393
  let result: PrivateExecutionResult;
@@ -401,7 +426,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
401
426
  // We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
402
427
  const { publicInputs } = await generateSimulatedProvingResult(
403
428
  result,
404
- this.contractStore,
429
+ (addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
430
+ this.stateMachine.node,
405
431
  minRevertibleSideEffectCounter,
406
432
  );
407
433
 
@@ -414,7 +440,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
414
440
 
415
441
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
416
442
 
417
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
443
+ const bindings = this.logger.getBindings();
444
+ const contractsDB = new PublicContractsDB(
445
+ new TXEPublicContractDataSource(blockNumber, this.contractStore),
446
+ bindings,
447
+ );
418
448
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
419
449
  const config = PublicSimulatorConfig.from({
420
450
  skipFeeEnforcement: true,
@@ -427,8 +457,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
427
457
  globals,
428
458
  guardedMerkleTrees,
429
459
  contractsDB,
430
- new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config),
460
+ new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings),
431
461
  new TestDateProvider(),
462
+ undefined,
463
+ createLogger('simulator:public-processor', bindings),
432
464
  );
433
465
 
434
466
  const tx = await Tx.create({
@@ -525,7 +557,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
525
557
 
526
558
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
527
559
 
528
- const contractsDB = new PublicContractsDB(new TXEPublicContractDataSource(blockNumber, this.contractStore));
560
+ const bindings2 = this.logger.getBindings();
561
+ const contractsDB = new PublicContractsDB(
562
+ new TXEPublicContractDataSource(blockNumber, this.contractStore),
563
+ bindings2,
564
+ );
529
565
  const guardedMerkleTrees = new GuardedMerkleTreeOperations(forkedWorldTrees);
530
566
  const config = PublicSimulatorConfig.from({
531
567
  skipFeeEnforcement: true,
@@ -534,8 +570,16 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
534
570
  collectStatistics: false,
535
571
  collectCallMetadata: true,
536
572
  });
537
- const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config);
538
- const processor = new PublicProcessor(globals, guardedMerkleTrees, contractsDB, simulator, new TestDateProvider());
573
+ const simulator = new CppPublicTxSimulator(guardedMerkleTrees, contractsDB, globals, config, bindings2);
574
+ const processor = new PublicProcessor(
575
+ globals,
576
+ guardedMerkleTrees,
577
+ contractsDB,
578
+ simulator,
579
+ new TestDateProvider(),
580
+ undefined,
581
+ createLogger('simulator:public-processor', bindings2),
582
+ );
539
583
 
540
584
  // We're simulating a scenario in which private execution immediately enqueues a public call and halts. The private
541
585
  // kernel init would in this case inject a nullifier with the transaction request hash as a non-revertible
@@ -566,7 +610,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
566
610
  constantData,
567
611
  /*gasUsed=*/ new Gas(0, 0),
568
612
  /*feePayer=*/ AztecAddress.zero(),
569
- /*includeByTimestamp=*/ 0n,
613
+ /*expirationTimestamp=*/ 0n,
570
614
  inputsForPublic,
571
615
  undefined,
572
616
  );
@@ -634,10 +678,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
634
678
  return returnValues ?? [];
635
679
  }
636
680
 
637
- async txeSimulateUtilityFunction(
681
+ async txeExecuteUtilityFunction(
638
682
  targetContractAddress: AztecAddress,
639
683
  functionSelector: FunctionSelector,
640
684
  args: Fr[],
685
+ jobId: string,
641
686
  ) {
642
687
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
643
688
  if (!artifact) {
@@ -645,25 +690,33 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
645
690
  }
646
691
 
647
692
  // Sync notes before executing utility function to discover notes from previous transactions
648
- await this.contractStore.syncPrivateState(targetContractAddress, functionSelector, async call => {
649
- await this.executeUtilityCall(call);
650
- });
651
-
652
- const call = new FunctionCall(
653
- artifact.name,
693
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
694
+ await this.stateMachine.contractSyncService.ensureContractSynced(
654
695
  targetContractAddress,
655
696
  functionSelector,
656
- FunctionType.UTILITY,
657
- false,
658
- false,
659
- args,
660
- [],
697
+ async (call, execScopes) => {
698
+ await this.executeUtilityCall(call, execScopes, jobId);
699
+ },
700
+ blockHeader,
701
+ jobId,
702
+ 'ALL_SCOPES',
661
703
  );
662
704
 
663
- return this.executeUtilityCall(call);
705
+ const call = FunctionCall.from({
706
+ name: artifact.name,
707
+ to: targetContractAddress,
708
+ selector: functionSelector,
709
+ type: FunctionType.UTILITY,
710
+ hideMsgSender: false,
711
+ isStatic: false,
712
+ args,
713
+ returnTypes: [],
714
+ });
715
+
716
+ return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
664
717
  }
665
718
 
666
- private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
719
+ private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
667
720
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
668
721
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
669
722
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -676,23 +729,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
676
729
 
677
730
  try {
678
731
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
679
- const oracle = new UtilityExecutionOracle(
680
- call.to,
681
- [],
682
- [],
732
+ const oracle = new UtilityExecutionOracle({
733
+ contractAddress: call.to,
734
+ authWitnesses: [],
735
+ capsules: [],
683
736
  anchorBlockHeader,
684
- this.contractStore,
685
- this.noteStore,
686
- this.keyStore,
687
- this.addressStore,
688
- this.stateMachine.node,
689
- this.stateMachine.anchorBlockStore,
690
- this.recipientTaggingStore,
691
- this.senderAddressBookStore,
692
- this.capsuleStore,
693
- this.privateEventStore,
694
- this.jobId,
695
- );
737
+ contractStore: this.contractStore,
738
+ noteStore: this.noteStore,
739
+ keyStore: this.keyStore,
740
+ addressStore: this.addressStore,
741
+ aztecNode: this.stateMachine.node,
742
+ recipientTaggingStore: this.recipientTaggingStore,
743
+ senderAddressBookStore: this.senderAddressBookStore,
744
+ capsuleStore: this.capsuleStore,
745
+ privateEventStore: this.privateEventStore,
746
+ jobId,
747
+ scopes,
748
+ });
696
749
  const acirExecutionResult = await new WASMSimulator()
697
750
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
698
751
  .catch((err: Error) => {
@@ -708,10 +761,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
708
761
  );
709
762
  });
710
763
 
711
- this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
764
+ this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
712
765
  return witnessMapToFields(acirExecutionResult.returnWitness);
713
766
  } catch (err) {
714
- throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility simulation'));
767
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error during utility execution'));
715
768
  }
716
769
  }
717
770