@aztec/txe 0.0.1-commit.24de95ac → 0.0.1-commit.2606882

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 (167) 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.d.ts +1 -1
  15. package/dest/bin/index.js +3 -30
  16. package/dest/bin/index.js.map +7 -0
  17. package/dest/bin/oracle_test_server.d.ts +3 -0
  18. package/dest/bin/oracle_test_server.d.ts.map +1 -0
  19. package/dest/bin/oracle_test_server.js +41 -0
  20. package/dest/chunk-3VX4QA7B.js +265 -0
  21. package/dest/chunk-3VX4QA7B.js.map +7 -0
  22. package/dest/chunk-BJVAAXNA.js +3 -0
  23. package/dest/chunk-BJVAAXNA.js.map +7 -0
  24. package/dest/chunk-FU3LBVE4.js +304 -0
  25. package/dest/chunk-FU3LBVE4.js.map +7 -0
  26. package/dest/constants.d.ts +7 -0
  27. package/dest/constants.d.ts.map +1 -0
  28. package/dest/constants.js +10 -0
  29. package/dest/dispatcher_pool.d.ts +67 -0
  30. package/dest/dispatcher_pool.d.ts.map +1 -0
  31. package/dest/dispatcher_pool.js +284 -0
  32. package/dest/index.d.ts +185 -7
  33. package/dest/index.d.ts.map +1 -1
  34. package/dest/index.js +73 -158
  35. package/dest/metafile.json +38352 -0
  36. package/dest/msgpackr_fr_extension.d.ts +2 -0
  37. package/dest/msgpackr_fr_extension.d.ts.map +1 -0
  38. package/dest/msgpackr_fr_extension.js +21 -0
  39. package/dest/oracle/interfaces.d.ts +60 -31
  40. package/dest/oracle/interfaces.d.ts.map +1 -1
  41. package/dest/oracle/test-resolver/fixtures.d.ts +43 -0
  42. package/dest/oracle/test-resolver/fixtures.d.ts.map +1 -0
  43. package/dest/oracle/test-resolver/fixtures.js +39 -0
  44. package/dest/oracle/test-resolver/index.d.ts +9 -0
  45. package/dest/oracle/test-resolver/index.d.ts.map +1 -0
  46. package/dest/oracle/test-resolver/index.js +33 -0
  47. package/dest/oracle/test-resolver/resolver.d.ts +34 -0
  48. package/dest/oracle/test-resolver/resolver.d.ts.map +1 -0
  49. package/dest/oracle/test-resolver/resolver.js +114 -0
  50. package/dest/oracle/txe_oracle_public_context.d.ts +41 -17
  51. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  52. package/dest/oracle/txe_oracle_public_context.js +63 -23
  53. package/dest/oracle/txe_oracle_registry.d.ts +147 -0
  54. package/dest/oracle/txe_oracle_registry.d.ts.map +1 -0
  55. package/dest/oracle/txe_oracle_registry.js +562 -0
  56. package/dest/oracle/txe_oracle_top_level_context.d.ts +57 -28
  57. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  58. package/dest/oracle/txe_oracle_top_level_context.js +342 -131
  59. package/dest/oracle/txe_oracle_version.d.ts +17 -0
  60. package/dest/oracle/txe_oracle_version.d.ts.map +1 -0
  61. package/dest/oracle/txe_oracle_version.js +14 -0
  62. package/dest/oracle/txe_private_execution_oracle.d.ts +17 -0
  63. package/dest/oracle/txe_private_execution_oracle.d.ts.map +1 -0
  64. package/dest/oracle/txe_private_execution_oracle.js +15 -0
  65. package/dest/rpc_server.d.ts +14 -0
  66. package/dest/rpc_server.d.ts.map +1 -0
  67. package/dest/rpc_server.js +78 -0
  68. package/dest/rpc_translator.d.ts +96 -213
  69. package/dest/rpc_translator.d.ts.map +1 -1
  70. package/dest/rpc_translator.js +815 -550
  71. package/dest/server.bundle.js +3 -0
  72. package/dest/server.bundle.js.map +7 -0
  73. package/dest/state_machine/archiver.d.ts +23 -52
  74. package/dest/state_machine/archiver.d.ts.map +1 -1
  75. package/dest/state_machine/archiver.js +77 -99
  76. package/dest/state_machine/dummy_p2p_client.d.ts +28 -15
  77. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  78. package/dest/state_machine/dummy_p2p_client.js +54 -22
  79. package/dest/state_machine/global_variable_builder.d.ts +11 -4
  80. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  81. package/dest/state_machine/global_variable_builder.js +21 -3
  82. package/dest/state_machine/index.d.ts +11 -7
  83. package/dest/state_machine/index.d.ts.map +1 -1
  84. package/dest/state_machine/index.js +60 -25
  85. package/dest/state_machine/mock_epoch_cache.d.ts +29 -12
  86. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  87. package/dest/state_machine/mock_epoch_cache.js +50 -15
  88. package/dest/state_machine/synchronizer.d.ts +6 -5
  89. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  90. package/dest/state_machine/synchronizer.js +9 -8
  91. package/dest/txe_session.d.ts +94 -21
  92. package/dest/txe_session.d.ts.map +1 -1
  93. package/dest/txe_session.js +314 -68
  94. package/dest/utils/block_creation.d.ts +21 -6
  95. package/dest/utils/block_creation.d.ts.map +1 -1
  96. package/dest/utils/block_creation.js +38 -4
  97. package/dest/utils/encoding.d.ts +191 -0
  98. package/dest/utils/encoding.d.ts.map +1 -0
  99. package/dest/{util → utils}/encoding.js +8 -3
  100. package/dest/utils/expected_failure_error.d.ts +4 -0
  101. package/dest/utils/expected_failure_error.d.ts.map +1 -0
  102. package/dest/utils/tx_effect_creation.d.ts +3 -3
  103. package/dest/utils/tx_effect_creation.d.ts.map +1 -1
  104. package/dest/utils/tx_effect_creation.js +4 -7
  105. package/dest/utils/txe_account_store.d.ts +10 -0
  106. package/dest/utils/txe_account_store.d.ts.map +1 -0
  107. package/dest/{util/txe_account_data_provider.js → utils/txe_account_store.js} +1 -1
  108. package/dest/utils/txe_artifact_resolver.d.ts +37 -0
  109. package/dest/utils/txe_artifact_resolver.d.ts.map +1 -0
  110. package/dest/utils/txe_artifact_resolver.js +161 -0
  111. package/dest/utils/txe_public_contract_data_source.d.ts +20 -0
  112. package/dest/utils/txe_public_contract_data_source.d.ts.map +1 -0
  113. package/dest/utils/txe_public_contract_data_source.js +48 -0
  114. package/dest/worker.bundle.js +3 -0
  115. package/dest/worker.bundle.js.map +7 -0
  116. package/dest/worker.d.ts +2 -0
  117. package/dest/worker.d.ts.map +1 -0
  118. package/dest/worker.js +92 -0
  119. package/package.json +40 -22
  120. package/src/bin/check_txe_oracle_version.ts +70 -0
  121. package/src/bin/index.ts +11 -2
  122. package/src/bin/oracle_test_server.ts +51 -0
  123. package/src/constants.ts +13 -0
  124. package/src/dispatcher_pool.ts +316 -0
  125. package/src/index.ts +111 -201
  126. package/src/msgpackr_fr_extension.ts +23 -0
  127. package/src/oracle/interfaces.ts +62 -36
  128. package/src/oracle/test-resolver/fixtures.ts +84 -0
  129. package/src/oracle/test-resolver/index.ts +45 -0
  130. package/src/oracle/test-resolver/resolver.ts +165 -0
  131. package/src/oracle/txe_oracle_public_context.ts +81 -28
  132. package/src/oracle/txe_oracle_registry.ts +401 -0
  133. package/src/oracle/txe_oracle_top_level_context.ts +419 -179
  134. package/src/oracle/txe_oracle_version.ts +17 -0
  135. package/src/oracle/txe_private_execution_oracle.ts +30 -0
  136. package/src/rpc_server.ts +87 -0
  137. package/src/rpc_translator.ts +999 -909
  138. package/src/state_machine/archiver.ts +83 -115
  139. package/src/state_machine/dummy_p2p_client.ts +83 -33
  140. package/src/state_machine/global_variable_builder.ts +39 -6
  141. package/src/state_machine/index.ts +82 -24
  142. package/src/state_machine/mock_epoch_cache.ts +63 -23
  143. package/src/state_machine/synchronizer.ts +10 -9
  144. package/src/txe_session.ts +486 -119
  145. package/src/utils/block_creation.ts +49 -14
  146. package/src/{util → utils}/encoding.ts +9 -3
  147. package/src/utils/tx_effect_creation.ts +5 -12
  148. package/src/{util/txe_account_data_provider.ts → utils/txe_account_store.ts} +1 -1
  149. package/src/utils/txe_artifact_resolver.ts +217 -0
  150. package/src/utils/txe_public_contract_data_source.ts +58 -0
  151. package/src/worker.ts +98 -0
  152. package/dest/util/encoding.d.ts +0 -121
  153. package/dest/util/encoding.d.ts.map +0 -1
  154. package/dest/util/expected_failure_error.d.ts +0 -4
  155. package/dest/util/expected_failure_error.d.ts.map +0 -1
  156. package/dest/util/txe_account_data_provider.d.ts +0 -10
  157. package/dest/util/txe_account_data_provider.d.ts.map +0 -1
  158. package/dest/util/txe_contract_data_provider.d.ts +0 -12
  159. package/dest/util/txe_contract_data_provider.d.ts.map +0 -1
  160. package/dest/util/txe_contract_data_provider.js +0 -22
  161. package/dest/util/txe_public_contract_data_source.d.ts +0 -20
  162. package/dest/util/txe_public_contract_data_source.d.ts.map +0 -1
  163. package/dest/util/txe_public_contract_data_source.js +0 -67
  164. package/src/util/txe_contract_data_provider.ts +0 -36
  165. package/src/util/txe_public_contract_data_source.ts +0 -85
  166. /package/dest/{util → utils}/expected_failure_error.js +0 -0
  167. /package/src/{util → utils}/expected_failure_error.ts +0 -0
