@aztec/txe 0.0.1-commit.96bb3f7 → 0.0.1-commit.993d240

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 (138) hide show
  1. package/dest/ContractClassRegistry-KKNT527R.js +3 -0
  2. package/dest/ContractClassRegistry-KKNT527R.js.map +7 -0
  3. package/dest/ContractInstanceRegistry-GSKBNL3Q.js +3 -0
  4. package/dest/ContractInstanceRegistry-GSKBNL3Q.js.map +7 -0
  5. package/dest/FeeJuice-GUJ5O6GB.js +3 -0
  6. package/dest/FeeJuice-GUJ5O6GB.js.map +7 -0
  7. package/dest/MultiCallEntrypoint-ZQPCKVKL.js +3 -0
  8. package/dest/MultiCallEntrypoint-ZQPCKVKL.js.map +7 -0
  9. package/dest/SchnorrAccount-QIXE5DXA.js +3 -0
  10. package/dest/SchnorrAccount-QIXE5DXA.js.map +7 -0
  11. package/dest/bin/check_txe_oracle_version.d.ts +2 -0
  12. package/dest/bin/check_txe_oracle_version.d.ts.map +1 -0
  13. package/dest/bin/check_txe_oracle_version.js +32 -0
  14. package/dest/bin/index.js +3 -30
  15. package/dest/bin/index.js.map +7 -0
  16. package/dest/chunk-BJVAAXNA.js +3 -0
  17. package/dest/chunk-BJVAAXNA.js.map +7 -0
  18. package/dest/chunk-DF6RMOC6.js +304 -0
  19. package/dest/chunk-DF6RMOC6.js.map +7 -0
  20. package/dest/chunk-PPJTNNUG.js +265 -0
  21. package/dest/chunk-PPJTNNUG.js.map +7 -0
  22. package/dest/constants.d.ts +3 -2
  23. package/dest/constants.d.ts.map +1 -1
  24. package/dest/constants.js +5 -1
  25. package/dest/dispatcher_pool.d.ts +67 -0
  26. package/dest/dispatcher_pool.d.ts.map +1 -0
  27. package/dest/dispatcher_pool.js +284 -0
  28. package/dest/index.d.ts +188 -7
  29. package/dest/index.d.ts.map +1 -1
  30. package/dest/index.js +148 -75
  31. package/dest/metafile.json +38147 -0
  32. package/dest/msgpackr_fr_extension.d.ts +2 -0
  33. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  34. package/dest/msgpackr_fr_extension.js +21 -0
  35. package/dest/oracle/interfaces.d.ts +40 -28
  36. package/dest/oracle/interfaces.d.ts.map +1 -1
  37. package/dest/oracle/txe_oracle_public_context.d.ts +18 -13
  38. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  39. package/dest/oracle/txe_oracle_public_context.js +31 -16
  40. package/dest/oracle/txe_oracle_top_level_context.d.ts +39 -23
  41. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  42. package/dest/oracle/txe_oracle_top_level_context.js +225 -89
  43. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  44. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  45. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  46. package/dest/rpc_server.d.ts +14 -0
  47. package/dest/rpc_server.d.ts.map +1 -0
  48. package/dest/rpc_server.js +78 -0
  49. package/dest/rpc_translator.d.ts +144 -82
  50. package/dest/rpc_translator.d.ts.map +1 -1
  51. package/dest/rpc_translator.js +599 -238
  52. package/dest/server.bundle.js +3 -0
  53. package/dest/server.bundle.js.map +7 -0
  54. package/dest/state_machine/archiver.d.ts +20 -69
  55. package/dest/state_machine/archiver.d.ts.map +1 -1
  56. package/dest/state_machine/archiver.js +47 -187
  57. package/dest/state_machine/dummy_p2p_client.d.ts +26 -16
  58. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  59. package/dest/state_machine/dummy_p2p_client.js +44 -20
  60. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  61. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  62. package/dest/state_machine/global_variable_builder.js +9 -3
  63. package/dest/state_machine/index.d.ts +10 -6
  64. package/dest/state_machine/index.d.ts.map +1 -1
  65. package/dest/state_machine/index.js +55 -14
  66. package/dest/state_machine/mock_epoch_cache.d.ts +25 -8
  67. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  68. package/dest/state_machine/mock_epoch_cache.js +46 -9
  69. package/dest/state_machine/synchronizer.d.ts +6 -6
  70. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  71. package/dest/state_machine/synchronizer.js +4 -4
  72. package/dest/txe_oracle_version.d.ts +17 -0
  73. package/dest/txe_oracle_version.d.ts.map +1 -0
  74. package/dest/txe_oracle_version.js +14 -0
  75. package/dest/txe_session.d.ts +73 -10
  76. package/dest/txe_session.d.ts.map +1 -1
  77. package/dest/txe_session.js +242 -39
  78. package/dest/utils/block_creation.d.ts +4 -4
  79. package/dest/utils/block_creation.d.ts.map +1 -1
  80. package/dest/utils/block_creation.js +18 -4
  81. package/dest/utils/encoding.d.ts +191 -0
  82. package/dest/utils/encoding.d.ts.map +1 -0
  83. package/dest/{util → utils}/encoding.js +7 -2
  84. package/dest/{util → utils}/expected_failure_error.d.ts +1 -1
  85. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  86. package/dest/utils/tx_effect_creation.d.ts +2 -3
  87. package/dest/utils/tx_effect_creation.d.ts.map +1 -1
  88. package/dest/utils/tx_effect_creation.js +3 -6
  89. package/dest/{util → utils}/txe_account_store.d.ts +1 -1
  90. package/dest/utils/txe_account_store.d.ts.map +1 -0
  91. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  92. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  93. package/dest/{util → utils}/txe_public_contract_data_source.js +6 -25
  94. package/dest/worker.bundle.js +3 -0
  95. package/dest/worker.bundle.js.map +7 -0
  96. package/dest/worker.d.ts +2 -0
  97. package/dest/worker.d.ts.map +1 -0
  98. package/dest/worker.js +92 -0
  99. package/package.json +38 -22
  100. package/src/bin/check_txe_oracle_version.ts +37 -0
  101. package/src/bin/index.ts +11 -2
  102. package/src/constants.ts +6 -1
  103. package/src/dispatcher_pool.ts +316 -0
  104. package/src/index.ts +166 -80
  105. package/src/msgpackr_fr_extension.ts +23 -0
  106. package/src/oracle/interfaces.ts +48 -34
  107. package/src/oracle/txe_oracle_public_context.ts +46 -18
  108. package/src/oracle/txe_oracle_top_level_context.ts +282 -151
  109. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  110. package/src/rpc_server.ts +87 -0
  111. package/src/rpc_translator.ts +687 -326
  112. package/src/state_machine/archiver.ts +53 -235
  113. package/src/state_machine/dummy_p2p_client.ts +70 -32
  114. package/src/state_machine/global_variable_builder.ts +18 -3
  115. package/src/state_machine/index.ts +72 -15
  116. package/src/state_machine/mock_epoch_cache.ts +57 -14
  117. package/src/state_machine/synchronizer.ts +6 -6
  118. package/src/txe_oracle_version.ts +17 -0
  119. package/src/txe_session.ts +352 -104
  120. package/src/utils/block_creation.ts +19 -16
  121. package/src/{util → utils}/encoding.ts +8 -2
  122. package/src/utils/tx_effect_creation.ts +3 -11
  123. package/src/{util → utils}/txe_public_contract_data_source.ts +10 -38
  124. package/src/worker.ts +98 -0
  125. package/dest/util/encoding.d.ts +0 -720
  126. package/dest/util/encoding.d.ts.map +0 -1
  127. package/dest/util/expected_failure_error.d.ts.map +0 -1
  128. package/dest/util/txe_account_store.d.ts.map +0 -1
  129. package/dest/util/txe_contract_store.d.ts +0 -12
  130. package/dest/util/txe_contract_store.d.ts.map +0 -1
  131. package/dest/util/txe_contract_store.js +0 -22
  132. package/dest/util/txe_public_contract_data_source.d.ts +0 -21
  133. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  134. package/src/util/txe_contract_store.ts +0 -36
  135. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  136. /package/dest/{util → utils}/txe_account_store.js +0 -0
  137. /package/src/{util → utils}/expected_failure_error.ts +0 -0
  138. /package/src/{util → utils}/txe_account_store.ts +0 -0
