@aztec/txe 0.0.1-commit.e588bc7e5 → 0.0.1-commit.e5a3663dd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dest/oracle/interfaces.d.ts +7 -2
  2. package/dest/oracle/interfaces.d.ts.map +1 -1
  3. package/dest/oracle/txe_oracle_top_level_context.d.ts +14 -6
  4. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  5. package/dest/oracle/txe_oracle_top_level_context.js +46 -21
  6. package/dest/rpc_translator.d.ts +44 -3
  7. package/dest/rpc_translator.d.ts.map +1 -1
  8. package/dest/rpc_translator.js +207 -26
  9. package/dest/state_machine/archiver.d.ts +1 -1
  10. package/dest/state_machine/archiver.d.ts.map +1 -1
  11. package/dest/state_machine/archiver.js +6 -5
  12. package/dest/state_machine/global_variable_builder.d.ts +9 -4
  13. package/dest/state_machine/global_variable_builder.d.ts.map +1 -1
  14. package/dest/state_machine/global_variable_builder.js +9 -3
  15. package/dest/state_machine/index.d.ts +1 -1
  16. package/dest/state_machine/index.d.ts.map +1 -1
  17. package/dest/state_machine/index.js +2 -2
  18. package/dest/state_machine/mock_epoch_cache.d.ts +2 -1
  19. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  20. package/dest/state_machine/mock_epoch_cache.js +3 -0
  21. package/dest/txe_session.d.ts +48 -4
  22. package/dest/txe_session.d.ts.map +1 -1
  23. package/dest/txe_session.js +77 -17
  24. package/dest/util/encoding.d.ts +3 -1
  25. package/dest/util/encoding.d.ts.map +1 -1
  26. package/dest/util/encoding.js +4 -0
  27. package/package.json +15 -15
  28. package/src/oracle/interfaces.ts +3 -1
  29. package/src/oracle/txe_oracle_top_level_context.ts +46 -21
  30. package/src/rpc_translator.ts +254 -37
  31. package/src/state_machine/archiver.ts +4 -5
  32. package/src/state_machine/global_variable_builder.ts +13 -4
  33. package/src/state_machine/index.ts +3 -1
  34. package/src/state_machine/mock_epoch_cache.ts +4 -0
  35. package/src/txe_session.ts +130 -12
  36. package/src/util/encoding.ts +5 -0
@@ -1,9 +1,7 @@
1
1
  import {
2
2
  CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS,
3
- DEFAULT_DA_GAS_LIMIT,
4
- DEFAULT_L2_GAS_LIMIT,
5
- DEFAULT_TEARDOWN_DA_GAS_LIMIT,
6
- DEFAULT_TEARDOWN_L2_GAS_LIMIT,
3
+ MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT,
4
+ MAX_PROCESSABLE_L2_GAS,
7
5
  NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP,
8
6
  } from '@aztec/constants';
9
7
  import { BlockNumber } from '@aztec/foundation/branded-types';
