@aztec/txe 0.0.1-commit.8f9871590 → 0.0.1-commit.9117c5f5a

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 (36) 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 +4 -3
  5. package/dest/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_top_level_context.d.ts +6 -7
  7. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_top_level_context.js +27 -17
  9. package/dest/rpc_translator.d.ts +2 -2
  10. package/dest/rpc_translator.d.ts.map +1 -1
  11. package/dest/rpc_translator.js +16 -4
  12. package/dest/state_machine/dummy_p2p_client.d.ts +6 -5
  13. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  14. package/dest/state_machine/dummy_p2p_client.js +7 -4
  15. package/dest/state_machine/index.d.ts +1 -1
  16. package/dest/state_machine/index.d.ts.map +1 -1
  17. package/dest/state_machine/index.js +1 -1
  18. package/dest/txe_session.d.ts +9 -6
  19. package/dest/txe_session.d.ts.map +1 -1
  20. package/dest/txe_session.js +12 -12
  21. package/dest/util/txe_public_contract_data_source.d.ts +2 -3
  22. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  23. package/dest/util/txe_public_contract_data_source.js +5 -22
  24. package/package.json +15 -15
  25. package/src/index.ts +83 -49
  26. package/src/oracle/interfaces.ts +6 -1
  27. package/src/oracle/txe_oracle_top_level_context.ts +36 -16
  28. package/src/rpc_translator.ts +18 -3
  29. package/src/state_machine/dummy_p2p_client.ts +11 -7
  30. package/src/state_machine/index.ts +1 -0
  31. package/src/txe_session.ts +19 -15
  32. package/src/util/txe_public_contract_data_source.ts +10 -36
  33. package/dest/util/txe_contract_store.d.ts +0 -12
  34. package/dest/util/txe_contract_store.d.ts.map +0 -1
  35. package/dest/util/txe_contract_store.js +0 -22
  36. 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) {
@@ -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
  }