@@ -1,51 +1,68 @@
1
- import { Fr } from '@aztec/foundation/fields';
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
4
  import { KeyStore } from '@aztec/key-store';
4
- import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
5
- import type { ProtocolContract } from '@aztec/protocol-contracts';
5
+ import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
+ import { openEphemeralStore } from '@aztec/kv-store/lmdb-v2';
6
7
  import {
7
- AddressDataProvider,
8
- CapsuleDataProvider,
9
- NoteDataProvider,
10
- PXEOracleInterface,
11
- PrivateEventDataProvider,
12
- TaggingDataProvider,
8
+ AddressStore,
9
+ AnchorBlockStore,
10
+ CapsuleService,
11
+ CapsuleStore,
12
+ ContractStore,
13
+ JobCoordinator,
14
+ NoteService,
15
+ NoteStore,
16
+ PrivateEventStore,
17
+ RecipientTaggingStore,
18
+ SenderAddressBookStore,
19
+ SenderTaggingStore,
13
20
  } from '@aztec/pxe/server';
14
21
  import {
15
22
  ExecutionNoteCache,
16
23
  ExecutionTaggingIndexCache,
17
24
  HashedValuesCache,
25
+ type IMiscOracle,
18
26
  type IPrivateExecutionOracle,
19
27
  type IUtilityExecutionOracle,
20
- PrivateExecutionOracle,
28
+ Option,
29
+ Oracle,
21
30
  UtilityExecutionOracle,
22
31
  } from '@aztec/pxe/simulator';