@@ -17,9 +15,8 @@ import {
17
15
  CapsuleService,
18
16
  CapsuleStore,
19
17
  type ContractStore,
20
- type ContractSyncService,
21
18
  NoteStore,
22
- ORACLE_VERSION,
19
+ ORACLE_VERSION_MAJOR,
23
20
  PrivateEventStore,
24
21
  RecipientTaggingStore,
25
22
  SenderAddressBookStore,
@@ -57,7 +54,13 @@ import { AuthWitness } from '@aztec/stdlib/auth-witness';
57
54
  import { PublicSimulatorConfig } from '@aztec/stdlib/avm';
58
55
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
59
56
  import { type ContractInstanceWithAddress, computePartialAddress } from '@aztec/stdlib/contract';
60
- import { Gas, GasFees, GasSettings } from '@aztec/stdlib/gas';
57
+ import {
58
+ FALLBACK_TEARDOWN_DA_GAS_LIMIT,
59
+ FALLBACK_TEARDOWN_L2_GAS_LIMIT,
60
+ Gas,
61
+ GasFees,
62
+ GasSettings,
63
+ } from '@aztec/stdlib/gas';
61
64
  import { computeCalldataHash, computeProtocolNullifier, siloNullifier } from '@aztec/stdlib/hash';
62
65
  import {
63
66
  PartialPrivateTailPublicInputsForPublic,
@@ -112,22 +115,29 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
112
115
  private version: Fr,
113
116
  private chainId: Fr,
114
117
  private authwits: Map<string, AuthWitness>,
115
- private readonly contractSyncService: ContractSyncService,
116
118
  ) {
117
119
  this.logger = createLogger('txe:top_level_context');
118
120
  this.logger.debug('Entering Top Level Context');
119
121
  }
120
122
 
121
- assertCompatibleOracleVersion(version: number): void {
122
- if (version !== ORACLE_VERSION) {
123
+ private contractOracleVersion: { major: number; minor: number } | undefined;
124
+
125
+ assertCompatibleOracleVersion(major: number, minor: number): void {
126
+ if (major !== ORACLE_VERSION_MAJOR) {
123
127
  const hint =
124
- version > ORACLE_VERSION
128
+ major > ORACLE_VERSION_MAJOR
125
129
  ? 'The contract was compiled with a newer version of Aztec.nr than this aztec cli version supports. Upgrade your aztec cli version to a compatible version.'
126
130
  : 'The contract was compiled with an older version of Aztec.nr than this aztec cli version supports. Recompile the contract with a compatible version of Aztec.nr.';
127
131
  throw new Error(
128
- `Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle version ${ORACLE_VERSION}, got ${version})`,
132
+ `Incompatible aztec cli version: ${hint} See https://docs.aztec.network/errors/8 (expected oracle major version ${ORACLE_VERSION_MAJOR}, got ${major})`,
129
133
  );
130
134
  }
135
+ this.contractOracleVersion = { major, minor };
136
+ }
137
+
138
+ // Prefixed with "nonOracleFunction" as it is not used as an oracle handler.
139
+ nonOracleFunctionGetContractOracleVersion(): { major: number; minor: number } | undefined {
140
+ return this.contractOracleVersion;
131
141
  }
132
142
 
133
143
  // This is typically only invoked in private contexts, but it is convenient to also have it in top-level for testing
@@ -174,7 +184,12 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
174
184
 
175
185
  const txEffects = block!.body.txEffects[0];
176
186
 
177
- return { txHash: txEffects.txHash, noteHashes: txEffects.noteHashes, nullifiers: txEffects.nullifiers };
187
+ return {
188
+ txHash: txEffects.txHash,
189
+ noteHashes: txEffects.noteHashes,
190
+ nullifiers: txEffects.nullifiers,
191
+ privateLogs: txEffects.privateLogs,
192
+ };
178
193
  }
179
194
 
180
195
  async syncContractNonOracleMethod(contractAddress: AztecAddress, scope: AztecAddress, jobId: string) {
@@ -346,8 +361,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
346
361
 
347
362
  const callContext = new CallContext(from, targetContractAddress, functionSelector, isStaticCall);
348
363
 
349
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
350
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
364
+ const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
365
+ const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
351
366
  const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
352
367
 
353
368
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
@@ -394,6 +409,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
394
409
  senderForTags: from,
395
410
  simulator,
396
411
  messageContextService: this.stateMachine.messageContextService,
412
+ l2TipsStore: this.stateMachine.node,
397
413
  });
398
414
 
399
415
  // Note: This is a slight modification of simulator.run without any of the checks. Maybe we should modify simulator.run with a boolean value to skip checks.
@@ -501,11 +517,17 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
501
517
  }
502
518
  }
503
519
 
520
+ // Walk the nested private-call tree and collect every offchain effect the transaction emitted.
521
+ // PXE stores these on each `PrivateCallExecutionResult` and they never reach TXE via the
522
+ // `aztec_utl_emitOffchainEffect` foreign-call path (that path only fires at the top-level), so
523
+ // we pull them out here and the RPC wrapper will hand them to `TXESession` for buffering.
524
+ const offchainEffects = collectNested([executionResult], r => r.offchainEffects.map(e => e.data));
525
+
504
526
  if (isStaticCall) {
505
527
  await checkpoint!.revert();
506
528
 
507
529
  await forkedWorldTrees.close();
508
- return executionResult.returnValues ?? [];
530
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
509
531
  }
510
532
 
511
533
  const txEffect = TxEffect.empty();
@@ -527,7 +549,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
527
549
 
528
550
  await forkedWorldTrees.close();
529
551
 
530
- return executionResult.returnValues ?? [];
552
+ return { returnValues: executionResult.returnValues ?? [], offchainEffects };
531
553
  }
532
554
 
