@aztec/txe 0.0.1-commit.2eb6648a → 0.0.1-commit.2f68f620

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