@aztec/txe 0.0.1-commit.fffb133c → 0.0.1-dev

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 (62) hide show
  1. package/dest/index.d.ts +1 -1
  2. package/dest/index.d.ts.map +1 -1
  3. package/dest/index.js +88 -54
  4. package/dest/oracle/interfaces.d.ts +35 -28
  5. package/dest/oracle/interfaces.d.ts.map +1 -1
  6. package/dest/oracle/txe_oracle_public_context.d.ts +13 -13
  7. package/dest/oracle/txe_oracle_public_context.d.ts.map +1 -1
  8. package/dest/oracle/txe_oracle_public_context.js +14 -15
  9. package/dest/oracle/txe_oracle_top_level_context.d.ts +33 -23
  10. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  11. package/dest/oracle/txe_oracle_top_level_context.js +185 -71
  12. package/dest/rpc_translator.d.ts +135 -83
  13. package/dest/rpc_translator.d.ts.map +1 -1
  14. package/dest/rpc_translator.js +540 -178
  15. package/dest/state_machine/archiver.d.ts +3 -3
  16. package/dest/state_machine/archiver.d.ts.map +1 -1
  17. package/dest/state_machine/archiver.js +7 -7
  18. package/dest/state_machine/dummy_p2p_client.d.ts +16 -12
  19. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  20. package/dest/state_machine/dummy_p2p_client.js +28 -16
  21. package/dest/state_machine/index.d.ts +7 -5
  22. package/dest/state_machine/index.d.ts.map +1 -1
  23. package/dest/state_machine/index.js +19 -10
  24. package/dest/state_machine/mock_epoch_cache.d.ts +3 -1
  25. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  26. package/dest/state_machine/mock_epoch_cache.js +4 -0
  27. package/dest/state_machine/synchronizer.d.ts +5 -5
  28. package/dest/state_machine/synchronizer.d.ts.map +1 -1
  29. package/dest/state_machine/synchronizer.js +3 -3
  30. package/dest/txe_oracle_version.d.ts +10 -0
  31. package/dest/txe_oracle_version.d.ts.map +1 -0
  32. package/dest/txe_oracle_version.js +8 -0
  33. package/dest/txe_session.d.ts +67 -8
  34. package/dest/txe_session.d.ts.map +1 -1
  35. package/dest/txe_session.js +175 -32
  36. package/dest/util/encoding.d.ts +43 -41
  37. package/dest/util/encoding.d.ts.map +1 -1
  38. package/dest/util/txe_public_contract_data_source.d.ts +2 -3
  39. package/dest/util/txe_public_contract_data_source.d.ts.map +1 -1
  40. package/dest/util/txe_public_contract_data_source.js +5 -22
  41. package/dest/utils/block_creation.d.ts +1 -1
  42. package/dest/utils/block_creation.d.ts.map +1 -1
  43. package/dest/utils/block_creation.js +3 -1
  44. package/package.json +15 -15
  45. package/src/index.ts +89 -52
  46. package/src/oracle/interfaces.ts +43 -34
  47. package/src/oracle/txe_oracle_public_context.ts +14 -16
  48. package/src/oracle/txe_oracle_top_level_context.ts +231 -132
  49. package/src/rpc_translator.ts +673 -208
  50. package/src/state_machine/archiver.ts +7 -5
  51. package/src/state_machine/dummy_p2p_client.ts +40 -22
  52. package/src/state_machine/index.ts +30 -10
  53. package/src/state_machine/mock_epoch_cache.ts +5 -0
  54. package/src/state_machine/synchronizer.ts +4 -4
  55. package/src/txe_oracle_version.ts +9 -0
  56. package/src/txe_session.ts +270 -93
  57. package/src/util/txe_public_contract_data_source.ts +10 -36
  58. package/src/utils/block_creation.ts +3 -1
  59. package/dest/util/txe_contract_store.d.ts +0 -12
  60. package/dest/util/txe_contract_store.d.ts.map +0 -1
  61. package/dest/util/txe_contract_store.js +0 -22
  62. package/src/util/txe_contract_store.ts +0 -36
@@ -3,10 +3,12 @@ import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { KeyStore } from '@aztec/key-store';
5
5
  import { openTmpStore } from '@aztec/kv-store/lmdb-v2';