@@ -16,6 +16,7 @@ import type { AccessScopes } from '@aztec/pxe/client/lazy';
16
16
  import {
17
17
  AddressStore,
18
18
  CapsuleStore,
19
+ type ContractStore,
19
20
  NoteStore,
20
21
  ORACLE_VERSION,
21
22
  PrivateEventStore,
@@ -84,7 +85,6 @@ import { ForkCheckpoint } from '@aztec/world-state';
84
85
  import { DEFAULT_ADDRESS } from '../constants.js';
85
86
  import type { TXEStateMachine } from '../state_machine/index.js';
86
87
  import type { TXEAccountStore } from '../util/txe_account_store.js';
87
- import type { TXEContractStore } from '../util/txe_contract_store.js';
88
88
  import { TXEPublicContractDataSource } from '../util/txe_public_contract_data_source.js';
89
89
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from '../utils/block_creation.js';
90
90
  import type { ITxeExecutionOracle } from './interfaces.js';
@@ -97,7 +97,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
97
97
 
98
98
  constructor(
99
99
  private stateMachine: TXEStateMachine,
100
- private contractStore: TXEContractStore,
100
+ private contractStore: ContractStore,
101
101
  private noteStore: NoteStore,
102
102
  private keyStore: KeyStore,
103
103
  private addressStore: AddressStore,
@@ -107,7 +107,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
107
107
  private senderAddressBookStore: SenderAddressBookStore,
108
108
  private capsuleStore: CapsuleStore,
109
109
  private privateEventStore: PrivateEventStore,
110
- private jobId: string,
111
110
  private nextBlockTimestamp: bigint,
112
111
  private version: Fr,
113
112
  private chainId: Fr,
@@ -172,6 +171,25 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
172
171
  return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
173
172
  }
174
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
+
175
193
  async txeGetPrivateEvents(selector: EventSelector, contractAddress: AztecAddress, scope: AztecAddress) {
176
194
  return (
177
195
  await this.privateEventStore.getPrivateEvents(selector, {
@@ -211,7 +229,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
211
229
  await this.txeAddAccount(artifact, instance, secret);
212
230
  } else {
213
231
  await this.contractStore.addContractInstance(instance);
214
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
232
+ await this.contractStore.addContractArtifact(artifact);
215
233
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
216
234
  }
217
235
  }
@@ -221,7 +239,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
221
239
 
222
240
  this.logger.debug(`Deployed ${artifact.name} at ${instance.address}`);
223
241
  await this.contractStore.addContractInstance(instance);
224
- await this.contractStore.addContractArtifact(instance.currentContractClassId, artifact);
242
+ await this.contractStore.addContractArtifact(artifact);
225
243
 
226
244
  const completeAddress = await this.keyStore.addAccount(secret, partialAddress);
227
245
  await this.accountStore.setAccount(completeAddress.address, completeAddress);
@@ -285,6 +303,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
285
303
  args: Fr[],
286
304
  argsHash: Fr = Fr.zero(),
287
305
  isStaticCall: boolean = false,
306
+ jobId: string,
288
307
  ) {
289
308
  this.logger.verbose(
290
309
  `Executing external function ${await this.contractStore.getDebugFunctionName(targetContractAddress, functionSelector)}@${targetContractAddress} isStaticCall=${isStaticCall}`,
@@ -304,7 +323,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
304
323
 
305
324
  // Sync notes before executing private function to discover notes from previous transactions
306
325
  const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
307
- await this.executeUtilityCall(call, execScopes);
326
+ await this.executeUtilityCall(call, execScopes, jobId);
308
327
  };
309
328
 
310
329
  const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
@@ -313,7 +332,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
313
332
  functionSelector,
314
333
  utilityExecutor,
315
334
  blockHeader,
316
- this.jobId,
335
+ jobId,
317
336
  effectiveScopes,
318
337
  );
319
338
 
@@ -360,7 +379,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
360
379
  capsuleStore: this.capsuleStore,
361
380
  privateEventStore: this.privateEventStore,
362
381
  contractSyncService: this.stateMachine.contractSyncService,
363
- jobId: this.jobId,
382
+ jobId,
364
383
  totalPublicCalldataCount: 0,
365
384
  sideEffectCounter: minRevertibleSideEffectCounter,
366
385
  scopes: effectiveScopes,
@@ -659,10 +678,11 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
659
678
  return returnValues ?? [];
660
679
  }
661
680
 
662
- async txeSimulateUtilityFunction(
681
+ async txeExecuteUtilityFunction(
663
682
  targetContractAddress: AztecAddress,
664
683
  functionSelector: FunctionSelector,
665
684
  args: Fr[],
685
+ jobId: string,
666
686
  ) {
667
687
  const artifact = await this.contractStore.getFunctionArtifact(targetContractAddress, functionSelector);
668
688
  if (!artifact) {
@@ -675,10 +695,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
675
695
  targetContractAddress,
676
696
  functionSelector,
677
697
  async (call, execScopes) => {
678
- await this.executeUtilityCall(call, execScopes);
698
+ await this.executeUtilityCall(call, execScopes, jobId);
679
699
  },
680
700
  blockHeader,
681
- this.jobId,
701
+ jobId,
682
702
  'ALL_SCOPES',
683
703
  );
684
704
 
@@ -693,10 +713,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
693
713
  returnTypes: [],
694
714
  });
695
715
 
696
- return this.executeUtilityCall(call, 'ALL_SCOPES');
716
+ return this.executeUtilityCall(call, 'ALL_SCOPES', jobId);
697
717
  }
698
718
 
699
- private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes): Promise<Fr[]> {
719
+ private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes, jobId: string): Promise<Fr[]> {
700
720
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
701
721
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
702
722
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -723,7 +743,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
723
743
  senderAddressBookStore: this.senderAddressBookStore,
724
744
  capsuleStore: this.capsuleStore,
725
745
  privateEventStore: this.privateEventStore,
726
- jobId: this.jobId,
746
+ jobId,
727
747
  scopes,
728
748
  });