533
555
  async publicCallNewFlow(
@@ -542,9 +564,9 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
542
564
 
543
565
  const blockNumber = await this.getNextBlockNumber();
544
566
 
545
- const gasLimits = new Gas(DEFAULT_DA_GAS_LIMIT, DEFAULT_L2_GAS_LIMIT);
567
+ const gasLimits = new Gas(MAX_PROCESSABLE_DA_GAS_PER_CHECKPOINT, MAX_PROCESSABLE_L2_GAS);
546
568
 
547
- const teardownGasLimits = new Gas(DEFAULT_TEARDOWN_DA_GAS_LIMIT, DEFAULT_TEARDOWN_L2_GAS_LIMIT);
569
+ const teardownGasLimits = new Gas(FALLBACK_TEARDOWN_DA_GAS_LIMIT, FALLBACK_TEARDOWN_L2_GAS_LIMIT);
548
570
 
549
571
  const gasSettings = new GasSettings(gasLimits, teardownGasLimits, GasFees.empty(), GasFees.empty());
550
572
 
@@ -736,6 +758,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
736
758
 
737
759
  try {
738
760
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
761
+ const simulator = new WASMSimulator();
739
762
  const oracle = new UtilityExecutionOracle({
740
763
  contractAddress: call.to,
741
764
  authWitnesses: [],
@@ -751,11 +774,13 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
751
774
  capsuleService: new CapsuleService(this.capsuleStore, scopes),
752
775
  privateEventStore: this.privateEventStore,
753
776
  messageContextService: this.stateMachine.messageContextService,
754
- contractSyncService: this.contractSyncService,
777
+ contractSyncService: this.stateMachine.contractSyncService,
778
+ l2TipsStore: this.stateMachine.node,
755
779
  jobId,
756
780
  scopes,
781
+ simulator,
757
782
  });
758
- const acirExecutionResult = await new WASMSimulator()
783
+ const acirExecutionResult = await simulator
759
784
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
760
785
  .catch((err: Error) => {
761
786
  err.message = resolveAssertionMessageFromError(err, entryPointArtifact);
@@ -1,6 +1,12 @@
1
1
  import type { ContractInstanceWithAddress } from '@aztec/aztec.js/contracts';
2
2
  import { Fr, Point } from '@aztec/aztec.js/fields';
3
- import { MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX } from '@aztec/constants';
3
+ import {
4
+ MAX_NOTE_HASHES_PER_TX,
5
+ MAX_NULLIFIERS_PER_TX,
6
+ MAX_PRIVATE_LOGS_PER_TX,
7
+ PRIVATE_LOG_CIPHERTEXT_LEN,
8
+ PRIVATE_LOG_SIZE_IN_FIELDS,
9
+ } from '@aztec/constants';
4
10
  import { BlockNumber } from '@aztec/foundation/branded-types';
5
11
  import {
6
12
  type IMiscOracle,
@@ -10,7 +16,6 @@ import {
10
16
  } from '@aztec/pxe/simulator';
11
17
  import { type ContractArtifact, EventSelector, FunctionSelector, NoteSelector } from '@aztec/stdlib/abi';
12
18
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
13
- import { BlockHash } from '@aztec/stdlib/block';
14
19
 
15
20
  import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
16
21
  import type { TXESessionStateHandler } from './txe_session.js';
@@ -20,6 +25,7 @@ import {
20
25
  addressFromSingle,
21
26
  arrayOfArraysToBoundedVecOfArrays,
22
27
  arrayToBoundedVec,
28
+ blockHashFromSingle,
23
29
  bufferToU8Array,
24
30
  fromArray,
25
31
  fromSingle,
@@ -264,10 +270,11 @@ export class RPCTranslator {
264
270
  // PXE oracles
265
271
 
266
272
  // eslint-disable-next-line camelcase
267
- aztec_utl_assertCompatibleOracleVersion(foreignVersion: ForeignCallSingle) {
268
- const version = fromSingle(foreignVersion).toNumber();
273
+ aztec_utl_assertCompatibleOracleVersionV2(foreignMajor: ForeignCallSingle, foreignMinor: ForeignCallSingle) {
274
+ const major = fromSingle(foreignMajor).toNumber();
275
+ const minor = fromSingle(foreignMinor).toNumber();
269
276
 
270
- this.handlerAsMisc().assertCompatibleOracleVersion(version);
277
+ this.handlerAsMisc().assertCompatibleOracleVersion(major, minor);
271
278
 
272
279
  return toForeignCallResult([]);
273
280
  }
@@ -288,15 +295,81 @@ export class RPCTranslator {
288
295
 
289
296
  // eslint-disable-next-line camelcase
290
297
  async aztec_txe_getLastTxEffects() {
291
- const { txHash, noteHashes, nullifiers } = await this.handlerAsTxe().getLastTxEffects();
298
+ const { txHash, noteHashes, nullifiers, privateLogs } = await this.handlerAsTxe().getLastTxEffects();
299
+
300
+ if (privateLogs.length > MAX_PRIVATE_LOGS_PER_TX) {
301
+ throw new Error(`${privateLogs.length} private logs exceed max ${MAX_PRIVATE_LOGS_PER_TX}`);
302
+ }
303
+
304
+ // Same workaround as `aztec_txe_getPrivateEvents`: Noir cannot yet return nested structs with arrays, so we return
305
+ // a flat multidimensional array plus per-log lengths and the total count, and reassemble into a
306
+ // `BoundedVec<BoundedVec<T>>` on the Noir side. Each log contributes only its emitted fields. The rest
307
+ // is zero-padded to `PRIVATE_LOG_SIZE_IN_FIELDS`.
308
+ const emittedLogs = privateLogs.map(log => log.getEmittedFields());
309
+
310
+ const rawLogStorage = emittedLogs
311
+ .map(fields => fields.concat(Array(PRIVATE_LOG_SIZE_IN_FIELDS - fields.length).fill(new Fr(0))))
312
+ .concat(
313
+ Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(Array(PRIVATE_LOG_SIZE_IN_FIELDS).fill(new Fr(0))),
314
+ )
315
+ .flat();
316
+
317
+ const logLengths = emittedLogs
318
+ .map(fields => new Fr(fields.length))
319
+ .concat(Array(MAX_PRIVATE_LOGS_PER_TX - emittedLogs.length).fill(new Fr(0)));
320
+
321
+ const logCount = new Fr(emittedLogs.length);
292
322
 
293
323
  return toForeignCallResult([
294
324
  toSingle(txHash.hash),
295
325
  ...arrayToBoundedVec(toArray(noteHashes), MAX_NOTE_HASHES_PER_TX),
296
326
  ...arrayToBoundedVec(toArray(nullifiers), MAX_NULLIFIERS_PER_TX),
327
+ toArray(rawLogStorage),
328
+ toArray(logLengths),
329
+ toSingle(logCount),
297
330
  ]);
298
331
  }
299
332
 
333
+ // eslint-disable-next-line camelcase
334
+ aztec_txe_getLastCallOffchainEffects() {
335
+ // This oracle returns all offchain effect payloads (messages, authwit requests, etc.) emitted by the last top-level call,
336
+ // MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY is arbitrarily set at 64 because we need a bound. Nothing inherent about it.
337
+ const MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY = 64;
338
+ // Must match MAX_OFFCHAIN_EFFECT_LEN in txe_oracles.nr.
339
+ const MAX_OFFCHAIN_EFFECT_LEN = 2 + PRIVATE_LOG_CIPHERTEXT_LEN;
340
+
341
+ const { effects } = this.stateHandler.getLastCallOffchainEffects();
342
+
343
+ if (effects.length > MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY) {
344
+ throw new Error(`${effects.length} offchain effects exceed max ${MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY}`);
345
+ }
346
+ if (effects.some(e => e.length > MAX_OFFCHAIN_EFFECT_LEN)) {
347
+ throw new Error(`Some offchain effect has length larger than max ${MAX_OFFCHAIN_EFFECT_LEN}`);
348
+ }
349
+
350
+ const rawArrayStorage = effects
351
+ .map(e => e.concat(Array(MAX_OFFCHAIN_EFFECT_LEN - e.length).fill(new Fr(0))))
352
+ .concat(
353
+ Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(Array(MAX_OFFCHAIN_EFFECT_LEN).fill(new Fr(0))),
354
+ )
355
+ .flat();
356
+
357
+ const effectLengths = effects
358
+ .map(e => new Fr(e.length))
359
+ .concat(Array(MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY - effects.length).fill(new Fr(0)));
360
+
361
+ const count = new Fr(effects.length);
362
+
363
+ return toForeignCallResult([toArray(rawArrayStorage), toArray(effectLengths), toSingle(count)]);
364
+ }
365
+
366
+ // eslint-disable-next-line camelcase
367
+ aztec_txe_getLastCallContext() {
368
+ const { txHash, anchorBlockTimestamp } = this.stateHandler.getLastCallContext();
369
+ const isSome = txHash.isZero() ? 0 : 1;
370
+ return toForeignCallResult([toSingle(isSome), toSingle(txHash), toSingle(new Fr(anchorBlockTimestamp))]);
371
+ }
372
+
300
373
  // eslint-disable-next-line camelcase
301
374
  async aztec_txe_getPrivateEvents(
302
375
  foreignSelector: ForeignCallSingle,
@@ -384,7 +457,7 @@ export class RPCTranslator {
384
457
  foreignStartStorageSlot: ForeignCallSingle,
385
458
  foreignNumberOfElements: ForeignCallSingle,
386
459
  ) {
387
- const blockHash = new BlockHash(fromSingle(foreignBlockHash));
460
+ const blockHash = blockHashFromSingle(foreignBlockHash);
388
461
  const contractAddress = addressFromSingle(foreignContractAddress);
389
462
  const startStorageSlot = fromSingle(foreignStartStorageSlot);
390
463
  const numberOfElements = fromSingle(foreignNumberOfElements).toNumber();
@@ -401,7 +474,7 @@ export class RPCTranslator {
401
474
 
402
475
  // eslint-disable-next-line camelcase
403
476
  async aztec_utl_getPublicDataWitness(foreignBlockHash: ForeignCallSingle, foreignLeafSlot: ForeignCallSingle) {
404
- const blockHash = new BlockHash(fromSingle(foreignBlockHash));
477
+ const blockHash = blockHashFromSingle(foreignBlockHash);
405
478
  const leafSlot = fromSingle(foreignLeafSlot);
406
479
 
407
480
  const witness = await this.handlerAsUtility().getPublicDataWitness(blockHash, leafSlot);
@@ -628,7 +701,7 @@ export class RPCTranslator {
628
701
  foreignBlockHash: ForeignCallSingle,
629
702
  foreignNullifier: ForeignCallSingle,
630
703
  ) {
631
- const blockHash = new BlockHash(fromSingle(foreignBlockHash));
704
+ const blockHash = blockHashFromSingle(foreignBlockHash);
632
705
  const nullifier = fromSingle(foreignNullifier);
633
706
 
634
707
  const witness = await this.handlerAsUtility().getNullifierMembershipWitness(blockHash, nullifier);
@@ -692,7 +765,7 @@ export class RPCTranslator {
692
765
  foreignAnchorBlockHash: ForeignCallSingle,
693
766
  foreignNoteHash: ForeignCallSingle,
694
767
  ) {
695
- const blockHash = new BlockHash(fromSingle(foreignAnchorBlockHash));
768
+ const blockHash = blockHashFromSingle(foreignAnchorBlockHash);
696
769
  const noteHash = fromSingle(foreignNoteHash);
697
770
 
698
771
  const witness = await this.handlerAsUtility().getNoteHashMembershipWitness(blockHash, noteHash);
@@ -708,8 +781,8 @@ export class RPCTranslator {
708
781
  foreignAnchorBlockHash: ForeignCallSingle,
709
782
  foreignBlockHash: ForeignCallSingle,
710
783
  ) {
711
- const anchorBlockHash = new BlockHash(fromSingle(foreignAnchorBlockHash));
712
- const blockHash = new BlockHash(fromSingle(foreignBlockHash));
784
+ const anchorBlockHash = blockHashFromSingle(foreignAnchorBlockHash);
785
+ const blockHash = blockHashFromSingle(foreignBlockHash);
713
786
 
714
787
  const witness = await this.handlerAsUtility().getBlockHashMembershipWitness(anchorBlockHash, blockHash);
715
788
 
@@ -726,7 +799,7 @@ export class RPCTranslator {
726
799
  foreignBlockHash: ForeignCallSingle,
727
800
  foreignNullifier: ForeignCallSingle,
728
801
  ) {
729
- const blockHash = new BlockHash(fromSingle(foreignBlockHash));
802
+ const blockHash = blockHashFromSingle(foreignBlockHash);
730
803
  const nullifier = fromSingle(foreignNullifier);
731
804
 
732
805
  const witness = await this.handlerAsUtility().getLowNullifierMembershipWitness(blockHash, nullifier);
@@ -750,6 +823,13 @@ export class RPCTranslator {
750
823
  return toForeignCallResult([]);
751
824
  }
752
825
 
826
+ // eslint-disable-next-line camelcase
827
+ async aztec_utl_getPendingTaggedLogs_v2(foreignScope: ForeignCallSingle) {
828
+ const scope = AztecAddress.fromField(fromSingle(foreignScope));
829
+ const slot = await this.handlerAsUtility().getPendingTaggedLogsV2(scope);
830
+ return toForeignCallResult([toSingle(slot)]);
831
+ }
832
+
753
833
  // eslint-disable-next-line camelcase
754
834
  public async aztec_utl_validateAndStoreEnqueuedNotesAndEvents(
755
835
  foreignContractAddress: ForeignCallSingle,
@@ -778,6 +858,31 @@ export class RPCTranslator {
778
858
  return toForeignCallResult([]);
779
859
  }
780
860
 
861
+ // eslint-disable-next-line camelcase
862
+ public async aztec_utl_validateAndStoreEnqueuedNotesAndEvents_v2(
863
+ foreignNoteValidationRequestsArrayBaseSlot: ForeignCallSingle,
864
+ foreignEventValidationRequestsArrayBaseSlot: ForeignCallSingle,
865
+ foreignMaxNotePackedLen: ForeignCallSingle,
866
+ foreignMaxEventSerializedLen: ForeignCallSingle,
867
+ foreignScope: ForeignCallSingle,
868
+ ) {
869
+ const noteValidationRequestsArrayBaseSlot = fromSingle(foreignNoteValidationRequestsArrayBaseSlot);
870
+ const eventValidationRequestsArrayBaseSlot = fromSingle(foreignEventValidationRequestsArrayBaseSlot);
871
+ const maxNotePackedLen = fromSingle(foreignMaxNotePackedLen).toNumber();
872
+ const maxEventSerializedLen = fromSingle(foreignMaxEventSerializedLen).toNumber();
873
+ const scope = AztecAddress.fromField(fromSingle(foreignScope));
874
+
875
+ await this.handlerAsUtility().validateAndStoreEnqueuedNotesAndEventsV2(
876
+ noteValidationRequestsArrayBaseSlot,
877
+ eventValidationRequestsArrayBaseSlot,
878
+ maxNotePackedLen,
879
+ maxEventSerializedLen,
880
+ scope,
881
+ );
882
+
883
+ return toForeignCallResult([]);
884
+ }
885
+
781
886
  // eslint-disable-next-line camelcase
782
887
  public async aztec_utl_getLogsByTag(
783
888
  foreignContractAddress: ForeignCallSingle,
@@ -822,6 +927,20 @@ export class RPCTranslator {
822
927
  return toForeignCallResult([]);
823
928
  }
824
929
 
930
+ // eslint-disable-next-line camelcase
931
+ async aztec_utl_getLogsByTag_v2(foreignRequestArrayBaseSlot: ForeignCallSingle) {
932
+ const requestArrayBaseSlot = fromSingle(foreignRequestArrayBaseSlot);
933
+ const responseSlot = await this.handlerAsUtility().getLogsByTagV2(requestArrayBaseSlot);
934
+ return toForeignCallResult([toSingle(responseSlot)]);
935
+ }
936
+
937
+ // eslint-disable-next-line camelcase
938
+ async aztec_utl_getMessageContextsByTxHash_v2(foreignRequestArrayBaseSlot: ForeignCallSingle) {
939
+ const requestArrayBaseSlot = fromSingle(foreignRequestArrayBaseSlot);
940
+ const responseSlot = await this.handlerAsUtility().getMessageContextsByTxHashV2(requestArrayBaseSlot);
941
+ return toForeignCallResult([toSingle(responseSlot)]);
942
+ }
943
+
825
944
  // eslint-disable-next-line camelcase
826
945
  aztec_utl_setCapsule(
827
946
  foreignContractAddress: ForeignCallSingle,
@@ -898,6 +1017,64 @@ export class RPCTranslator {
898
1017
  return toForeignCallResult([]);
899
1018
  }
900
1019
 
1020
+ // eslint-disable-next-line camelcase
1021
+ aztec_utl_pushEphemeral(foreignSlot: ForeignCallSingle, foreignElements: ForeignCallArray) {
1022
+ const slot = fromSingle(foreignSlot);
1023
+ const elements = fromArray(foreignElements);
1024
+ const newLen = this.handlerAsUtility().pushEphemeral(slot, elements);
1025
+ return toForeignCallResult([toSingle(new Fr(newLen))]);
1026
+ }
1027
+
1028
+ // eslint-disable-next-line camelcase
1029
+ aztec_utl_popEphemeral(foreignSlot: ForeignCallSingle) {
1030
+ const slot = fromSingle(foreignSlot);
1031
+ const element = this.handlerAsUtility().popEphemeral(slot);
1032
+ return toForeignCallResult([toArray(element)]);
1033
+ }
1034
+
1035
+ // eslint-disable-next-line camelcase
1036
+ aztec_utl_getEphemeral(foreignSlot: ForeignCallSingle, foreignIndex: ForeignCallSingle) {
1037
+ const slot = fromSingle(foreignSlot);
1038
+ const index = fromSingle(foreignIndex).toNumber();
1039
+ const element = this.handlerAsUtility().getEphemeral(slot, index);
1040
+ return toForeignCallResult([toArray(element)]);
1041
+ }
1042
+
1043
+ // eslint-disable-next-line camelcase
1044
+ aztec_utl_setEphemeral(
1045
+ foreignSlot: ForeignCallSingle,
1046
+ foreignIndex: ForeignCallSingle,
1047
+ foreignElements: ForeignCallArray,
1048
+ ) {
1049
+ const slot = fromSingle(foreignSlot);
1050
+ const index = fromSingle(foreignIndex).toNumber();
1051
+ const elements = fromArray(foreignElements);
1052
+ this.handlerAsUtility().setEphemeral(slot, index, elements);
1053
+ return toForeignCallResult([]);
1054
+ }
1055
+
1056
+ // eslint-disable-next-line camelcase
1057
+ aztec_utl_getEphemeralLen(foreignSlot: ForeignCallSingle) {
1058
+ const slot = fromSingle(foreignSlot);
1059
+ const len = this.handlerAsUtility().getEphemeralLen(slot);
1060
+ return toForeignCallResult([toSingle(new Fr(len))]);
1061
+ }
1062
+
1063
+ // eslint-disable-next-line camelcase
1064
+ aztec_utl_removeEphemeral(foreignSlot: ForeignCallSingle, foreignIndex: ForeignCallSingle) {
1065
+ const slot = fromSingle(foreignSlot);
1066
+ const index = fromSingle(foreignIndex).toNumber();
1067
+ this.handlerAsUtility().removeEphemeral(slot, index);
1068
+ return toForeignCallResult([]);
1069
+ }
1070
+
1071
+ // eslint-disable-next-line camelcase
1072
+ aztec_utl_clearEphemeral(foreignSlot: ForeignCallSingle) {
1073
+ const slot = fromSingle(foreignSlot);
1074
+ this.handlerAsUtility().clearEphemeral(slot);
1075
+ return toForeignCallResult([]);
1076
+ }
1077
+
901
1078
  // TODO: I forgot to add a corresponding function here, when I introduced an oracle method to txe_oracle.ts.
902
1079
  // The compiler didn't throw an error, so it took me a while to learn of the existence of this file, and that I need
903
1080
  // to implement this function here. Isn't there a way to programmatically identify that this is missing, given the
@@ -966,8 +1143,13 @@ export class RPCTranslator {
966
1143
  }
967
1144
 
968
1145
  // eslint-disable-next-line camelcase
969
- aztec_utl_emitOffchainEffect(_foreignData: ForeignCallArray) {
970
- throw new Error('Offchain effects are not yet supported in the TestEnvironment');
1146
+ aztec_utl_emitOffchainEffect(foreignData: ForeignCallArray) {
1147
+ // Record the raw payload against the currently-executing top-level call. The Noir side
1148
+ // (via `env.offchain_messages()`) is responsible for decoding the protocol-reserved prefix
1149
+ // (`OFFCHAIN_MESSAGE_IDENTIFIER`, recipient) and turning each payload into an `OffchainMessage` struct suitable
1150
+ // for `offchain_receive`.
1151
+ this.stateHandler.recordOffchainEffect(fromArray(foreignData));
1152
+ return Promise.resolve(toForeignCallResult([]));
971
1153
  }
972
1154
 
973
1155
  // AVM opcodes
@@ -1176,18 +1358,38 @@ export class RPCTranslator {
1176
1358
  const argsHash = fromSingle(foreignArgsHash);
1177
1359
  const isStaticCall = fromSingle(foreignIsStaticCall).toBool();
1178
1360
 
1179
- const returnValues = await this.handlerAsTxe().privateCallNewFlow(
1180
- from,
1181
- targetContractAddress,
1182
- functionSelector,
1183
- args,
1184
- argsHash,
1185
- isStaticCall,
1186
- this.stateHandler.getCurrentJob(),
1187
- );
1361
+ const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
1362
+ const { returnValues, offchainEffects } = await this.handlerAsTxe().privateCallNewFlow(
1363
+ from,
1364
+ targetContractAddress,
1365
+ functionSelector,
1366
+ args,
1367
+ argsHash,
1368
+ isStaticCall,
1369
+ this.stateHandler.getCurrentJob(),
1370
+ );
1371
+
1372
+ // Private execution collects offchain effects inside PXE's PrivateExecutionOracle rather than
1373
+ // round-tripping them through `aztec_utl_emitOffchainEffect`, so the session buffer is empty
1374
+ // at this point. Drain the effects from the execution tree into the session buffer so the
1375
+ // next `env.offchain_messages()` call in the test sees them.
1376
+ for (const data of offchainEffects) {
1377
+ this.stateHandler.recordOffchainEffect(data);
1378
+ }
1379
+
1380
+ // TODO(F-335): Avoid doing the following call here.
1381
+ await this.stateHandler.cycleJob();
1382
+
1383
+ if (isStaticCall) {
1384
+ // Static calls revert their checkpoint and mine no block, so there is no tx hash to tag
1385
+ // offchain effects with. Querying `getLastTxEffects()` here would return an unrelated
1386
+ // predecessor tx.
1387
+ return { result: returnValues };
1388
+ }
1389
+ const { txHash } = await this.handlerAsTxe().getLastTxEffects();
1390
+ return { result: returnValues, txHash: txHash.hash };
1391
+ });
1188
1392
 
1189
- // TODO(F-335): Avoid doing the following call here.
1190
- await this.stateHandler.cycleJob();
1191
1393
  return toForeignCallResult([toArray(returnValues)]);
1192
1394
  }
1193
1395
 
@@ -1201,15 +1403,20 @@ export class RPCTranslator {
1201
1403
  const functionSelector = FunctionSelector.fromField(fromSingle(foreignFunctionSelector));
1202
1404
  const args = fromArray(foreignArgs);
1203
1405
 
1204
- const returnValues = await this.handlerAsTxe().executeUtilityFunction(
1205
- targetContractAddress,
1206
- functionSelector,
1207
- args,
1208
- this.stateHandler.getCurrentJob(),
1209
- );
1406
+ const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
1407
+ const returnValues = await this.handlerAsTxe().executeUtilityFunction(
1408
+ targetContractAddress,
1409
+ functionSelector,
1410
+ args,
1411
+ this.stateHandler.getCurrentJob(),
1412
+ );
1413
+
1414
+ // TODO(F-335): Avoid doing the following call here.
1415
+ await this.stateHandler.cycleJob();
1416
+
1417
+ return { result: returnValues };
1418
+ });
1210
1419
 
1211
- // TODO(F-335): Avoid doing the following call here.
1212
- await this.stateHandler.cycleJob();
1213
1420
  return toForeignCallResult([toArray(returnValues)]);
1214
1421
  }
1215
1422
 
@@ -1225,10 +1432,20 @@ export class RPCTranslator {
1225
1432
  const calldata = fromArray(foreignCalldata);
1226
1433
  const isStaticCall = fromSingle(foreignIsStaticCall).toBool();
1227
1434
 
1228
- const returnValues = await this.handlerAsTxe().publicCallNewFlow(from, address, calldata, isStaticCall);
1435
+ const returnValues = await this.stateHandler.withTopLevelCallTracking(async () => {
1436
+ const returnValues = await this.handlerAsTxe().publicCallNewFlow(from, address, calldata, isStaticCall);
1437
+
1438
+ // TODO(F-335): Avoid doing the following call here.
1439
+ await this.stateHandler.cycleJob();
1440
+
1441
+ if (isStaticCall) {
1442
+ // See equivalent branch in `aztec_txe_privateCallNewFlow`.
1443
+ return { result: returnValues };
1444
+ }
1445
+ const { txHash } = await this.handlerAsTxe().getLastTxEffects();
1446
+ return { result: returnValues, txHash: txHash.hash };
1447
+ });
1229
1448
 
1230
- // TODO(F-335): Avoid doing the following call here.
1231
- await this.stateHandler.cycleJob();
1232
1449
  return toForeignCallResult([toArray(returnValues)]);
1233
1450
  }
1234
1451
 
@@ -1,4 +1,4 @@
1
- import { ArchiverDataSourceBase, ArchiverDataStoreUpdater, KVArchiverDataStore } from '@aztec/archiver';
1
+ import { ArchiverDataSourceBase, ArchiverDataStoreUpdater, createArchiverDataStores } from '@aztec/archiver';
2
2
  import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants';
3
3
  import { CheckpointNumber, type EpochNumber, type SlotNumber } from '@aztec/foundation/branded-types';
4
4
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -14,11 +14,10 @@ import type { L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
14
14
  * without needing any of the extra overhead that the Archiver itself requires (i.e. an L1 client).
15
15
  */
16
16
  export class TXEArchiver extends ArchiverDataSourceBase {
17
- private readonly updater = new ArchiverDataStoreUpdater(this.store);
17
+ private readonly updater = new ArchiverDataStoreUpdater(this.stores);
18
18
 
19
19
  constructor(db: AztecAsyncKVStore) {
20
- const store = new KVArchiverDataStore(db, 9999);
21
- super(store);
20
+ super(createArchiverDataStores(db, { logsMaxPageSize: 9999 }));
22
21
  }
23
22
 
24
23
  public async addCheckpoints(checkpoints: PublishedCheckpoint[], result?: ValidateCheckpointResult): Promise<void> {
@@ -61,7 +60,7 @@ export class TXEArchiver extends ArchiverDataSourceBase {
61
60
  }
62
61
  // TXE uses 1-block-per-checkpoint for testing simplicity, so we can use block number as checkpoint number.
63
62
  // This uses the deprecated fromBlockNumber method intentionally for the TXE testing environment.
64
- const checkpoint = await this.store.getRangeOfCheckpoints(CheckpointNumber.fromBlockNumber(number), 1);
63
+ const checkpoint = await this.stores.blocks.getRangeOfCheckpoints(CheckpointNumber.fromBlockNumber(number), 1);
65
64
  if (checkpoint.length === 0) {
66
65
  throw new Error(`L2Tips requested from TXE Archiver but no checkpoint found for block number ${number}`);
67
66
  }
@@ -1,20 +1,29 @@
1
+ import type { SimulationOverridesPlan } from '@aztec/ethereum/contracts';
1
2
  import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { times } from '@aztec/foundation/collection';
2
4
  import type { EthAddress } from '@aztec/foundation/eth-address';
3
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
- import { GasFees } from '@aztec/stdlib/gas';
6
+ import { FEE_ORACLE_LAG, GasFees } from '@aztec/stdlib/gas';
5
7
  import { makeGlobalVariables } from '@aztec/stdlib/testing';
6
8
  import {
7
- type BuildCheckpointGlobalVariablesOpts,
8
9
  type CheckpointGlobalVariables,
10
+ type FeeProvider,
9
11
  type GlobalVariableBuilder,
10
12
  GlobalVariables,
11
13
  } from '@aztec/stdlib/tx';
12
14
 
13
- export class TXEGlobalVariablesBuilder implements GlobalVariableBuilder {
15
+ /** Simple FeeProvider for TXE that returns zero fees. */
16
+ export class TXEFeeProvider implements FeeProvider {
14
17
  public getCurrentMinFees(): Promise<GasFees> {
15
18
  return Promise.resolve(new GasFees(0, 0));
16
19
  }
17
20
 
21
+ public getPredictedMinFees(): Promise<GasFees[]> {
22
+ return Promise.resolve(times(FEE_ORACLE_LAG, () => new GasFees(0, 0)));
23
+ }
24
+ }
25
+
26
+ export class TXEGlobalVariablesBuilder implements GlobalVariableBuilder {
18
27
  public buildGlobalVariables(
19
28
  _blockNumber: BlockNumber,
20
29
  _coinbase: EthAddress,
@@ -28,7 +37,7 @@ export class TXEGlobalVariablesBuilder implements GlobalVariableBuilder {
28
37
  _coinbase: EthAddress,
29
38
  _feeRecipient: AztecAddress,
30
39
  _slotNumber: SlotNumber,
31
- _opts?: BuildCheckpointGlobalVariablesOpts,
40
+ _simulationOverridesPlan?: SimulationOverridesPlan,
32
41
  ): Promise<CheckpointGlobalVariables> {
33
42
  const vars = makeGlobalVariables();
34
43
  return Promise.resolve({