6
- import type { ProtocolContract } from '@aztec/protocol-contracts';
7
6
  import {
8
7
  AddressStore,
8
+ AnchorBlockStore,
9
+ CapsuleService,
9
10
  CapsuleStore,
11
+ ContractStore,
10
12
  JobCoordinator,
11
13
  NoteService,
12
14
  NoteStore,
@@ -36,11 +38,11 @@ import {
36
38
  import { FunctionCall, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
37
39
  import type { AuthWitness } from '@aztec/stdlib/auth-witness';
38
40
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
39
- import { GasSettings } from '@aztec/stdlib/gas';
41
+ import type { GasSettings } from '@aztec/stdlib/gas';
40
42
  import { computeProtocolNullifier } from '@aztec/stdlib/hash';
41
43
  import { PrivateContextInputs } from '@aztec/stdlib/kernel';
42
44
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
43
- import { CallContext, GlobalVariables, TxContext } from '@aztec/stdlib/tx';
45
+ import { CallContext, GlobalVariables, OFFCHAIN_MESSAGE_IDENTIFIER, TxContext } from '@aztec/stdlib/tx';
44
46
 
45
47
  import { z } from 'zod';
46
48
 
@@ -49,10 +51,11 @@ import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfac
49
51
  import { TXEOraclePublicContext } from './oracle/txe_oracle_public_context.js';
50
52
  import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.js';
51
53
  import { RPCTranslator } from './rpc_translator.js';
54
+ import { TXEArchiver } from './state_machine/archiver.js';
52
55
  import { TXEStateMachine } from './state_machine/index.js';
56
+ import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './txe_oracle_version.js';
53
57
  import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
54
58
  import { TXEAccountStore } from './util/txe_account_store.js';
55
- import { TXEContractStore } from './util/txe_contract_store.js';
56
59
  import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
57
60
  import { makeTxEffect } from './utils/tx_effect_creation.js';
58
61
 
@@ -107,10 +110,80 @@ export type TXEOracleFunctionName = Exclude<
107
110
  >;
108
111
 
109
112
  export interface TXESessionStateHandler {
113
+ /** Records the TXE oracle version reported by the Noir test code for diagnostics. */
114
+ setTxeOracleVersion(version: { major: number; minor: number }): void;
115
+
110
116
  enterTopLevelState(): Promise<void>;
111
117
  enterPublicState(contractAddress?: AztecAddress): Promise<void>;
112
- enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
118
+ enterPrivateState(
119
+ contractAddress: AztecAddress | undefined,
120
+ anchorBlockNumber: BlockNumber | undefined,
121
+ gasSettings: GasSettings,
122
+ ): Promise<PrivateContextInputs>;
113
123
  enterUtilityState(contractAddress?: AztecAddress): Promise<void>;
124
+
125
+ // TODO(F-335): Exposing the job info is abstraction breakage - drop the following 2 functions.
126
+ cycleJob(): Promise<string>;
127
+ getCurrentJob(): string;
128
+
129
+ /**
130
+ * Runs an executor-style top-level call (private/public call, utility execution) with last-call tracking.
131
+ */
132
+ withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T>;
133
+
134
+ /**
135
+ * Captures a raw offchain effect payload for consumption from test environment. Called by the `emit_offchain_effect`
136
+ * oracle handler whenever a contract function emits an offchain message, at any call depth.
137
+ */
138
+ recordOffchainEffect(data: Fr[]): void;
139
+
140
+ /**
141
+ * Returns the raw offchain effect payloads emitted by the last top-level call. Each payload follows the protocol
142
+ * convention documented on `OFFCHAIN_MESSAGE_IDENTIFIER`, i.e. `[identifier, recipient, ...ciphertext]`. Decoding into
143
+ * `OffchainMessage` structs happens on the Noir side of the test helper. Marks the buffer as queried so the
144
+ * unqueried-messages warning doesn't fire on the next reset.
145
+ */
146
+ getLastCallOffchainEffects(): { effects: Fr[][] };
147
+
148
+ /**
149
+ * Returns the context of the last top-level call: its tx hash (`Fr.ZERO` if the call was tx-less) and the anchor
150
+ * block timestamp captured at the start of the call. Does *not* mark the buffer as queried — context reads are
151
+ * metadata, not effect consumption.
152
+ */
153
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint };
154
+ }
155
+
156
+ /**
157
+ * Session state tracking the most recently completed top-level call: the offchain effect buffer it produced, and the
158
+ * call's context (tx hash + anchor block timestamp). The context is refreshed on every top-level call, independently
159
+ * of whether the call produced offchain effects.
160
+ */
161
+ interface LastCallState {
162
+ /**
163
+ * Raw offchain effect payloads emitted by the currently-executing (or most recently completed) top-level call. Wiped
164
+ * at the start of every top-level entry point, appended to on every `emit_offchain_effect` oracle invocation.
165
+ */
166
+ offchainEffects: Fr[][];
167
+ /**
168
+ * Tracks whether the test has queried `effects` since the last reset. If a new top-level call clobbers the buffer
169
+ * without it being queried first, any accumulated messages are lost and we emit a warning so tests don't silently
170
+ * drop delivery.
171
+ */
172
+ queried: boolean;
173
+ /**
174
+ * Tx hash of the most recently completed top-level call, or `Fr.ZERO` if the call was tx-less (context setters,
175
+ * utility execution). Populated by call executor handlers after execution completes.
176
+ */
177
+ txHash: Fr;
178
+ /**
179
+ * Anchor block timestamp of the most recently completed top-level call, captured from the anchor block header that
180
+ * was active when the call started. Populated by call executor handlers after execution completes.
181
+ */
182
+ anchorBlockTimestamp: bigint;
183
+ }
184
+
185
+ function emptyLastCallState(): LastCallState {
186
+ return { offchainEffects: [], queried: false, txHash: Fr.ZERO, anchorBlockTimestamp: 0n };
114
187
  }
115
188
 
116
189
  /**
@@ -120,6 +193,8 @@ export interface TXESessionStateHandler {
120
193
  export class TXESession implements TXESessionStateHandler {
121
194
  private state: SessionState = { name: 'TOP_LEVEL' };
122
195
  private authwits: Map<string, AuthWitness> = new Map();
196
+ private lastCallInfo: LastCallState = emptyLastCallState();
197
+ private txeOracleVersion: { major: number; minor: number } | undefined;
123
198
 
124
199
  constructor(
125
200
  private logger: Logger,
@@ -129,7 +204,7 @@ export class TXESession implements TXESessionStateHandler {
129
204
  | IPrivateExecutionOracle
130
205
  | IAvmExecutionOracle
131
206
  | ITxeExecutionOracle,
132
- private contractStore: TXEContractStore,
207
+ private contractStore: ContractStore,
133
208
  private noteStore: NoteStore,
134
209
  private keyStore: KeyStore,
135
210
  private addressStore: AddressStore,
@@ -146,12 +221,11 @@ export class TXESession implements TXESessionStateHandler {
146
221
  private nextBlockTimestamp: bigint,
147
222
  ) {}
148
223
 
149
- static async init(protocolContracts: ProtocolContract[]) {
224
+ static async init(contractStore: ContractStore) {
150
225
  const store = await openTmpStore('txe-session');
151
226
 
152
227
  const addressStore = new AddressStore(store);
153
228
  const privateEventStore = new PrivateEventStore(store);
154
- const contractStore = new TXEContractStore(store);
155
229
  const noteStore = new NoteStore(store);
156
230
  const senderTaggingStore = new SenderTaggingStore(store);
157
231
  const recipientTaggingStore = new RecipientTaggingStore(store);
@@ -170,13 +244,9 @@ export class TXESession implements TXESessionStateHandler {
170
244
  noteStore,
171
245
  ]);
172
246
 
173
- // Register protocol contracts.
174
- for (const { contractClass, instance, artifact } of protocolContracts) {
175
- await contractStore.addContractArtifact(contractClass.id, artifact);
176
- await contractStore.addContractInstance(instance);
177
- }
178
-
179
- const stateMachine = await TXEStateMachine.create(store);
247
+ const archiver = new TXEArchiver(store);
248
+ const anchorBlockStore = new AnchorBlockStore(store);
249
+ const stateMachine = await TXEStateMachine.create(archiver, anchorBlockStore, contractStore, noteStore);
180
250
 
181
251
  const nextBlockTimestamp = BigInt(Math.floor(new Date().getTime() / 1000));
182
252
  const version = new Fr(await stateMachine.node.getVersion());
@@ -184,6 +254,8 @@ export class TXESession implements TXESessionStateHandler {
184
254
 
185
255
  const initialJobId = jobCoordinator.beginJob();
186
256
 
257
+ const logger = createLogger('txe:session');
258
+
187
259
  const topLevelOracleHandler = new TXEOracleTopLevelContext(
188
260
  stateMachine,
189
261
  contractStore,
@@ -196,16 +268,15 @@ export class TXESession implements TXESessionStateHandler {
196
268
  senderAddressBookStore,
197
269
  capsuleStore,
198
270
  privateEventStore,
199
- initialJobId,
200
271
  nextBlockTimestamp,
201
272
  version,
202
273
  chainId,
203
274
  new Map(),
204
275
  );
205
- await topLevelOracleHandler.txeAdvanceBlocksBy(1);
276
+ await topLevelOracleHandler.advanceBlocksBy(1);
206
277
 
207
278
  return new TXESession(
208
- createLogger('txe:session'),
279
+ logger,
209
280
  stateMachine,
210
281
  topLevelOracleHandler,
211
282
  contractStore,
@@ -244,7 +315,28 @@ export class TXESession implements TXESessionStateHandler {
244
315
  return translator[validatedFunctionName](...inputs);
245
316
  } catch (error) {
246
317
  if (error instanceof z.ZodError) {
247
- throw new Error(`${functionName} does not correspond to any oracle handler available on RPCTranslator`);
318
+ let versionHint: string;
319
+ if (!this.txeOracleVersion) {
320
+ versionHint =
321
+ ' The test appears to use an older version of Aztec.nr that does not' +
322
+ ' support test environment oracle versioning. Update Aztec.nr to a compatible version.' +
323
+ ' See https://docs.aztec.network/errors/12';
324
+ } else if (this.txeOracleVersion.minor > TXE_ORACLE_VERSION_MINOR) {
325
+ versionHint =
326
+ ` The test uses Aztec.nr test oracle version` +
327
+ ` ${this.txeOracleVersion.major}.${this.txeOracleVersion.minor}, but this test environment` +
328
+ ` only supports up to ${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}.` +
329
+ ` Upgrade the Aztec CLI to a compatible version.` +
330
+ ` See https://docs.aztec.network/errors/12`;
331
+ } else {
332
+ versionHint =
333
+ ` The test's oracle version (${this.txeOracleVersion.major}.${this.txeOracleVersion.minor})` +
334
+ ` is compatible with this test environment` +
335
+ ` (${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}), so this oracle should be` +
336
+ ` available. This is an unexpected error, please report it.` +
337
+ ` See https://docs.aztec.network/errors/13`;
338
+ }
339
+ throw new Error(`Unknown oracle '${functionName}'.${versionHint}`);
248
340
  } else if (error instanceof Error) {
249
341
  throw new Error(
250
342
  `Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
@@ -257,6 +349,66 @@ export class TXESession implements TXESessionStateHandler {
257
349
  }
258
350
  }
259
351
 
352
+ getCurrentJob(): string {
353
+ return this.currentJobId;
354
+ }
355
+
356
+ /** Commits the current job and begins a new one. Returns the new job ID. */
357
+ async cycleJob(): Promise<string> {
358
+ await this.jobCoordinator.commitJob(this.currentJobId);
359
+ this.currentJobId = this.jobCoordinator.beginJob();
360
+ return this.currentJobId;
361
+ }
362
+
363
+ private resetLastCall(): void {
364
+ const notQueriedMessageCount = this.lastCallInfo.queried
365
+ ? 0
366
+ : this.lastCallInfo.offchainEffects.filter(payload => payload[0]?.equals(OFFCHAIN_MESSAGE_IDENTIFIER)).length;
367
+ if (notQueriedMessageCount > 0) {
368
+ this.logger.warn(
369
+ `Dropping ${notQueriedMessageCount} unqueried offchain message(s) from the previous top-level call. ` +
370
+ `To deliver them, call \`env.offchain_messages()\` and forward the result to the recipient contract's ` +
371
+ `\`offchain_receive\` utility before issuing another top-level call. To intentionally discard, assign ` +
372
+ `to \`let _ = env.offchain_messages()\` to silence this warning.`,
373
+ );
374
+ }
375
+ this.lastCallInfo = emptyLastCallState();
376
+ }
377
+
378
+ recordOffchainEffect(data: Fr[]): void {
379
+ this.lastCallInfo.offchainEffects.push(data);
380
+ }
381
+
382
+ private setLastCallContext(txHash: Fr, anchorBlockTimestamp: bigint): void {
383
+ this.lastCallInfo.txHash = txHash;
384
+ this.lastCallInfo.anchorBlockTimestamp = anchorBlockTimestamp;
385
+ }
386
+
387
+ async withTopLevelCallTracking<T>(work: () => Promise<{ result: T; txHash?: Fr }>): Promise<T> {
388
+ this.resetLastCall();
389
+ // Capture the anchor *before* `work` runs: private/public executor calls mine a new block as a
390
+ // side effect, and that block's timestamp should not be attributed to this call's anchor.
391
+ const anchorBlockTimestamp = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.timestamp;
392
+ const { result, txHash } = await work();
393
+ this.setLastCallContext(txHash ?? Fr.ZERO, anchorBlockTimestamp);
394
+ return result;
395
+ }
396
+
397
+ getLastCallOffchainEffects(): { effects: Fr[][] } {
398
+ this.lastCallInfo.queried = true;
399
+ return { effects: this.lastCallInfo.offchainEffects };
400
+ }
401
+
402
+ getLastCallContext(): { txHash: Fr; anchorBlockTimestamp: bigint } {
403
+ const { txHash, anchorBlockTimestamp } = this.lastCallInfo;
404
+ return { txHash, anchorBlockTimestamp };
405
+ }
406
+
407
+ setTxeOracleVersion(version: { major: number; minor: number }): void {
408
+ this.txeOracleVersion = version;
409
+ this.logger.debug(`Test compiled with test oracle version ${version.major}.${version.minor}`);
410
+ }
411
+
260
412
  async enterTopLevelState() {
261
413
  switch (this.state.name) {
262
414
  case 'PRIVATE': {
@@ -280,8 +432,7 @@ export class TXESession implements TXESessionStateHandler {
280
432
  }
281
433
 
282
434
  // Commit all staged stores from the job that was just completed, then begin a new job
283
- await this.jobCoordinator.commitJob(this.currentJobId);
284
- this.currentJobId = this.jobCoordinator.beginJob();
435
+ await this.cycleJob();
285
436
 
286
437
  this.oracleHandler = new TXEOracleTopLevelContext(
287
438
  this.stateMachine,
@@ -295,7 +446,6 @@ export class TXESession implements TXESessionStateHandler {
295
446
  this.senderAddressBookStore,
296
447
  this.capsuleStore,
297
448
  this.privateEventStore,
298
- this.currentJobId,
299
449
  this.nextBlockTimestamp,
300
450
  this.version,
301
451
  this.chainId,
@@ -308,21 +458,21 @@ export class TXESession implements TXESessionStateHandler {
308
458
 
309
459
  async enterPrivateState(
310
460
  contractAddress: AztecAddress = DEFAULT_ADDRESS,
311
- anchorBlockNumber?: BlockNumber,
461
+ anchorBlockNumber: BlockNumber | undefined,
462
+ gasSettings: GasSettings,
312
463
  ): Promise<PrivateContextInputs> {
313
464
  this.exitTopLevelState();
314
-
315
- await new NoteService(
316
- this.noteStore,
317
- this.stateMachine.node,
318
- this.stateMachine.anchorBlockStore,
319
- this.currentJobId,
320
- ).syncNoteNullifiers(contractAddress);
465
+ this.resetLastCall();
321
466
 
322
467
  // Private execution has two associated block numbers: the anchor block (i.e. the historical block that is used to
323
468
  // build the proof), and the *next* block, i.e. the one we'll create once the execution ends, and which will contain
324
469
  // a single transaction with the effects of what was done in the test.
325
470
  const anchorBlock = await this.stateMachine.node.getBlockHeader(anchorBlockNumber ?? 'latest');
471
+
472
+ await new NoteService(this.noteStore, this.stateMachine.node, anchorBlock!, this.currentJobId).syncNoteNullifiers(
473
+ contractAddress,
474
+ await this.keyStore.getAccounts(),
475
+ );
326
476
  const latestBlock = await this.stateMachine.node.getBlockHeader('latest');
327
477
 
328
478
  const nextBlockGlobalVariables = makeGlobalVariables(undefined, {
@@ -338,30 +488,34 @@ export class TXESession implements TXESessionStateHandler {
338
488
  const taggingIndexCache = new ExecutionTaggingIndexCache();
339
489
 
340
490
  const utilityExecutor = this.utilityExecutorForContractSync(anchorBlock);
341
- this.oracleHandler = new PrivateExecutionOracle(
342
- Fr.ZERO,
343
- new TxContext(this.chainId, this.version, GasSettings.empty()),
344
- new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
345
- anchorBlock!,
491
+ this.oracleHandler = new PrivateExecutionOracle({
492
+ argsHash: Fr.ZERO,
493
+ txContext: new TxContext(this.chainId, this.version, gasSettings),
494
+ callContext: new CallContext(AztecAddress.ZERO, contractAddress, FunctionSelector.empty(), false),
495
+ anchorBlockHeader: anchorBlock!,
346
496
  utilityExecutor,
347
- [],
348
- [],
349
- new HashedValuesCache(),
497
+ authWitnesses: [],
498
+ capsules: [],
499
+ executionCache: new HashedValuesCache(),
350
500
  noteCache,
351
501
  taggingIndexCache,
352
- this.contractStore,
353
- this.noteStore,
354
- this.keyStore,
355
- this.addressStore,
356
- this.stateMachine.node,
357
- this.stateMachine.anchorBlockStore,
358
- this.senderTaggingStore,
359
- this.recipientTaggingStore,
360
- this.senderAddressBookStore,
361
- this.capsuleStore,
362
- this.privateEventStore,
363
- this.currentJobId,
364
- );
502
+ contractStore: this.contractStore,
503
+ noteStore: this.noteStore,
504
+ keyStore: this.keyStore,
505
+ addressStore: this.addressStore,
506
+ aztecNode: this.stateMachine.node,
507
+ senderTaggingStore: this.senderTaggingStore,
508
+ recipientTaggingStore: this.recipientTaggingStore,
509
+ senderAddressBookStore: this.senderAddressBookStore,
510
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
511
+ privateEventStore: this.privateEventStore,
512
+ contractSyncService: this.stateMachine.contractSyncService,
513
+ l2TipsStore: this.stateMachine.node,
514
+ jobId: this.currentJobId,
515
+ scopes: await this.keyStore.getAccounts(),
516
+ messageContextService: this.stateMachine.messageContextService,
517
+ simulator: new WASMSimulator(),
518
+ });
365
519
 
366
520
  // We store the note and tagging index caches fed into the PrivateExecutionOracle (along with some other auxiliary
367
521
  // data) in order to refer to it later, mimicking the way this object is used by the ContractFunctionSimulator. The
@@ -371,17 +525,22 @@ export class TXESession implements TXESessionStateHandler {
371
525
  this.state = { name: 'PRIVATE', nextBlockGlobalVariables, noteCache, taggingIndexCache };
372
526
  this.logger.debug(`Entered state ${this.state.name}`);
373
527
 
528
+ // Record the *resolved* anchor's timestamp — if the caller pinned the anchor to a past block
529
+ // via `anchorBlockNumber`, "latest" would be the wrong anchor for offchain-message semantics.
530
+ this.setLastCallContext(Fr.ZERO, anchorBlock!.globalVariables.timestamp);
531
+
374
532
  return (this.oracleHandler as PrivateExecutionOracle).getPrivateContextInputs();
375
533
  }
376
534
 
377
535
  async enterPublicState(contractAddress?: AztecAddress) {
378
536
  this.exitTopLevelState();
537
+ this.resetLastCall();
379
538
 
380
539
  // The PublicContext will create a block with a single transaction in it, containing the effects of what was done in
381
540
  // the test. The block therefore gets the *next* block number and timestamp.
382
- const latestBlockNumber = (await this.stateMachine.node.getBlockHeader('latest'))!.globalVariables.blockNumber;
541
+ const latestHeader = (await this.stateMachine.node.getBlockHeader('latest'))!;
383
542
  const globalVariables = makeGlobalVariables(undefined, {
384
- blockNumber: BlockNumber(latestBlockNumber + 1),
543
+ blockNumber: BlockNumber(latestHeader.globalVariables.blockNumber + 1),
385
544
  timestamp: this.nextBlockTimestamp,
386
545
  version: this.version,
387
546
  chainId: this.chainId,
@@ -396,10 +555,16 @@ export class TXESession implements TXESessionStateHandler {
396
555
 
397
556
  this.state = { name: 'PUBLIC' };
398
557
  this.logger.debug(`Entered state ${this.state.name}`);
558
+
559
+ // Public state is anchored at the latest block.
560
+ this.setLastCallContext(Fr.ZERO, latestHeader.globalVariables.timestamp);
399
561
  }
400
562
 
401
563
  async enterUtilityState(contractAddress: AztecAddress = DEFAULT_ADDRESS) {
402
564
  this.exitTopLevelState();
565
+ this.resetLastCall();
566
+
567
+ const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
403
568
 
404
569
  // There is no automatic message discovery and contract-driven syncing process in inlined private or utility
405
570
  // contexts, which means that known nullifiers are also not searched for, since it is during the tagging sync that
@@ -409,32 +574,38 @@ export class TXESession implements TXESessionStateHandler {
409
574
  await new NoteService(
410
575
  this.noteStore,
411
576
  this.stateMachine.node,
412
- this.stateMachine.anchorBlockStore,
577
+ anchorBlockHeader,
413
578
  this.currentJobId,
414
- ).syncNoteNullifiers(contractAddress);
415
-
416
- const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
579
+ ).syncNoteNullifiers(contractAddress, await this.keyStore.getAccounts());
417
580
 
418
- this.oracleHandler = new UtilityExecutionOracle(
581
+ this.oracleHandler = new UtilityExecutionOracle({
419
582
  contractAddress,
420
- [],
421
- [],
583
+ authWitnesses: [],
584
+ capsules: [],
422
585
  anchorBlockHeader,
423
- this.contractStore,
424
- this.noteStore,
425
- this.keyStore,
426
- this.addressStore,
427
- this.stateMachine.node,
428
- this.stateMachine.anchorBlockStore,
429
- this.recipientTaggingStore,
430
- this.senderAddressBookStore,
431
- this.capsuleStore,
432
- this.privateEventStore,
433
- this.currentJobId,
434
- );
586
+ contractStore: this.contractStore,
587
+ noteStore: this.noteStore,
588
+ keyStore: this.keyStore,
589
+ addressStore: this.addressStore,
590
+ aztecNode: this.stateMachine.node,
591
+ recipientTaggingStore: this.recipientTaggingStore,
592
+ senderAddressBookStore: this.senderAddressBookStore,
593
+ capsuleService: new CapsuleService(this.capsuleStore, await this.keyStore.getAccounts()),
594
+ privateEventStore: this.privateEventStore,
595
+ messageContextService: this.stateMachine.messageContextService,
596
+ contractSyncService: this.stateMachine.contractSyncService,
597
+ l2TipsStore: this.stateMachine.node,
598
+ jobId: this.currentJobId,
599
+ scopes: await this.keyStore.getAccounts(),
600
+ simulator: new WASMSimulator(),
601
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlockHeader),
602
+ });
435
603
 
436
604
  this.state = { name: 'UTILITY' };
437
605
  this.logger.debug(`Entered state ${this.state.name}`);
606
+
607
+ // Utility state anchors at whatever the anchor block store is pointing to (tracked as latest).
608
+ this.setLastCallContext(Fr.ZERO, anchorBlockHeader.globalVariables.timestamp);
438
609
  }
439
610
 
440
611
  private exitTopLevelState() {
@@ -444,8 +615,8 @@ export class TXESession implements TXESessionStateHandler {
444
615
 
445
616
  // Note that while all public and private contexts do is build a single block that we then process when exiting
446
617
  // 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
618
+ // others, it will create empty blocks (via `advanceBlocksBy` and `deploy`), create blocks with transactions via
619
+ // `privateCallNewFlow` and `publicCallNewFlow`, add accounts to PXE via `addAccount`, etc. This is a
449
620
  // slight inconsistency in the working model of this class, but is not too bad.
450
621
  // TODO: it's quite unfortunate that we need to capture the authwits created to later pass them again when the top
451
622
  // level context is re-created. This is because authwits create a temporary utility context that'd otherwise reset
@@ -499,31 +670,37 @@ export class TXESession implements TXESessionStateHandler {
499
670
  }
500
671
 
501
672
  private utilityExecutorForContractSync(anchorBlock: any) {
502
- return async (call: FunctionCall) => {
673
+ return async (call: FunctionCall, scopes: AztecAddress[]) => {
503
674
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
504
675
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
505
676
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
506
677
  }
507
678
 
508
679
  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.stateMachine.anchorBlockStore,
520
- this.recipientTaggingStore,
521
- this.senderAddressBookStore,
522
- this.capsuleStore,
523
- this.privateEventStore,
524
- this.currentJobId,
525
- );
526
- await new WASMSimulator()
680
+ const simulator = new WASMSimulator();
681
+ const oracle = new UtilityExecutionOracle({
682
+ contractAddress: call.to,
683
+ authWitnesses: [],
684
+ capsules: [],
685
+ anchorBlockHeader: anchorBlock!,
686
+ contractStore: this.contractStore,
687
+ noteStore: this.noteStore,
688
+ keyStore: this.keyStore,
689
+ addressStore: this.addressStore,
690
+ aztecNode: this.stateMachine.node,
691
+ recipientTaggingStore: this.recipientTaggingStore,
692
+ senderAddressBookStore: this.senderAddressBookStore,
693
+ capsuleService: new CapsuleService(this.capsuleStore, scopes),
694
+ privateEventStore: this.privateEventStore,
695
+ messageContextService: this.stateMachine.messageContextService,
696
+ contractSyncService: this.stateMachine.contractSyncService,
697
+ l2TipsStore: this.stateMachine.node,
698
+ jobId: this.currentJobId,
699
+ scopes,
700
+ simulator,
701
+ utilityExecutor: this.utilityExecutorForContractSync(anchorBlock),
702
+ });
703
+ await simulator
527
704
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
528
705
  .catch((err: Error) => {
529
706
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -1,19 +1,11 @@
1
1
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { Fr } from '@aztec/foundation/curves/bn254';
3
3
  import type { ContractStore } from '@aztec/pxe/server';
4
- import { type ContractArtifact, FunctionSelector, FunctionType } from '@aztec/stdlib/abi';
4
+ import { type ContractArtifact, FunctionSelector } from '@aztec/stdlib/abi';
5
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
6
- import {
7
- type ContractClassPublic,
8
- type ContractDataSource,
9
- type ContractInstanceWithAddress,
10
- computePrivateFunctionsRoot,
11
- computePublicBytecodeCommitment,
12
- getContractClassPrivateFunctionFromArtifact,
13
- } from '@aztec/stdlib/contract';
6
+ import type { ContractClassPublic, ContractDataSource, ContractInstanceWithAddress } from '@aztec/stdlib/contract';
14
7
 
15
8
  export class TXEPublicContractDataSource implements ContractDataSource {
16
- #privateFunctionsRoot: Map<string, Buffer> = new Map();
17
9
  constructor(
18
10
  private blockNumber: BlockNumber,
19
11
  private contractStore: ContractStore,
@@ -24,42 +16,24 @@ export class TXEPublicContractDataSource implements ContractDataSource {
24
16
  }
25
17
 
26
18
  async getContractClass(id: Fr): Promise<ContractClassPublic | undefined> {
27
- const contractClass = await this.contractStore.getContractClass(id);
19
+ const contractClass = await this.contractStore.getContractClassWithPreimage(id);
28
20
  if (!contractClass) {
29
21
  return;
30
22
  }
31
- const artifact = await this.contractStore.getContractArtifact(id);
32
- if (!artifact) {
33
- return;
34
- }
35
-
36
- let privateFunctionsRoot;
37
- if (!this.#privateFunctionsRoot.has(id.toString())) {
38
- const privateFunctions = await Promise.all(
39
- artifact.functions
40
- .filter(fn => fn.functionType === FunctionType.PRIVATE)
41
- .map(fn => getContractClassPrivateFunctionFromArtifact(fn)),
42
- );
43
- privateFunctionsRoot = await computePrivateFunctionsRoot(privateFunctions);
44
- this.#privateFunctionsRoot.set(id.toString(), privateFunctionsRoot.toBuffer());
45
- } else {
46
- privateFunctionsRoot = Fr.fromBuffer(this.#privateFunctionsRoot.get(id.toString())!);
47
- }
48
-
49
23
  return {
50
- id,
51
- artifactHash: contractClass!.artifactHash,
52
- packedBytecode: contractClass!.packedBytecode,
53
- privateFunctionsRoot,
54
- version: contractClass!.version,
24
+ id: contractClass.id,
25
+ artifactHash: contractClass.artifactHash,
26
+ packedBytecode: contractClass.packedBytecode,
27
+ privateFunctionsRoot: contractClass.privateFunctionsRoot,
28
+ version: contractClass.version,
55
29
  privateFunctions: [],
56
30
  utilityFunctions: [],
57
31
  };
58
32
  }
59
33
 
60
34
  async getBytecodeCommitment(id: Fr): Promise<Fr | undefined> {
61
- const contractClass = await this.contractStore.getContractClass(id);
62
- return contractClass && computePublicBytecodeCommitment(contractClass.packedBytecode);
35
+ const contractClass = await this.contractStore.getContractClassWithPreimage(id);
36
+ return contractClass?.publicBytecodeCommitment;
63
37
  }
64
38
 
65
39
  async getContract(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
@@ -87,7 +87,9 @@ export async function makeTXEBlock(
87
87
  const newArchiveInfo = await worldTrees.getTreeInfo(MerkleTreeId.ARCHIVE);
88
88
  const newArchive = new AppendOnlyTreeSnapshot(new Fr(newArchiveInfo.root), Number(newArchiveInfo.size));
89
89
 
90
- // L2Block requires checkpointNumber and indexWithinCheckpoint
90
+ // L2Block requires checkpointNumber and indexWithinCheckpoint.
91
+ // TXE uses 1-block-per-checkpoint for testing simplicity, so we can use block number as checkpoint number.
92
+ // This uses the deprecated fromBlockNumber method intentionally for the TXE testing environment.
91
93
  const checkpointNumber = CheckpointNumber.fromBlockNumber(globalVariables.blockNumber);
92
94
  const indexWithinCheckpoint = IndexWithinCheckpoint(0);
93
95
 
@@ -1,12 +0,0 @@
1
- import type { ContractArtifact } from '@aztec/aztec.js/abi';
2
- import { Fr } from '@aztec/aztec.js/fields';
3
- import { ContractStore } from '@aztec/pxe/server';
4
- export type ContractArtifactWithHash = ContractArtifact & {
5
- artifactHash: Fr;
6
- };
7
- export declare class TXEContractStore extends ContractStore {
8
- #private;
9
- addContractArtifact(id: Fr, artifact: ContractArtifact | ContractArtifactWithHash): Promise<void>;
10
- getContractArtifact(contractClassId: Fr): Promise<ContractArtifact | ContractArtifactWithHash | undefined>;
11
- }
12
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhlX2NvbnRyYWN0X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbC90eGVfY29udHJhY3Rfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsRUFBRSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDNUMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRWxELE1BQU0sTUFBTSx3QkFBd0IsR0FBRyxnQkFBZ0IsR0FBRztJQUFFLFlBQVksRUFBRSxFQUFFLENBQUE7Q0FBRSxDQUFDO0FBTy9FLHFCQUFhLGdCQUFpQixTQUFRLGFBQWE7O0lBRzNCLG1CQUFtQixDQUN2QyxFQUFFLEVBQUUsRUFBRSxFQUNOLFFBQVEsRUFBRSxnQkFBZ0IsR0FBRyx3QkFBd0IsR0FDcEQsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUtmO0lBRXFCLG1CQUFtQixDQUN2QyxlQUFlLEVBQUUsRUFBRSxHQUNsQixPQUFPLENBQUMsZ0JBQWdCLEdBQUcsd0JBQXdCLEdBQUcsU0FBUyxDQUFDLENBUWxFO0NBQ0YifQ==
@@ -1 +0,0 @@
1
- {"version":3,"file":"txe_contract_store.d.ts","sourceRoot":"","sources":["../../src/util/txe_contract_store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,MAAM,wBAAwB,GAAG,gBAAgB,GAAG;IAAE,YAAY,EAAE,EAAE,CAAA;CAAE,CAAC;AAO/E,qBAAa,gBAAiB,SAAQ,aAAa;;IAG3B,mBAAmB,CACvC,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,gBAAgB,GAAG,wBAAwB,GACpD,OAAO,CAAC,IAAI,CAAC,CAKf;IAEqB,mBAAmB,CACvC,eAAe,EAAE,EAAE,GAClB,OAAO,CAAC,gBAAgB,GAAG,wBAAwB,GAAG,SAAS,CAAC,CAQlE;CACF"}