23
- import { FunctionSelector } from '@aztec/stdlib/abi';
32
+ import {
33
+ ExecutionError,
34
+ WASMSimulator,
35
+ createSimulationError,
36
+ extractCallStack,
37
+ resolveAssertionMessageFromError,
38
+ toACVMWitness,
39
+ } from '@aztec/simulator/client';
40
+ import { STANDARD_AUTH_REGISTRY_ADDRESS } from '@aztec/standard-contracts/auth-registry/constants';
41
+ import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
24
42
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
25
43
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
26
- import { Body, L2Block } from '@aztec/stdlib/block';
27
- import { GasSettings } from '@aztec/stdlib/gas';
44
+ import type { GasSettings } from '@aztec/stdlib/gas';
45
+ import { computeProtocolNullifier } from '@aztec/stdlib/hash';
28
46
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
29
- import { makeAppendOnlyTreeSnapshot, makeGlobalVariables } from '@aztec/stdlib/testing';
30
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
31
- import type { UInt32 } from '@aztec/stdlib/types';
47
+ import { makeGlobalVariables } from '@aztec/stdlib/testing';
48
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
32
49
 
33
50
  import { z } from 'zod';
34
51
 
52
+ import { DEFAULT_ADDRESS, MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY, MAX_OFFCHAIN_EFFECT_LEN } from './constants.js';
35
53
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
36
54
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
37
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';
38
58
  import { RPCTranslator } from './rpc_translator.js';