729
749
  const acirExecutionResult = await new WASMSimulator()
@@ -741,10 +761,10 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
741
761
  );
742
762
  });
743
763
 
744
- this.logger.verbose(`Utility simulation for ${call.to}.${call.selector} completed`);
764
+ this.logger.verbose(`Utility execution for ${call.to}.${call.selector} completed`);
745
765
  return witnessMapToFields(acirExecutionResult.returnWitness);
746
766
  } catch (err) {
747
- 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'));
748
768
  }
749
769
  }
750
770
 
@@ -30,7 +30,7 @@ import {
30
30
  toSingle,
31
31
  } from './util/encoding.js';
32
32
 
33
- const MAX_EVENT_LEN = 12; // This is MAX_MESSAGE_CONTENT_LEN - PRIVATE_EVENT_RESERVED_FIELDS
33
+ const MAX_EVENT_LEN = 10; // This is MAX_MESSAGE_CONTENT_LEN - PRIVATE_EVENT_MSG_PLAINTEXT_RESERVED_FIELDS_LEN
34
34
  const MAX_PRIVATE_EVENTS_PER_TXE_QUERY = 5;
35
35
 
36
36
  export class UnavailableOracleError extends Error {
@@ -285,6 +285,13 @@ export class RPCTranslator {
285
285
  const contractAddress = addressFromSingle(foreignContractAddress);
286
286
  const scope = addressFromSingle(foreignScope);
287
287
 
288
+ // TODO(F-335): Avoid doing the following 2 calls here.
289
+ {
290
+ await this.handlerAsTxe().syncContractNonOracleMethod(contractAddress, scope, this.stateHandler.getCurrentJob());
291
+ // We cycle job to commit the stores after the contract sync.
292
+ await this.stateHandler.cycleJob();
293
+ }
294
+
288
295
  const events = await this.handlerAsTxe().txeGetPrivateEvents(selector, contractAddress, scope);
289
296
 
290
297
  if (events.length > MAX_PRIVATE_EVENTS_PER_TXE_QUERY) {
@@ -1038,12 +1045,15 @@ export class RPCTranslator {
1038
1045
  args,
1039
1046
  argsHash,
1040
1047
  isStaticCall,
1048
+ this.stateHandler.getCurrentJob(),
1041
1049
  );
1042
1050
 
1051
+ // TODO(F-335): Avoid doing the following call here.
1052
+ await this.stateHandler.cycleJob();
1043
1053
  return toForeignCallResult([toArray(returnValues)]);
1044
1054
  }
1045
1055
 
1046
- async txeSimulateUtilityFunction(
1056
+ async txeExecuteUtilityFunction(
1047
1057
  foreignTargetContractAddress: ForeignCallSingle,
1048
1058
  foreignFunctionSelector: ForeignCallSingle,
1049
1059
  foreignArgs: ForeignCallArray,
@@ -1052,12 +1062,15 @@ export class RPCTranslator {
1052
1062
  const functionSelector = FunctionSelector.fromField(fromSingle(foreignFunctionSelector));
1053
1063
  const args = fromArray(foreignArgs);
1054
1064
 
1055
- const returnValues = await this.handlerAsTxe().txeSimulateUtilityFunction(
1065
+ const returnValues = await this.handlerAsTxe().txeExecuteUtilityFunction(
1056
1066
  targetContractAddress,
1057
1067
  functionSelector,
1058
1068
  args,
1069
+ this.stateHandler.getCurrentJob(),
1059
1070
  );
1060
1071
 
1072
+ // TODO(F-335): Avoid doing the following call here.
1073
+ await this.stateHandler.cycleJob();
1061
1074
  return toForeignCallResult([toArray(returnValues)]);
1062
1075
  }
1063
1076
 
@@ -1074,6 +1087,8 @@ export class RPCTranslator {
1074
1087
 
1075
1088
  const returnValues = await this.handlerAsTxe().txePublicCallNewFlow(from, address, calldata, isStaticCall);
1076
1089
 
1090
+ // TODO(F-335): Avoid doing the following call here.
1091
+ await this.stateHandler.cycleJob();
1077
1092
  return toForeignCallResult([toArray(returnValues)]);
1078
1093
  }
1079
1094
 
@@ -16,12 +16,12 @@ import type {
16
16
  StatusMessage,
17
17
  } from '@aztec/p2p';
18
18
  import type { EthAddress, L2BlockStreamEvent, L2Tips } from '@aztec/stdlib/block';
19
- import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
20
- import type { BlockProposal, CheckpointAttestation, CheckpointProposal } from '@aztec/stdlib/p2p';
19
+ import type { ITxProvider, PeerInfo } from '@aztec/stdlib/interfaces/server';
20
+ import type { BlockProposal, CheckpointAttestation, CheckpointProposal, TopicType } from '@aztec/stdlib/p2p';
21
21
  import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
22
22
 
23
23
  export class DummyP2P implements P2P {
24
- public validate(_txs: Tx[]): Promise<void> {
24
+ public validateTxsReceivedInBlockProposal(_txs: Tx[]): Promise<void> {
25
25
  return Promise.resolve();
26
26
  }
27
27
 
@@ -41,6 +41,10 @@ export class DummyP2P implements P2P {
41
41
  throw new Error('DummyP2P does not implement "getPeers"');
42
42
  }
43
43
 
44
+ public getGossipMeshPeerCount(_topicType: TopicType): Promise<number> {
45
+ return Promise.resolve(0);
46
+ }
47
+
44
48
  public broadcastProposal(_proposal: BlockProposal): Promise<void> {
45
49
  throw new Error('DummyP2P does not implement "broadcastProposal"');
46
50
  }
@@ -131,6 +135,10 @@ export class DummyP2P implements P2P {
131
135
  throw new Error('DummyP2P does not implement "isP2PClient"');
132
136
  }
133
137
 
138
+ public getTxProvider(): ITxProvider {
139
+ throw new Error('DummyP2P does not implement "getTxProvider"');
140
+ }
141
+
134
142
  public getTxsByHash(_txHashes: TxHash[]): Promise<Tx[]> {
135
143
  throw new Error('DummyP2P does not implement "getTxsByHash"');
136
144
  }
@@ -171,10 +179,6 @@ export class DummyP2P implements P2P {
171
179
  throw new Error('DummyP2P does not implement "hasTxsInPool"');
172
180
  }
173
181
 
174
- public addTxsToPool(_txs: Tx[]): Promise<number> {
175
- throw new Error('DummyP2P does not implement "addTxs"');
176
- }
177
-
178
182
  public getSyncedLatestBlockNum(): Promise<number> {
179
183
  throw new Error('DummyP2P does not implement "getSyncedLatestBlockNum"');
180
184
  }
@@ -50,6 +50,7 @@ export class TXEStateMachine {
50
50
  undefined,
51
51
  undefined,
52
52
  undefined,
53
+ undefined,
53
54
  VERSION,
54
55
  CHAIN_ID,
55
56
  new TXEGlobalVariablesBuilder(),
@@ -3,12 +3,12 @@ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { KeyStore } from '@aztec/key-store';
5
5
  import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
6
- import type { ProtocolContract } from '@aztec/protocol-contracts';
7
6
  import type { AccessScopes } from '@aztec/pxe/client/lazy';
8
7
  import {
9
8
  AddressStore,
10
9
  AnchorBlockStore,
11
10
  CapsuleStore,
11
+ ContractStore,
12
12
  JobCoordinator,
13
13
  NoteService,
14
14
  NoteStore,
@@ -55,7 +55,6 @@ import { TXEArchiver } from './state_machine/archiver.js';
55
55
  import { TXEStateMachine } from './state_machine/index.js';
56
56
  import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
57
57
  import { TXEAccountStore } from './util/txe_account_store.js';
58
- import { TXEContractStore } from './util/txe_contract_store.js';
59
58
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
60
59
  import { makeTxEffect } from './utils/tx_effect_creation.js';
61
60
 
@@ -114,6 +113,10 @@ export interface TXESessionStateHandler {
114
113
  enterPublicState(contractAddress?: AztecAddress): Promise<void>;
115
114
  enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
116
115
  enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
116
+
117
+ // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
118
+ cycleJob(): Promise<string>;
119
+ getCurrentJob(): string;
117
120
  }
118
121
 
119
122
  /**
@@ -132,7 +135,7 @@ export class TXESession implements TXESessionStateHandler {
132
135
  | IPrivateExecutionOracle
133
136
  | IAvmExecutionOracle
134
137
  | ITxeExecutionOracle,
135
- private contractStore: TXEContractStore,
138
+ private contractStore: ContractStore,
136
139
  private noteStore: NoteStore,
137
140
  private keyStore: KeyStore,
138
141
  private addressStore: AddressStore,
@@ -149,12 +152,11 @@ export class TXESession implements TXESessionStateHandler {
149
152
  private nextBlockTimestamp: bigint,
150
153
  ) {}
151
154
 
152
- static async init(protocolContracts: ProtocolContract[]) {
155
+ static async init(contractStore: ContractStore) {
153
156
  const store = await openTmpStore('txe-session');
154
157
 
155
158
  const addressStore = new AddressStore(store);
156
159
  const privateEventStore = new PrivateEventStore(store);
157
- const contractStore = new TXEContractStore(store);
158
160
  const noteStore = new NoteStore(store);
159
161
  const senderTaggingStore = new SenderTaggingStore(store);
160
162
  const recipientTaggingStore = new RecipientTaggingStore(store);
@@ -173,12 +175,6 @@ export class TXESession implements TXESessionStateHandler {
173
175
  noteStore,
174
176
  ]);
175
177
 
176
- // Register protocol contracts.
177
- for (const { contractClass, instance, artifact } of protocolContracts) {
178
- await contractStore.addContractArtifact(contractClass.id, artifact);
179
- await contractStore.addContractInstance(instance);
180
- }
181
-
182
178
  const archiver = new TXEArchiver(store);
183
179
  const anchorBlockStore = new AnchorBlockStore(store);
184
180
  const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
@@ -201,7 +197,6 @@ export class TXESession implements TXESessionStateHandler {
201
197
  senderAddressBookStore,
202
198
  capsuleStore,
203
199
  privateEventStore,
204
- initialJobId,
205
200
  nextBlockTimestamp,
206
201
  version,
207
202
  chainId,
@@ -262,6 +257,17 @@ export class TXESession implements TXESessionStateHandler {
262
257
  }
263
258
  }
264
259
 
260
+ getCurrentJob(): string {
261
+ return this.currentJobId;
262
+ }
263
+
264
+ /** Commits the current job and begins a new one. Returns the new job ID. */
265
+ async cycleJob(): Promise<string> {
266
+ await this.jobCoordinator.commitJob(this.currentJobId);
267
+ this.currentJobId = this.jobCoordinator.beginJob();
268
+ return this.currentJobId;
269
+ }
270
+
265
271
  async enterTopLevelState() {
266
272
  switch (this.state.name) {
267
273
  case 'PRIVATE': {
@@ -285,8 +291,7 @@ export class TXESession implements TXESessionStateHandler {
285
291
  }
286
292
 
287
293
  // Commit all staged stores from the job that was just completed, then begin a new job
288
- await this.jobCoordinator.commitJob(this.currentJobId);
289
- this.currentJobId = this.jobCoordinator.beginJob();
294
+ await this.cycleJob();
290
295
 
291
296
  this.oracleHandler = new TXEOracleTopLevelContext(
292
297
  this.stateMachine,
@@ -300,7 +305,6 @@ export class TXESession implements TXESessionStateHandler {
300
305
  this.senderAddressBookStore,
301
306
  this.capsuleStore,
302
307
  this.privateEventStore,
303
- this.currentJobId,
304
308
  this.nextBlockTimestamp,
305
309
  this.version,
306
310
  this.chainId,