@@ -2,11 +2,15 @@ import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  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
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
6
- import type { ProtocolContract } from '@aztec/protocol-contracts';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import { openEphemeralStore } from '@aztec/kv-store/lmdb-v2';
7
7
  import {
8
8
  AddressStore,
9
+ AnchorBlockStore,
10
+ CapsuleService,
9
11
  CapsuleStore,
12
+ ContractStore,
13
+ JobCoordinator,
10
14
  NoteService,
11
15
  NoteStore,
12
16
  PrivateEventStore,
@@ -21,7 +25,6 @@ import {
21
25
  type IPrivateExecutionOracle,
22
26
  type IUtilityExecutionOracle,
23
27
  Oracle,
24
- PrivateExecutionOracle,
25
28
  UtilityExecutionOracle,
26
29
  } from '@aztec/pxe/simulator';
27
30
  import {
@@ -32,28 +35,31 @@ import {
32
35
  resolveAssertionMessageFromError,
33
36
  toACVMWitness,
34
37
  } from '@aztec/simulator/client';
38
+ import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
35
39
  import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
36
40
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
37
41
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
38
- import { GasSettings } from '@aztec/stdlib/gas';
42
+ import type { GasSettings } from '@aztec/stdlib/gas';
39
43
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
40
44
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
41
45
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
42
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
46
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
43
47
 
44
48
  import { z } from 'zod';
45
49
 
46
- import { DEFAULT_ADDRESS, TXE_JOB_ID } from './constants.js';
50
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
47
51
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
48
52
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
49
53
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
54
+ import { TXEPrivateExecutionOracle } from './oracle/txe_private_execution_oracle.js';
50
55
  import { RPCTranslator } from './rpc_translator.js';
56
+ import { TXEArchiver } from './state_machine/archiver.js';
51
57
  import { TXEStateMachine } from './state_machine/index.js';
52
- import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
53
- import { TXEAccountStore } from './util/txe_account_store.js';
54
- import { TXEContractStore } from './util/txe_contract_store.js';
58
+ import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './txe_oracle_version.js';
55
59
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
60
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
56
61
  import { makeTxEffect } from './utils/tx_effect_creation.js';
62
+ import { TXEAccountStore } from './utils/txe_account_store.js';
57
63
 
58
64
  /**
59
65
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -74,7 +80,6 @@ type SessionState =
74
80
  | {
75
81
  name: 'PRIVATE';
76
82
  nextBlockGlobalVariables: GlobalVariables;
77
- protocolNullifier: Fr;
78
83
  noteCache: ExecutionNoteCache;
79
84
  taggingIndexCache: ExecutionTaggingIndexCache;
80
85
  }
@@ -107,10 +112,80 @@ export type TXEOracleFunctionName = Exclude<
107
112
  >;
108
113
 
109
114
  export interface TXESessionStateHandler {
115
+ /** Records the TXE oracle version reported by the Noir test code for diagnostics. */
116
+ setTxeOracleVersion(major: number, minor: number): void;
117
+
110
118
  enterTopLevelState(): Promise<void>;
111
119
  enterPublicState(contractAddress?: AztecAddress): Promise<void>;
112
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
120
+ enterPrivateState(
121
+ contractAddress: AztecAddress | undefined,
122
+ anchorBlockNumber: BlockNumber | undefined,
123
+ gasSettings: GasSettings,
124
+ ): Promise<PrivateContextInputs>;
113
125
  enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
126
+
127
+ // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
128
+ cycleJob(): Promise<string>;
129
+ getCurrentJob(): string;
130
+
131
+ /**
132
+ * Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
133
+ */
134
+ withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
135
+
136
+ /**
137
+ * Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
138
+ * oracle handler whenever a contract function emits an offchain message, at any call depth.
139
+ */
140
+ recordOffchainEffect(data: Fr[]): void;
141
+
142
+ /**
143
+ * Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
144
+ * convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
145
+ * `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
146
+ * unqueried-messages warning doesn't fire on the next reset.
147
+ */
148
+ getLastCallOffchainEffects(): { effects: Fr[][] };
149
+
150
+ /**
151
+ * Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
152
+ * block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
153
+ * metadata, not effect consumption.
154
+ */
155
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
156
+ }
157
+
158
+ /**
159
+ * Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
160
+ * call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
161
+ * of whether the call produced offchain effects.
162
+ */
163
+ interface LastCallState {
164
+ /**
165
+ * Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
166
+ * at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
167
+ */
168
+ offchainEffects: Fr[][];
169
+ /**
170
+ * Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
171
+ * without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
172
+ * drop delivery.
173
+ */
174
+ queried: boolean;
175
+ /**
176
+ * Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
177
+ * utility execution). Populated by call executor handlers after execution completes.
178
+ */
179
+ txHash: Fr;
180
+ /**
181
+ * Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
182
+ * was active when the call started. Populated by call executor handlers after execution completes.
183
+ */
184
+ anchorBlockTimestamp: bigint;
185
+ }
186
+
187
+ function emptyLastCallState(): LastCallState {
188
+ return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
114
189
  }
115
190
 
116
191
  /**
@@ -120,16 +195,21 @@ export interface TXESessionStateHandler {
120
195
  export class TXESession implements TXESessionStateHandler {
121
196
  private state: SessionState = { name: 'TOP_LEVEL' };
122
197
  private authwits: Map<string, AuthWitness> = new Map();
198
+ private lastCallInfo: LastCallState = emptyLastCallState();
199
+ private txeOracleVersion: { major: number; minor: number } | undefined;
200
+
201
+ private disposed = false;
123
202
 
124
203
  constructor(
125
204
  private logger: Logger,
205
+ private sessionStore: AztecAsyncKVStore,
126
206
  private stateMachine: TXEStateMachine,
127
207
  private oracleHandler:
128
208
  | IUtilityExecutionOracle
129
209
  | IPrivateExecutionOracle
130
210
  | IAvmExecutionOracle
131
211
  | ITxeExecutionOracle,
132
- private contractStore: TXEContractStore,
212
+ private contractStore: ContractStore,
133
213
  private noteStore: NoteStore,
134
214
  private keyStore: KeyStore,
135
215
  private addressStore: AddressStore,
@@ -139,18 +219,44 @@ export class TXESession implements TXESessionStateHandler {
139
219
  private senderAddressBookStore: SenderAddressBookStore,
140
220
  private capsuleStore: CapsuleStore,
141
221
  private privateEventStore: PrivateEventStore,
222
+ private jobCoordinator: JobCoordinator,
223
+ private currentJobId: string,
142
224
  private chainId: Fr,
143
225
  private version: Fr,
144
226
  private nextBlockTimestamp: bigint,
145
227
  ) {}
146
228
 
147
- static async init(protocolContracts: ProtocolContract[]) {
148
- const store = await openTmpStore('txe-session');
229
+ /**
230
+ * Closes the per-session `txe-session` LMDB and the `NativeWorldStateService` .
231
+ * Called via IPC when the dispatcher detects the end of a test. Idempotent.
232
+ */
233
+ async dispose(): Promise<void> {
234
+ if (this.disposed) {
235
+ return;
236
+ }
237
+ this.disposed = true;
238
+ try {
239
+ await this.stateMachine.synchronizer.nativeWorldStateService.close();
240
+ } catch (err) {
241
+ this.logger.warn(`Error closing native world state during session dispose`, err);
242
+ }
243
+ try {
244
+ await this.sessionStore.close();
245
+ } catch (err) {
246
+ this.logger.warn(`Error closing session LMDB during dispose`, err);
247
+ }
248
+ }
249
+
250
+ static async init(contractStore: ContractStore) {
251
+ // Size LMDB's reader slots to the libuv pool (capped to 2 in bin/index.ts via
252
+ // HARDWARE_CONCURRENCY): each native LMDB read needs a libuv worker thread to run, so any
253
+ // slot beyond the pool size would sit idle while still consuming a semaphore + reader-table
254
+ // entry per session.
255
+ const store = await openEphemeralStore('txe-session', undefined, 2);
149
256
 
150
257
  const addressStore = new AddressStore(store);
151
258
  const privateEventStore = new PrivateEventStore(store);
152
- const contractStore = new TXEContractStore(store);
153
- const noteStore = await NoteStore.create(store);
259
+ const noteStore = new NoteStore(store);
154
260
  const senderTaggingStore = new SenderTaggingStore(store);
155
261
  const recipientTaggingStore = new RecipientTaggingStore(store);
156
262
  const senderAddressBookStore = new SenderAddressBookStore(store);
@@ -158,18 +264,27 @@ export class TXESession implements TXESessionStateHandler {
158
264
  const keyStore = new KeyStore(store);
159
265
  const accountStore = new TXEAccountStore(store);
160
266
 
161
- // Register protocol contracts.
162
- for (const { contractClass, instance, artifact } of protocolContracts) {
163
- await contractStore.addContractArtifact(contractClass.id, artifact);
164
- await contractStore.addContractInstance(instance);
165
- }
267
+ const jobCoordinator = new JobCoordinator(store);
268
+ jobCoordinator.registerStores([
269
+ capsuleStore,
270
+ senderTaggingStore,
271
+ recipientTaggingStore,
272
+ privateEventStore,
273
+ noteStore,
274
+ ]);
166
275
 
167
- const stateMachine = await TXEStateMachine.create(store);
276
+ const archiver = new TXEArchiver(store);
277
+ const anchorBlockStore = new AnchorBlockStore(store);
278
+ const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
168
279
 
169
280
  const nextBlockTimestamp = BigInt(Math.floor(new Date().getTime() / 1000));
170
281
  const version = new Fr(await stateMachine.node.getVersion());
171
282
  const chainId = new Fr(await stateMachine.node.getChainId());
172
283
 
284
+ const initialJobId = jobCoordinator.beginJob();
285
+
286
+ const logger = createLogger('txe:session');
287
+
173
288
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
174
289
  stateMachine,
175
290
  contractStore,
@@ -187,10 +302,12 @@ export class TXESession implements TXESessionStateHandler {
187
302
  chainId,
188
303
  new Map(),
189
304
  );
190
- await topLevelOracleHandler.txeAdvanceBlocksBy(1);
305
+
306
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
191
307
 
192
308
  return new TXESession(
193
- createLogger('txe:session'),
309
+ logger,
310
+ store,
194
311
  stateMachine,
195
312
  topLevelOracleHandler,
196
313
  contractStore,
@@ -203,6 +320,8 @@ export class TXESession implements TXESessionStateHandler {
203
320
  senderAddressBookStore,
204
321
  capsuleStore,
205
322
  privateEventStore,
323
+ jobCoordinator,
324
+ initialJobId,
206
325
  version,
207
326
  chainId,
208
327
  nextBlockTimestamp,
@@ -227,7 +346,28 @@ export class TXESession implements TXESessionStateHandler {
227
346
  return translator[validatedFunctionName](...inputs);
228
347
  } catch (error) {
229
348
  if (error instanceof z.ZodError) {
230
- throw new Error(`${functionName} does not correspond to any oracle handler available on RPCTranslator`);
349
+ let versionHint: string;
350
+ if (!this.txeOracleVersion) {
351
+ versionHint =
352
+ ' The test appears to use an older version of Aztec.nr that does not' +
353
+ ' support test environment oracle versioning. Update Aztec.nr to a compatible version.' +
354
+ ' See https://docs.aztec.network/errors/12';
355
+ } else if (this.txeOracleVersion.minor > TXE_ORACLE_VERSION_MINOR) {
356
+ versionHint =
357
+ ` The test uses Aztec.nr test oracle version` +
358
+ ` ${this.txeOracleVersion.major}.${this.txeOracleVersion.minor}, but this test environment` +
359
+ ` only supports up to ${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}.` +
360
+ ` Upgrade the Aztec CLI to a compatible version.` +
361
+ ` See https://docs.aztec.network/errors/12`;
362
+ } else {
363
+ versionHint =
364
+ ` The test's oracle version (${this.txeOracleVersion.major}.${this.txeOracleVersion.minor})` +
365
+ ` is compatible with this test environment` +
366
+ ` (${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}), so this oracle should be` +
367
+ ` available. This is an unexpected error, please report it.` +
368
+ ` See https://docs.aztec.network/errors/13`;
369
+ }
370
+ throw new Error(`Unknown oracle '${functionName}'.${versionHint}`);
231
371
  } else if (error instanceof Error) {
232
372
  throw new Error(
233
373
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -240,6 +380,86 @@ export class TXESession implements TXESessionStateHandler {
240
380
  }
241
381
  }
242
382
 
383
+ getCurrentJob(): string {
384
+ return this.currentJobId;
385
+ }
386
+
387
+ /** Commits the current job and begins a new one. Returns the new job ID. */
388
+ async cycleJob(): Promise<string> {
389
+ await this.jobCoordinator.commitJob(this.currentJobId);
390
+ this.currentJobId = this.jobCoordinator.beginJob();
391
+ return this.currentJobId;
392
+ }
393
+
394
+ private resetLastCall(): void {
395
+ const notQueriedMessageCount = this.lastCallInfo.queried
396
+ ? 0
397
+ : this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
398
+ if (notQueriedMessageCount > 0) {
399
+ this.logger.warn(
400
+ `Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
401
+ `To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
402
+ `\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
403
+ `to \`let _ = env.offchain_messages()\` to silence this warning.`,
404
+ );
405
+ }
406
+ this.lastCallInfo = emptyLastCallState();
407
+ }
408
+
409
+ recordOffchainEffect(data: Fr[]): void {
410
+ this.lastCallInfo.offchainEffects.push(data);
411
+ }
412
+
413
+ private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
414
+ this.lastCallInfo.txHash = txHash;
415
+ this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
416
+ }
417
+
418
+ async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
419
+ this.resetLastCall();
420
+ // Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
421
+ // side effect, and that block's timestamp should not be attributed to this call's anchor.
422
+ const anchorBlockTimestamp = (await this.stateMachine.node.getBlockData('latest'))!.header.globalVariables
423
+ .timestamp;
424
+ const { result, txHash } = await work();
425
+ this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
426
+ return result;
427
+ }
428
+
429
+ getLastCallOffchainEffects(): { effects: Fr[][] } {
430
+ this.lastCallInfo.queried = true;
431
+ const effects = this.lastCallInfo.offchainEffects;
432
+
433
+ if (effects.length > MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY) {
434
+ throw new Error(`${effects.length} offchain effects exceed max ${MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY}`);
435
+ }
436
+ if (effects.some(e => e.length > MAX_OFFCHAIN_EFFECT_LEN)) {
437
+ throw new Error(`Some offchain effect has length larger than max ${MAX_OFFCHAIN_EFFECT_LEN}`);
438
+ }
439
+
440
+ return { effects };
441
+ }
442
+
443
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
444
+ const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
445
+ return { txHash, anchorBlockTimestamp };
446
+ }
447
+
448
+ setTxeOracleVersion(major: number, minor: number): void {
449
+ if (major !== TXE_ORACLE_VERSION_MAJOR) {
450
+ const hint =
451
+ major > TXE_ORACLE_VERSION_MAJOR
452
+ ? 'The test was compiled with a newer version of Aztec.nr than your test environment supports. Upgrade your test environment to a compatible version.'
453
+ : 'The test was compiled with an older version of Aztec.nr than your test environment supports. Recompile the test with a compatible version of Aztec.nr.';
454
+ throw new Error(
455
+ `Incompatible test environment version: ${hint} See https://docs.aztec.network/errors/12 (expected test oracle major version ${TXE_ORACLE_VERSION_MAJOR}, got ${major})`,
456
+ );
457
+ }
458
+
459
+ this.txeOracleVersion = { major, minor };
460
+ this.logger.debug(`Test compiled with test oracle version ${major}.${minor}`);
461
+ }
462
+
243
463
  async enterTopLevelState() {
244
464
  switch (this.state.name) {
245
465
  case 'PRIVATE': {
@@ -262,6 +482,9 @@ export class TXESession implements TXESessionStateHandler {
262
482
  }
263
483
  }
264
484
 
485
+ // Commit all staged stores from the job that was just completed, then begin a new job
486
+ await this.cycleJob();
487
+
265
488
  this.oracleHandler = new TXEOracleTopLevelContext(
266
489
  this.stateMachine,
267
490
  this.contractStore,
@@ -286,21 +509,22 @@ export class TXESession implements TXESessionStateHandler {
286
509
 
287
510
  async enterPrivateState(
288
511
  contractAddress: AztecAddress = DEFAULT_ADDRESS,
289
- anchorBlockNumber?: BlockNumber,
512
+ anchorBlockNumber: BlockNumber | undefined,
513
+ gasSettings: GasSettings,
290
514
  ): Promise<PrivateContextInputs> {
291
515
  this.exitTopLevelState();
292
-
293
- await new NoteService(
294
- this.noteStore,
295
- this.stateMachine.node,
296
- this.stateMachine.anchorBlockStore,
297
- ).syncNoteNullifiers(contractAddress);
516
+ this.resetLastCall();
298
517
 
299
518
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
300
519
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
301
520
  // a single transaction with the effects of what was done in the test.
302
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
303
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
521
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
522
+
523
+ await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
524
+ contractAddress,
525
+ await this.keyStore.getAccounts(),
526
+ );
527
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
304
528
 
305
529
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
306
530
  blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
@@ -315,50 +539,59 @@ export class TXESession implements TXESessionStateHandler {
315
539
  const taggingIndexCache = new ExecutionTaggingIndexCache();
316
540
 
317
541
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
318
- this.oracleHandler = new PrivateExecutionOracle(
319
- Fr.ZERO,
320
- new TxContext(this.chainId, this.version, GasSettings.empty()),
321
- new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
322
- anchorBlock!,
542
+ this.oracleHandler = new TXEPrivateExecutionOracle({
543
+ argsHash: Fr.ZERO,
544
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
545
+ callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
546
+ anchorBlockHeader: anchorBlock!,
323
547
  utilityExecutor,
324
- [],
325
- [],
326
- new HashedValuesCache(),
548
+ authWitnesses: [],
549
+ capsules: [],
550
+ executionCache: new HashedValuesCache(),
327
551
  noteCache,
328
552
  taggingIndexCache,
329
- this.contractStore,
330
- this.noteStore,
331
- this.keyStore,
332
- this.addressStore,
333
- this.stateMachine.node,
334
- this.stateMachine.anchorBlockStore,
335
- this.senderTaggingStore,
336
- this.recipientTaggingStore,
337
- this.senderAddressBookStore,
338
- this.capsuleStore,
339
- this.privateEventStore,
340
- TXE_JOB_ID,
341
- );
553
+ contractStore: this.contractStore,
554
+ noteStore: this.noteStore,
555
+ keyStore: this.keyStore,
556
+ addressStore: this.addressStore,
557
+ aztecNode: this.stateMachine.node,
558
+ senderTaggingStore: this.senderTaggingStore,
559
+ recipientTaggingStore: this.recipientTaggingStore,
560
+ senderAddressBookStore: this.senderAddressBookStore,
561
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
562
+ privateEventStore: this.privateEventStore,
563
+ contractSyncService: this.stateMachine.contractSyncService,
564
+ l2TipsStore: this.stateMachine.l2TipsProvider,
565
+ jobId: this.currentJobId,
566
+ scopes: await this.keyStore.getAccounts(),
567
+ messageContextService: this.stateMachine.messageContextService,
568
+ simulator: new WASMSimulator(),
569
+ });
342
570
 
343
571
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
344
572
  // data) in order to refer to it later, mimicking the way this object is used by the ContractFunctionSimulator. The
345
573
  // difference resides in that the simulator has all information needed in order to run the simulation, while ours
346
574
  // will be ongoing as the different oracles will be invoked from the Noir test, until eventually the private
347
575
  // execution finishes.
348
- this.state = { name: 'PRIVATE', nextBlockGlobalVariables, protocolNullifier, noteCache, taggingIndexCache };
576
+ this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
349
577
  this.logger.debug(`Entered state ${this.state.name}`);
350
578
 
351
- return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
579
+ // Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
580
+ // via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
581
+ this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
582
+
583
+ return (this.oracleHandler as TXEPrivateExecutionOracle).getPrivateContextInputs();
352
584
  }
353
585
 
354
586
  async enterPublicState(contractAddress?: AztecAddress) {
355
587
  this.exitTopLevelState();
588
+ this.resetLastCall();
356
589
 
357
590
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
358
591
  // the test. The block therefore gets the *next* block number and timestamp.
359
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
592
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
360
593
  const globalVariables = makeGlobalVariables(undefined, {
361
- blockNumber: BlockNumber(latestBlockNumber + 1),
594
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
362
595
  timestamp: this.nextBlockTimestamp,
363
596
  version: this.version,
364
597
  chainId: this.chainId,
@@ -373,10 +606,16 @@ export class TXESession implements TXESessionStateHandler {
373
606
 
374
607
  this.state = { name: 'PUBLIC' };
375
608
  this.logger.debug(`Entered state ${this.state.name}`);
609
+
610
+ // Public state is anchored at the latest block.
611
+ this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
376
612
  }
377
613
 
378
614
  async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
379
615
  this.exitTopLevelState();
616
+ this.resetLastCall();
617
+
618
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
380
619
 
381
620
  // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
382
621
  // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
@@ -386,31 +625,38 @@ export class TXESession implements TXESessionStateHandler {
386
625
  await new NoteService(
387
626
  this.noteStore,
388
627
  this.stateMachine.node,
389
- this.stateMachine.anchorBlockStore,
390
- ).syncNoteNullifiers(contractAddress);
391
-
392
- const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
628
+ anchorBlockHeader,
629
+ this.currentJobId,
630
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
393
631
 
394
- this.oracleHandler = new UtilityExecutionOracle(
632
+ this.oracleHandler = new UtilityExecutionOracle({
395
633
  contractAddress,
396
- [],
397
- [],
634
+ authWitnesses: [],
635
+ capsules: [],
398
636
  anchorBlockHeader,
399
- this.contractStore,
400
- this.noteStore,
401
- this.keyStore,
402
- this.addressStore,
403
- this.stateMachine.node,
404
- this.stateMachine.anchorBlockStore,
405
- this.recipientTaggingStore,
406
- this.senderAddressBookStore,
407
- this.capsuleStore,
408
- this.privateEventStore,
409
- TXE_JOB_ID,
410
- );
637
+ contractStore: this.contractStore,
638
+ noteStore: this.noteStore,
639
+ keyStore: this.keyStore,
640
+ addressStore: this.addressStore,
641
+ aztecNode: this.stateMachine.node,
642
+ recipientTaggingStore: this.recipientTaggingStore,
643
+ senderAddressBookStore: this.senderAddressBookStore,
644
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
645
+ privateEventStore: this.privateEventStore,
646
+ messageContextService: this.stateMachine.messageContextService,
647
+ contractSyncService: this.stateMachine.contractSyncService,
648
+ l2TipsStore: this.stateMachine.l2TipsProvider,
649
+ jobId: this.currentJobId,
650
+ scopes: await this.keyStore.getAccounts(),
651
+ simulator: new WASMSimulator(),
652
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
653
+ });
411
654
 
412
655
  this.state = { name: 'UTILITY' };
413
656
  this.logger.debug(`Entered state ${this.state.name}`);
657
+
658
+ // Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
659
+ this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
414
660
  }
415
661
 
416
662
  private exitTopLevelState() {
@@ -420,8 +666,8 @@ export class TXESession implements TXESessionStateHandler {
420
666
 
421
667
  // Note that while all public and private contexts do is build a single block that we then process when exiting
422
668
  // those, the top level context performs a large number of actions not captured in the following 'close' call. Among
423
- // others, it will create empty blocks (via `txeAdvanceBlocksBy` and `deploy`), create blocks with transactions via
424
- // `txePrivateCallNewFlow` and `txePublicCallNewFlow`, add accounts to PXE via `txeAddAccount`, etc. This is a
669
+ // others, it will create empty blocks (via `advanceBlocksBy` and `deploy`), create blocks with transactions via
670
+ // `privateCallNewFlow` and `publicCallNewFlow`, add accounts to PXE via `addAccount`, etc. This is a
425
671
  // slight inconsistency in the working model of this class, but is not too bad.
426
672
  // TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
427
673
  // level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
@@ -442,11 +688,7 @@ export class TXESession implements TXESessionStateHandler {
442
688
  // We rely on the note cache to determine the effects of the transaction. This is incomplete as it doesn't private
443
689
  // logs (other effects like enqueued public calls don't need to be considered since those are not allowed).
444
690
 
445
- const txEffect = await makeTxEffect(
446
- this.state.noteCache,
447
- this.state.protocolNullifier,
448
- this.state.nextBlockGlobalVariables.blockNumber,
449
- );
691
+ const txEffect = await makeTxEffect(this.state.noteCache, this.state.nextBlockGlobalVariables.blockNumber);
450
692
 
451
693
  // We build a block holding just this transaction
452
694
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
@@ -479,31 +721,37 @@ export class TXESession implements TXESessionStateHandler {
479
721
  }
480
722
 
481
723
  private utilityExecutorForContractSync(anchorBlock: any) {
482
- return async (call: FunctionCall) => {
724
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
483
725
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
484
726
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
485
727
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
486
728
  }
487
729
 
488
730
  try {
489
- const oracle = new UtilityExecutionOracle(
490
- call.to,
491
- [],
492
- [],
493
- anchorBlock!,
494
- this.contractStore,
495
- this.noteStore,
496
- this.keyStore,
497
- this.addressStore,
498
- this.stateMachine.node,
499
- this.stateMachine.anchorBlockStore,
500
- this.recipientTaggingStore,
501
- this.senderAddressBookStore,
502
- this.capsuleStore,
503
- this.privateEventStore,
504
- TXE_JOB_ID,
505
- );
506
- await new WASMSimulator()
731
+ const simulator = new WASMSimulator();
732
+ const oracle = new UtilityExecutionOracle({
733
+ contractAddress: call.to,
734
+ authWitnesses: [],
735
+ capsules: [],
736
+ anchorBlockHeader: anchorBlock!,
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
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
745
+ privateEventStore: this.privateEventStore,
746
+ messageContextService: this.stateMachine.messageContextService,
747
+ contractSyncService: this.stateMachine.contractSyncService,
748
+ l2TipsStore: this.stateMachine.l2TipsProvider,
749
+ jobId: this.currentJobId,
750
+ scopes,
751
+ simulator,
752
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
753
+ });
754
+ await simulator
507
755
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
508
756
  .catch((err: Error) => {
509
757
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);