59
+ import { TXEArchiver } from './state_machine/archiver.js';
39
60
  import { TXEStateMachine } from './state_machine/index.js';
40
- import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
41
- import { TXEAccountDataProvider } from './util/txe_account_data_provider.js';
42
- import { TXEContractDataProvider } from './util/txe_contract_data_provider.js';
43
- import {
44
- getSingleTxBlockRequestHash,
45
- insertTxEffectIntoWorldTrees,
46
- makeTXEBlockHeader,
47
- } from './utils/block_creation.js';
61
+ import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
62
+ import type { ForeignCallArgs, ForeignCallResult } from './utils/encoding.js';
48
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';
49
66
 
50
67
  /**
51
68
  * A TXE Session can be in one of four states, which change as the test progresses and different oracles are called.
@@ -66,7 +83,6 @@ type SessionState =
66
83
  | {
67
84
  name: 'PRIVATE';
68
85
  nextBlockGlobalVariables: GlobalVariables;
69
- txRequestHash: Fr;
70
86
  noteCache: ExecutionNoteCache;
71
87
  taggingIndexCache: ExecutionTaggingIndexCache;
72
88
  }
@@ -99,13 +115,81 @@ export type TXEOracleFunctionName = Exclude<
99
115
  >;
100
116
 
101
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
+
102
121
  enterTopLevelState(): Promise<void>;
103
- enterPublicState(contractAddress?: AztecAddress): Promise<void>;
104
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: UInt32): Promise<PrivateContextInputs>;
105
- 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 };
106
159
  }
107
160
 
108
- const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
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 };
192
+ }
109
193
 
110
194
  /**
111
195
  * A `TXESession` corresponds to a Noir `#[test]` function, and handles all of its oracle calls, stores test-specific
@@ -114,87 +198,151 @@ const DEFAULT_ADDRESS = AztecAddress.fromNumber(42);
114
198
  export class TXESession implements TXESessionStateHandler {
115
199
  private state: SessionState = { name: 'TOP_LEVEL' };
116
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;
117
205
 
118
206
  constructor(
119
207
  private logger: Logger,
208
+ private sessionStore: AztecAsyncKVStore,
120
209
  private stateMachine: TXEStateMachine,
121
210
  private oracleHandler:
211
+ | IMiscOracle
122
212
  | IUtilityExecutionOracle
123
213
  | IPrivateExecutionOracle
124
214
  | IAvmExecutionOracle
125
215
  | ITxeExecutionOracle,
126
- private contractDataProvider: TXEContractDataProvider,
216
+ private contractStore: ContractStore,
217
+ private noteStore: NoteStore,
127
218
  private keyStore: KeyStore,
128
- private addressDataProvider: AddressDataProvider,
129
- private accountDataProvider: TXEAccountDataProvider,
219
+ private addressStore: AddressStore,
220
+ private accountStore: TXEAccountStore,
221
+ private senderTaggingStore: SenderTaggingStore,
222
+ private recipientTaggingStore: RecipientTaggingStore,
223
+ private senderAddressBookStore: SenderAddressBookStore,
224
+ private capsuleStore: CapsuleStore,
225
+ private privateEventStore: PrivateEventStore,
226
+ private jobCoordinator: JobCoordinator,
227
+ private currentJobId: string,
130
228
  private chainId: Fr,
131
229
  private version: Fr,
132
230
  private nextBlockTimestamp: bigint,
133
- private pxeOracleInterface: PXEOracleInterface,
231
+ private readonly artifactResolver: TXEArtifactResolver,
232
+ private readonly rootPath: string,
233
+ private readonly packageName: string,
134
234
  ) {}
135
235
 
136
- static async init(protocolContracts: ProtocolContract[]) {
137
- 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
+ }
138
256
 
139
- const addressDataProvider = new AddressDataProvider(store);
140
- const privateEventDataProvider = new PrivateEventDataProvider(store);
141
- const contractDataProvider = new TXEContractDataProvider(store);
142
- const noteDataProvider = await NoteDataProvider.create(store);
143
- const taggingDataProvider = new TaggingDataProvider(store);
144
- const capsuleDataProvider = new CapsuleDataProvider(store);
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);
268
+
269
+ const addressStore = new AddressStore(store);
270
+ const privateEventStore = new PrivateEventStore(store);
271
+ const noteStore = new NoteStore(store);
272
+ const senderTaggingStore = new SenderTaggingStore(store);
273
+ const recipientTaggingStore = new RecipientTaggingStore(store);
274
+ const senderAddressBookStore = new SenderAddressBookStore(store);
275
+ const capsuleStore = new CapsuleStore(store);
145
276
  const keyStore = new KeyStore(store);
146
- const accountDataProvider = new TXEAccountDataProvider(store);
277
+ const accountStore = new TXEAccountStore(store);
147
278
 
148
- // Register protocol contracts.
149
- for (const { contractClass, instance, artifact } of protocolContracts) {
150
- await contractDataProvider.addContractArtifact(contractClass.id, artifact);
151
- await contractDataProvider.addContractInstance(instance);
152
- }
279
+ const jobCoordinator = new JobCoordinator(store);
280
+ jobCoordinator.registerStores([
281
+ capsuleStore,
282
+ senderTaggingStore,
283
+ recipientTaggingStore,
284
+ privateEventStore,
285
+ noteStore,
286
+ ]);
153
287
 
154
- const stateMachine = await TXEStateMachine.create(store);
288
+ const archiver = new TXEArchiver(store);
289
+ const anchorBlockStore = new AnchorBlockStore(store);
290
+ const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
155
291
 
156
292
  const nextBlockTimestamp = BigInt(Math.floor(new Date().getTime() / 1000));
157
293
  const version = new Fr(await stateMachine.node.getVersion());
158
294
  const chainId = new Fr(await stateMachine.node.getChainId());
159
295
 
160
- const pxeOracleInterface = new PXEOracleInterface(
161
- stateMachine.node,
162
- keyStore,
163
- contractDataProvider,
164
- noteDataProvider,
165
- capsuleDataProvider,
166
- stateMachine.syncDataProvider,
167
- taggingDataProvider,
168
- addressDataProvider,
169
- privateEventDataProvider,
170
- );
296
+ const initialJobId = jobCoordinator.beginJob();
297
+
298
+ const logger = createLogger('txe:session');
171
299
 
172
300
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
173
301
  stateMachine,
174
- contractDataProvider,
302
+ contractStore,
303
+ noteStore,
175
304
  keyStore,
176
- addressDataProvider,
177
- accountDataProvider,
178
- pxeOracleInterface,
305
+ addressStore,
306
+ accountStore,
307
+ senderTaggingStore,
308
+ recipientTaggingStore,
309
+ senderAddressBookStore,
310
+ capsuleStore,
311
+ privateEventStore,
179
312
  nextBlockTimestamp,
180
313
  version,
181
314
  chainId,
182
315
  new Map(),
316
+ artifactResolver,
317
+ rootPath,
318
+ packageName,
183
319
  );
184
- await topLevelOracleHandler.txeAdvanceBlocksBy(1);
320
+
321
+ await topLevelOracleHandler.mineDeploymentNullifiers([STANDARD_AUTH_REGISTRY_ADDRESS]);
185
322
 
186
323
  return new TXESession(
187
- createLogger('txe:session'),
324
+ logger,
325
+ store,
188
326
  stateMachine,
189
327
  topLevelOracleHandler,
190
- contractDataProvider,
328
+ contractStore,
329
+ noteStore,
191
330
  keyStore,
192
- addressDataProvider,
193
- accountDataProvider,
331
+ addressStore,
332
+ accountStore,
333
+ senderTaggingStore,
334
+ recipientTaggingStore,
335
+ senderAddressBookStore,
336
+ capsuleStore,
337
+ privateEventStore,
338
+ jobCoordinator,
339
+ initialJobId,
194
340
  version,
195
341
  chainId,
196
342
  nextBlockTimestamp,
197
- pxeOracleInterface,
343
+ artifactResolver,
344
+ rootPath,
345
+ packageName,
198
346
  );
199
347
  }
200
348
 
@@ -216,7 +364,28 @@ export class TXESession implements TXESessionStateHandler {
216
364
  return translator[validatedFunctionName](...inputs);
217
365
  } catch (error) {
218
366
  if (error instanceof z.ZodError) {
219
- 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}`);
220
389
  } else if (error instanceof Error) {
221
390
  throw new Error(
222
391
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -229,6 +398,86 @@ export class TXESession implements TXESessionStateHandler {
229
398
  }
230
399
  }
231
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
+
232
481
  async enterTopLevelState() {
233
482
  switch (this.state.name) {
234
483
  case 'PRIVATE': {
@@ -251,17 +500,28 @@ export class TXESession implements TXESessionStateHandler {
251
500
  }
252
501
  }
253
502
 
503
+ // Commit all staged stores from the job that was just completed, then begin a new job
504
+ await this.cycleJob();
505
+
254
506
  this.oracleHandler = new TXEOracleTopLevelContext(
255
507
  this.stateMachine,
256
- this.contractDataProvider,
508
+ this.contractStore,
509
+ this.noteStore,
257
510
  this.keyStore,
258
- this.addressDataProvider,
259
- this.accountDataProvider,
260
- this.pxeOracleInterface,
511
+ this.addressStore,
512
+ this.accountStore,
513
+ this.senderTaggingStore,
514
+ this.recipientTaggingStore,
515
+ this.senderAddressBookStore,
516
+ this.capsuleStore,
517
+ this.privateEventStore,
261
518
  this.nextBlockTimestamp,
262
519
  this.version,
263
520
  this.chainId,
264
521
  this.authwits,
522
+ this.artifactResolver,
523
+ this.rootPath,
524
+ this.packageName,
265
525
  );
266
526
 
267
527
  this.state = { name: 'TOP_LEVEL' };
@@ -269,97 +529,160 @@ export class TXESession implements TXESessionStateHandler {
269
529
  }
270
530
 
271
531
  async enterPrivateState(
272
- contractAddress: AztecAddress = DEFAULT_ADDRESS,
273
- anchorBlockNumber?: UInt32,
532
+ contractAddressOpt: Option<AztecAddress>,
533
+ anchorBlockNumberOpt: Option<BlockNumber>,
534
+ gasSettings: GasSettings,
274
535
  ): Promise<PrivateContextInputs> {
536
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
537
+ const anchorBlockNumber = anchorBlockNumberOpt?.value;
275
538
  this.exitTopLevelState();
276
-
277
- // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
278
- // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
279
- // we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
280
- // be removed from the database.
281
- // TODO(#12553): make the synchronizer sync here instead and remove this
282
- await this.pxeOracleInterface.syncNoteNullifiers(contractAddress);
539
+ this.resetLastCall();
283
540
 
284
541
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
285
542
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
286
543
  // a single transaction with the effects of what was done in the test.
287
- const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
288
- const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
544
+ const anchorBlock = await this.stateMachine.node.getBlock(anchorBlockNumber ?? 'latest').then(b => b?.header);
545
+
546
+ await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
547
+ contractAddress,
548
+ await this.keyStore.getAccounts(),
549
+ );
550
+ const latestBlock = await this.stateMachine.node.getBlock('latest').then(b => b?.header);
289
551
 
290
552
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
291
- blockNumber: latestBlock!.globalVariables.blockNumber + 1,
553
+ blockNumber: BlockNumber(latestBlock!.globalVariables.blockNumber + 1),
292
554
  timestamp: this.nextBlockTimestamp,
293
555
  version: this.version,
294
556
  chainId: this.chainId,
295
557
  });
296
558
 
297
559
  const txRequestHash = getSingleTxBlockRequestHash(nextBlockGlobalVariables.blockNumber);
298
- const noteCache = new ExecutionNoteCache(txRequestHash);
560
+ const protocolNullifier = await computeProtocolNullifier(txRequestHash);
561
+ const noteCache = new ExecutionNoteCache(protocolNullifier);
299
562
  const taggingIndexCache = new ExecutionTaggingIndexCache();
300
563
 
301
- this.oracleHandler = new PrivateExecutionOracle(
302
- Fr.ZERO,
303
- new TxContext(this.chainId, this.version, GasSettings.empty()),
304
- new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
305
- anchorBlock!,
306
- [],
307
- [],
308
- new HashedValuesCache(),
564
+ const utilityExecutor = this.utilityExecutorForContractSync(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!,
570
+ utilityExecutor,
571
+ authWitnesses: [],
572
+ capsules: [],
573
+ executionCache: new HashedValuesCache(),
309
574
  noteCache,
310
575
  taggingIndexCache,
311
- this.pxeOracleInterface,
312
- );
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
+ });
313
593
 
314
594
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
315
595
  // data) in order to refer to it later, mimicking the way this object is used by the ContractFunctionSimulator. The
316
596
  // difference resides in that the simulator has all information needed in order to run the simulation, while ours
317
597
  // will be ongoing as the different oracles will be invoked from the Noir test, until eventually the private
318
598
  // execution finishes.
319
- this.state = { name: 'PRIVATE', nextBlockGlobalVariables, txRequestHash, noteCache, taggingIndexCache };
599
+ this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
320
600
  this.logger.debug(`Entered state ${this.state.name}`);
321
601
 
322
- 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();
323
607
  }
324
608
 
325
- async enterPublicState(contractAddress?: AztecAddress) {
609
+ async enterPublicState(contractAddressOpt: Option<AztecAddress>) {
610
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
326
611
  this.exitTopLevelState();
612
+ this.resetLastCall();
327
613
 
328
614
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
329
615
  // the test. The block therefore gets the *next* block number and timestamp.
330
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
616
+ const latestHeader = (await this.stateMachine.node.getBlockData('latest'))!.header;
331
617
  const globalVariables = makeGlobalVariables(undefined, {
332
- blockNumber: latestBlockNumber + 1,
618
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
333
619
  timestamp: this.nextBlockTimestamp,
334
620
  version: this.version,
335
621
  chainId: this.chainId,
336
622
  });
337
623
 
338
624
  this.oracleHandler = new TXEOraclePublicContext(
339
- contractAddress ?? DEFAULT_ADDRESS,
625
+ contractAddress,
340
626
  await this.stateMachine.synchronizer.nativeWorldStateService.fork(),
341
627
  getSingleTxBlockRequestHash(globalVariables.blockNumber),
342
628
  globalVariables,
629
+ this.contractStore,
343
630
  );
344
631
 
345
632
  this.state = { name: 'PUBLIC' };
346
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);
347
637
  }
348
638
 
349
- async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
639
+ async enterUtilityState(contractAddressOpt: Option<AztecAddress>) {
640
+ const contractAddress = contractAddressOpt?.value ?? DEFAULT_ADDRESS;
350
641
  this.exitTopLevelState();
642
+ this.resetLastCall();
643
+
644
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
351
645
 
352
646
  // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
353
647
  // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
354
648
  // we perform this. We therefore search for known nullifiers now, as otherwise notes that were nullified would not
355
649
  // be removed from the database.
356
650
  // TODO(#12553): make the synchronizer sync here instead and remove this
357
- await this.pxeOracleInterface.syncNoteNullifiers(contractAddress);
358
-
359
- this.oracleHandler = new UtilityExecutionOracle(contractAddress, [], [], this.pxeOracleInterface);
651
+ await new NoteService(
652
+ this.noteStore,
653
+ this.stateMachine.node,
654
+ anchorBlockHeader,
655
+ this.currentJobId,
656
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
657
+
658
+ this.oracleHandler = new UtilityExecutionOracle({
659
+ contractAddress,
660
+ authWitnesses: [],
661
+ capsules: [],
662
+ anchorBlockHeader,
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
+ });
360
680
 
361
681
  this.state = { name: 'UTILITY' };
362
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);
363
686
  }
364
687
 
365
688
  private exitTopLevelState() {
@@ -369,8 +692,8 @@ export class TXESession implements TXESessionStateHandler {
369
692
 
370
693
  // Note that while all public and private contexts do is build a single block that we then process when exiting
371
694
  // those, the top level context performs a large number of actions not captured in the following 'close' call. Among
372
- // others, it will create empty blocks (via `txeAdvanceBlocksBy` and `deploy`), create blocks with transactions via
373
- // `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
374
697
  // slight inconsistency in the working model of this class, but is not too bad.
375
698
  // TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
376
699
  // level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
@@ -390,21 +713,14 @@ export class TXESession implements TXESessionStateHandler {
390
713
 
391
714
  // We rely on the note cache to determine the effects of the transaction. This is incomplete as it doesn't private
392
715
  // logs (other effects like enqueued public calls don't need to be considered since those are not allowed).
393
- const txEffect = await makeTxEffect(
394
- this.state.noteCache,
395
- this.state.txRequestHash,
396
- this.state.nextBlockGlobalVariables.blockNumber,
397
- );
716
+
717
+ const txEffect = await makeTxEffect(this.state.noteCache, this.state.nextBlockGlobalVariables.blockNumber);
398
718
 
399
719
  // We build a block holding just this transaction
400
720
  const forkedWorldTrees = await this.stateMachine.synchronizer.nativeWorldStateService.fork();
401
721
  await insertTxEffectIntoWorldTrees(txEffect, forkedWorldTrees);
402
722
 
403
- const block = new L2Block(
404
- makeAppendOnlyTreeSnapshot(),
405
- await makeTXEBlockHeader(forkedWorldTrees, this.state.nextBlockGlobalVariables),
406
- new Body([txEffect]),
407
- );
723
+ const block = await makeTXEBlock(forkedWorldTrees, this.state.nextBlockGlobalVariables, [txEffect]);
408
724
  await this.stateMachine.handleL2Block(block);
409
725
 
410
726
  await forkedWorldTrees.close();
@@ -429,4 +745,55 @@ export class TXESession implements TXESessionStateHandler {
429
745
  throw new Error(`Expected to be in state 'UTILITY', but got '${this.state.name}' instead`);
430
746
  }
431
747
  }
748
+
749
+ private utilityExecutorForContractSync(anchorBlock: any) {
750
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
751
+ const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
752
+ if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
753
+ throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
754
+ }
755
+
756
+ try {
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
781
+ .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
782
+ .catch((err: Error) => {
783
+ err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
784
+ throw new ExecutionError(
785
+ err.message,
786
+ {
787
+ contractAddress: call.to,
788
+ functionSelector: call.selector,
789
+ },
790
+ extractCallStack(err, entryPointArtifact.debug),
791
+ { cause: err },
792
+ );
793
+ });
794
+ } catch (err) {
795
+ throw createSimulationError(err instanceof Error ? err : new Error('Unknown error contract data sync'));
796
+ }
797
+ };
798
+ }
432
799
  }