@aztec/txe 0.0.1-commit.c80b6263 → 0.0.1-commit.cf93bcc56

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 (33) hide show
  1. package/dest/oracle/txe_oracle_top_level_context.d.ts +2 -2
  2. package/dest/oracle/txe_oracle_top_level_context.d.ts.map +1 -1
  3. package/dest/oracle/txe_oracle_top_level_context.js +80 -21
  4. package/dest/rpc_translator.d.ts +7 -7
  5. package/dest/rpc_translator.d.ts.map +1 -1
  6. package/dest/rpc_translator.js +37 -25
  7. package/dest/state_machine/archiver.d.ts +1 -1
  8. package/dest/state_machine/archiver.d.ts.map +1 -1
  9. package/dest/state_machine/archiver.js +2 -0
  10. package/dest/state_machine/dummy_p2p_client.d.ts +13 -10
  11. package/dest/state_machine/dummy_p2p_client.d.ts.map +1 -1
  12. package/dest/state_machine/dummy_p2p_client.js +24 -15
  13. package/dest/state_machine/index.d.ts +5 -5
  14. package/dest/state_machine/index.d.ts.map +1 -1
  15. package/dest/state_machine/index.js +15 -10
  16. package/dest/state_machine/mock_epoch_cache.d.ts +3 -1
  17. package/dest/state_machine/mock_epoch_cache.d.ts.map +1 -1
  18. package/dest/state_machine/mock_epoch_cache.js +4 -0
  19. package/dest/txe_session.d.ts +1 -1
  20. package/dest/txe_session.d.ts.map +1 -1
  21. package/dest/txe_session.js +68 -9
  22. package/dest/utils/block_creation.d.ts +1 -1
  23. package/dest/utils/block_creation.d.ts.map +1 -1
  24. package/dest/utils/block_creation.js +3 -1
  25. package/package.json +15 -15
  26. package/src/oracle/txe_oracle_top_level_context.ts +83 -69
  27. package/src/rpc_translator.ts +39 -20
  28. package/src/state_machine/archiver.ts +2 -0
  29. package/src/state_machine/dummy_p2p_client.ts +34 -20
  30. package/src/state_machine/index.ts +25 -9
  31. package/src/state_machine/mock_epoch_cache.ts +5 -0
  32. package/src/txe_session.ts +70 -66
  33. package/src/utils/block_creation.ts +3 -1
@@ -12,6 +12,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
12
12
  import { LogLevels, type Logger, applyStringFormatting, createLogger } from '@aztec/foundation/log';
13
13
  import { TestDateProvider } from '@aztec/foundation/timer';
14
14
  import type { KeyStore } from '@aztec/key-store';
15
+ import type { AccessScopes } from '@aztec/pxe/client/lazy';
15
16
  import {
16
17
  AddressStore,
17
18
  CapsuleStore,
@@ -22,7 +23,6 @@ import {
22
23
  SenderAddressBookStore,
23
24
  SenderTaggingStore,
24
25
  enrichPublicSimulationError,
25
- syncState,
26
26
  } from '@aztec/pxe/server';
27
27
  import {
28
28
  ExecutionNoteCache,
@@ -132,13 +132,14 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
132
132
  }
133
133
 
134
134
  // We instruct users to debug contracts via this oracle, so it makes sense that they'd expect it to also work in tests
135
- utilityDebugLog(level: number, message: string, fields: Fr[]): void {
135
+ utilityLog(level: number, message: string, fields: Fr[]): Promise<void> {
136
136
  if (!LogLevels[level]) {
137
- throw new Error(`Invalid debug log level: ${level}`);
137
+ throw new Error(`Invalid log level: ${level}`);
138
138
  }
139
139
  const levelName = LogLevels[level];
140
140
 
141
141
  this.logger[levelName](`${applyStringFormatting(message, fields)}`, { module: `${this.logger.module}:debug_log` });
142
+ return Promise.resolve();
142
143
  }
143
144
 
144
145
  txeGetDefaultAddress(): AztecAddress {
@@ -297,12 +298,24 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
297
298
  throw new Error(message);
298
299
  }
299
300
 
301
+ // When `from` is the zero address (e.g. when deploying a new account contract), we return an
302
+ // empty scope list which acts as deny-all: no notes are visible and no keys are accessible.
303
+ const effectiveScopes = from.isZero() ? [] : [from];
304
+
300
305
  // Sync notes before executing private function to discover notes from previous transactions
301
- const utilityExecutor = async (call: FunctionCall) => {
302
- await this.executeUtilityCall(call);
306
+ const utilityExecutor = async (call: FunctionCall, execScopes: AccessScopes) => {
307
+ await this.executeUtilityCall(call, execScopes);
303
308
  };
304
309
 
305
- await syncState(targetContractAddress, this.contractStore, functionSelector, utilityExecutor);
310
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
311
+ await this.stateMachine.contractSyncService.ensureContractSynced(
312
+ targetContractAddress,
313
+ functionSelector,
314
+ utilityExecutor,
315
+ blockHeader,
316
+ this.jobId,
317
+ effectiveScopes,
318
+ );
306
319
 
307
320
  const blockNumber = await this.txeGetNextBlockNumber();
308
321
 
@@ -314,8 +327,6 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
314
327
 
315
328
  const txContext = new TxContext(this.chainId, this.version, gasSettings);
316
329
 
317
- const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
318
-
319
330
  const protocolNullifier = await computeProtocolNullifier(getSingleTxBlockRequestHash(blockNumber));
320
331
  const noteCache = new ExecutionNoteCache(protocolNullifier);
321
332
  // In production, the account contract sets the min revertible counter before calling the app function.
@@ -327,43 +338,37 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
327
338
 
328
339
  const simulator = new WASMSimulator();
329
340
 
330
- const privateExecutionOracle = new PrivateExecutionOracle(
341
+ const privateExecutionOracle = new PrivateExecutionOracle({
331
342
  argsHash,
332
343
  txContext,
333
344
  callContext,
334
- /** Header of a block whose state is used during private execution (not the block the transaction is included in). */
335
- blockHeader,
345
+ anchorBlockHeader: blockHeader,
336
346
  utilityExecutor,
337
- /** List of transient auth witnesses to be used during this simulation */
338
- Array.from(this.authwits.values()),
339
- /** List of transient auth witnesses to be used during this simulation */
340
- [],
341
- HashedValuesCache.create([new HashedValues(args, argsHash)]),
347
+ authWitnesses: Array.from(this.authwits.values()),
348
+ capsules: [],
349
+ executionCache: HashedValuesCache.create([new HashedValues(args, argsHash)]),
342
350
  noteCache,
343
351
  taggingIndexCache,
344
- this.contractStore,
345
- this.noteStore,
346
- this.keyStore,
347
- this.addressStore,
348
- this.stateMachine.node,
349
- this.stateMachine.anchorBlockStore,
350
- this.senderTaggingStore,
351
- this.recipientTaggingStore,
352
- this.senderAddressBookStore,
353
- this.capsuleStore,
354
- this.privateEventStore,
355
- this.jobId,
356
- 0, // totalPublicArgsCount
357
- minRevertibleSideEffectCounter, // (start) sideEffectCounter
358
- undefined, // log
359
- undefined, // scopes
360
- /**
361
- * In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
362
- * contract would perform, including setting senderForTags.
363
- */
364
- from,
352
+ contractStore: this.contractStore,
353
+ noteStore: this.noteStore,
354
+ keyStore: this.keyStore,
355
+ addressStore: this.addressStore,
356
+ aztecNode: this.stateMachine.node,
357
+ senderTaggingStore: this.senderTaggingStore,
358
+ recipientTaggingStore: this.recipientTaggingStore,
359
+ senderAddressBookStore: this.senderAddressBookStore,
360
+ capsuleStore: this.capsuleStore,
361
+ privateEventStore: this.privateEventStore,
362
+ contractSyncService: this.stateMachine.contractSyncService,
363
+ jobId: this.jobId,
364
+ totalPublicCalldataCount: 0,
365
+ sideEffectCounter: minRevertibleSideEffectCounter,
366
+ scopes: effectiveScopes,
367
+ // In TXE, the typical transaction entrypoint is skipped, so we need to simulate the actions that such a
368
+ // contract would perform, including setting senderForTags.
369
+ senderForTags: from,
365
370
  simulator,
366
- );
371
+ });
367
372
 
368
373
  // 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.
369
374
  let result: PrivateExecutionResult;
@@ -402,7 +407,8 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
402
407
  // We pass the non-zero minRevertibleSideEffectCounter to make sure the side effects are split correctly.
403
408
  const { publicInputs } = await generateSimulatedProvingResult(
404
409
  result,
405
- this.contractStore,
410
+ (addr, sel) => this.contractStore.getDebugFunctionName(addr, sel),
411
+ this.stateMachine.node,
406
412
  minRevertibleSideEffectCounter,
407
413
  );
408
414
 
@@ -585,7 +591,7 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
585
591
  constantData,
586
592
  /*gasUsed=*/ new Gas(0, 0),
587
593
  /*feePayer=*/ AztecAddress.zero(),
588
- /*includeByTimestamp=*/ 0n,
594
+ /*expirationTimestamp=*/ 0n,
589
595
  inputsForPublic,
590
596
  undefined,
591
597
  );
@@ -664,25 +670,33 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
664
670
  }
665
671
 
666
672
  // Sync notes before executing utility function to discover notes from previous transactions
667
- await syncState(targetContractAddress, this.contractStore, functionSelector, async call => {
668
- await this.executeUtilityCall(call);
669
- });
670
-
671
- const call = new FunctionCall(
672
- artifact.name,
673
+ const blockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
674
+ await this.stateMachine.contractSyncService.ensureContractSynced(
673
675
  targetContractAddress,
674
676
  functionSelector,
675
- FunctionType.UTILITY,
676
- false,
677
- false,
678
- args,
679
- [],
677
+ async (call, execScopes) => {
678
+ await this.executeUtilityCall(call, execScopes);
679
+ },
680
+ blockHeader,
681
+ this.jobId,
682
+ 'ALL_SCOPES',
680
683
  );
681
684
 
682
- return this.executeUtilityCall(call);
685
+ const call = FunctionCall.from({
686
+ name: artifact.name,
687
+ to: targetContractAddress,
688
+ selector: functionSelector,
689
+ type: FunctionType.UTILITY,
690
+ hideMsgSender: false,
691
+ isStatic: false,
692
+ args,
693
+ returnTypes: [],
694
+ });
695
+
696
+ return this.executeUtilityCall(call, 'ALL_SCOPES');
683
697
  }
684
698
 
685
- private async executeUtilityCall(call: FunctionCall): Promise<Fr[]> {
699
+ private async executeUtilityCall(call: FunctionCall, scopes: AccessScopes): Promise<Fr[]> {
686
700
  const entryPointArtifact = await this.contractStore.getFunctionArtifactWithDebugMetadata(call.to, call.selector);
687
701
  if (entryPointArtifact.functionType !== FunctionType.UTILITY) {
688
702
  throw new Error(`Cannot run ${entryPointArtifact.functionType} function as utility`);
@@ -695,23 +709,23 @@ export class TXEOracleTopLevelContext implements IMiscOracle, ITxeExecutionOracl
695
709
 
696
710
  try {
697
711
  const anchorBlockHeader = await this.stateMachine.anchorBlockStore.getBlockHeader();
698
- const oracle = new UtilityExecutionOracle(
699
- call.to,
700
- [],
701
- [],
712
+ const oracle = new UtilityExecutionOracle({
713
+ contractAddress: call.to,
714
+ authWitnesses: [],
715
+ capsules: [],
702
716
  anchorBlockHeader,
703
- this.contractStore,
704
- this.noteStore,
705
- this.keyStore,
706
- this.addressStore,
707
- this.stateMachine.node,
708
- this.stateMachine.anchorBlockStore,
709
- this.recipientTaggingStore,
710
- this.senderAddressBookStore,
711
- this.capsuleStore,
712
- this.privateEventStore,
713
- this.jobId,
714
- );
717
+ contractStore: this.contractStore,
718
+ noteStore: this.noteStore,
719
+ keyStore: this.keyStore,
720
+ addressStore: this.addressStore,
721
+ aztecNode: this.stateMachine.node,
722
+ recipientTaggingStore: this.recipientTaggingStore,
723
+ senderAddressBookStore: this.senderAddressBookStore,
724
+ capsuleStore: this.capsuleStore,
725
+ privateEventStore: this.privateEventStore,
726
+ jobId: this.jobId,
727
+ scopes,
728
+ });
715
729
  const acirExecutionResult = await new WASMSimulator()
716
730
  .executeUserCircuit(toACVMWitness(0, call.args), entryPointArtifact, new Oracle(oracle).toACIRCallback())
717
731
  .catch((err: Error) => {
@@ -328,7 +328,7 @@ export class RPCTranslator {
328
328
 
329
329
  // When the argument is a slice, noir automatically adds a length field to oracle call.
330
330
  // When the argument is an array, we add the field length manually to the signature.
331
- utilityDebugLog(
331
+ async utilityLog(
332
332
  foreignLevel: ForeignCallSingle,
333
333
  foreignMessage: ForeignCallArray,
334
334
  _foreignLength: ForeignCallSingle,
@@ -340,7 +340,7 @@ export class RPCTranslator {
340
340
  .join('');
341
341
  const fields = fromArray(foreignFields);
342
342
 
343
- this.handlerAsMisc().utilityDebugLog(level, message, fields);
343
+ await this.handlerAsMisc().utilityLog(level, message, fields);
344
344
 
345
345
  return toForeignCallResult([]);
346
346
  }
@@ -351,7 +351,7 @@ export class RPCTranslator {
351
351
  foreignStartStorageSlot: ForeignCallSingle,
352
352
  foreignNumberOfElements: ForeignCallSingle,
353
353
  ) {
354
- const blockHash = BlockHash.fromString(foreignBlockHash);
354
+ const blockHash = new BlockHash(fromSingle(foreignBlockHash));
355
355
  const contractAddress = addressFromSingle(foreignContractAddress);
356
356
  const startStorageSlot = fromSingle(foreignStartStorageSlot);
357
357
  const numberOfElements = fromSingle(foreignNumberOfElements).toNumber();
@@ -367,7 +367,7 @@ export class RPCTranslator {
367
367
  }
368
368
 
369
369
  async utilityGetPublicDataWitness(foreignBlockHash: ForeignCallSingle, foreignLeafSlot: ForeignCallSingle) {
370
- const blockHash = BlockHash.fromString(foreignBlockHash);
370
+ const blockHash = new BlockHash(fromSingle(foreignBlockHash));
371
371
  const leafSlot = fromSingle(foreignLeafSlot);
372
372
 
373
373
  const witness = await this.handlerAsUtility().utilityGetPublicDataWitness(blockHash, leafSlot);
@@ -545,12 +545,23 @@ export class RPCTranslator {
545
545
  );
546
546
  }
547
547
 
548
- async utilityGetPublicKeysAndPartialAddress(foreignAddress: ForeignCallSingle) {
548
+ async utilityTryGetPublicKeysAndPartialAddress(foreignAddress: ForeignCallSingle) {
549
549
  const address = addressFromSingle(foreignAddress);
550
550
 
551
- const { publicKeys, partialAddress } = await this.handlerAsUtility().utilityGetPublicKeysAndPartialAddress(address);
551
+ const result = await this.handlerAsUtility().utilityTryGetPublicKeysAndPartialAddress(address);
552
552
 
553
- return toForeignCallResult([toArray([...publicKeys.toFields(), partialAddress])]);
553
+ // We are going to return a Noir Option struct to represent the possibility of null values. Options are a struct
554
+ // with two fields: `some` (a boolean) and `value` (a field array in this case).
555
+ if (result === undefined) {
556
+ // No data was found so we set `some` to 0 and pad `value` with zeros get the correct return size.
557
+ return toForeignCallResult([toSingle(new Fr(0)), toArray(Array(13).fill(new Fr(0)))]);
558
+ } else {
559
+ // Data was found so we set `some` to 1 and return it along with `value`.
560
+ return toForeignCallResult([
561
+ toSingle(new Fr(1)),
562
+ toArray([...result.publicKeys.toFields(), result.partialAddress]),
563
+ ]);
564
+ }
554
565
  }
555
566
 
556
567
  async utilityGetKeyValidationRequest(foreignPkMHash: ForeignCallSingle) {
@@ -574,7 +585,7 @@ export class RPCTranslator {
574
585
  }
575
586
 
576
587
  async utilityGetNullifierMembershipWitness(foreignBlockHash: ForeignCallSingle, foreignNullifier: ForeignCallSingle) {
577
- const blockHash = BlockHash.fromString(foreignBlockHash);
588
+ const blockHash = new BlockHash(fromSingle(foreignBlockHash));
578
589
  const nullifier = fromSingle(foreignNullifier);
579
590
 
580
591
  const witness = await this.handlerAsUtility().utilityGetNullifierMembershipWitness(blockHash, nullifier);
@@ -641,26 +652,34 @@ export class RPCTranslator {
641
652
  return toForeignCallResult(header.toFields().map(toSingle));
642
653
  }
643
654
 
644
- async utilityGetNoteHashMembershipWitness(foreignBlockHash: ForeignCallSingle, foreignLeafValue: ForeignCallSingle) {
645
- const blockHash = BlockHash.fromString(foreignBlockHash);
646
- const leafValue = fromSingle(foreignLeafValue);
655
+ async utilityGetNoteHashMembershipWitness(
656
+ foreignAnchorBlockHash: ForeignCallSingle,
657
+ foreignNoteHash: ForeignCallSingle,
658
+ ) {
659
+ const blockHash = new BlockHash(fromSingle(foreignAnchorBlockHash));
660
+ const noteHash = fromSingle(foreignNoteHash);
647
661
 
648
- const witness = await this.handlerAsUtility().utilityGetNoteHashMembershipWitness(blockHash, leafValue);
662
+ const witness = await this.handlerAsUtility().utilityGetNoteHashMembershipWitness(blockHash, noteHash);
649
663
 
650
664
  if (!witness) {
651
- throw new Error(`Note hash ${leafValue} not found in the note hash tree at block ${blockHash.toString()}.`);
665
+ throw new Error(`Note hash ${noteHash} not found in the note hash tree at block ${blockHash.toString()}.`);
652
666
  }
653
667
  return toForeignCallResult(witness.toNoirRepresentation());
654
668
  }
655
669
 
656
- async utilityGetArchiveMembershipWitness(foreignBlockHash: ForeignCallSingle, foreignLeafValue: ForeignCallSingle) {
657
- const blockHash = BlockHash.fromString(foreignBlockHash);
658
- const leafValue = fromSingle(foreignLeafValue);
670
+ async utilityGetBlockHashMembershipWitness(
671
+ foreignAnchorBlockHash: ForeignCallSingle,
672
+ foreignBlockHash: ForeignCallSingle,
673
+ ) {
674
+ const anchorBlockHash = new BlockHash(fromSingle(foreignAnchorBlockHash));
675
+ const blockHash = new BlockHash(fromSingle(foreignBlockHash));
659
676
 
660
- const witness = await this.handlerAsUtility().utilityGetArchiveMembershipWitness(blockHash, leafValue);
677
+ const witness = await this.handlerAsUtility().utilityGetBlockHashMembershipWitness(anchorBlockHash, blockHash);
661
678
 
662
679
  if (!witness) {
663
- throw new Error(`Block hash ${leafValue} not found in the archive tree at block ${blockHash.toString()}.`);
680
+ throw new Error(
681
+ `Block hash ${blockHash.toString()} not found in the archive tree at anchor block ${anchorBlockHash.toString()}.`,
682
+ );
664
683
  }
665
684
  return toForeignCallResult(witness.toNoirRepresentation());
666
685
  }
@@ -669,7 +688,7 @@ export class RPCTranslator {
669
688
  foreignBlockHash: ForeignCallSingle,
670
689
  foreignNullifier: ForeignCallSingle,
671
690
  ) {
672
- const blockHash = BlockHash.fromString(foreignBlockHash);
691
+ const blockHash = new BlockHash(fromSingle(foreignBlockHash));
673
692
  const nullifier = fromSingle(foreignNullifier);
674
693
 
675
694
  const witness = await this.handlerAsUtility().utilityGetLowNullifierMembershipWitness(blockHash, nullifier);
@@ -830,7 +849,7 @@ export class RPCTranslator {
830
849
 
831
850
  // AVM opcodes
832
851
 
833
- avmOpcodeEmitUnencryptedLog(_foreignMessage: ForeignCallArray) {
852
+ avmOpcodeEmitPublicLog(_foreignMessage: ForeignCallArray) {
834
853
  // TODO(#8811): Implement
835
854
  return toForeignCallResult([]);
836
855
  }
@@ -59,6 +59,8 @@ export class TXEArchiver extends ArchiverDataSourceBase {
59
59
  if (!checkpointedBlock) {
60
60
  throw new Error(`L2Tips requested from TXE Archiver but no checkpointed block found for block number ${number}`);
61
61
  }
62
+ // TXE uses 1-block-per-checkpoint for testing simplicity, so we can use block number as checkpoint number.
63
+ // This uses the deprecated fromBlockNumber method intentionally for the TXE testing environment.
62
64
  const checkpoint = await this.store.getRangeOfCheckpoints(CheckpointNumber.fromBlockNumber(number), 1);
63
65
  if (checkpoint.length === 0) {
64
66
  throw new Error(`L2Tips requested from TXE Archiver but no checkpoint found for block number ${number}`);
@@ -6,6 +6,8 @@ import type {
6
6
  P2PBlockReceivedCallback,
7
7
  P2PCheckpointReceivedCallback,
8
8
  P2PConfig,
9
+ P2PDuplicateAttestationCallback,
10
+ P2PDuplicateProposalCallback,
9
11
  P2PSyncState,
10
12
  PeerId,
11
13
  ReqRespSubProtocol,
@@ -14,9 +16,9 @@ import type {
14
16
  StatusMessage,
15
17
  } from '@aztec/p2p';
16
18
  import type { EthAddress, L2BlockStreamEvent, L2Tips } from '@aztec/stdlib/block';
17
- import type { PeerInfo } from '@aztec/stdlib/interfaces/server';
19
+ import type { ITxProvider, PeerInfo } from '@aztec/stdlib/interfaces/server';
18
20
  import type { BlockProposal, CheckpointAttestation, CheckpointProposal } from '@aztec/stdlib/p2p';
19
- import type { Tx, TxHash } from '@aztec/stdlib/tx';
21
+ import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
20
22
 
21
23
  export class DummyP2P implements P2P {
22
24
  public validate(_txs: Tx[]): Promise<void> {
@@ -71,8 +73,8 @@ export class DummyP2P implements P2P {
71
73
  throw new Error('DummyP2P does not implement "sendTx"');
72
74
  }
73
75
 
74
- public deleteTxs(_txHashes: TxHash[]): Promise<void> {
75
- throw new Error('DummyP2P does not implement "deleteTxs"');
76
+ public handleFailedExecution(_txHashes: TxHash[]): Promise<void> {
77
+ throw new Error('DummyP2P does not implement "handleFailedExecution"');
76
78
  }
77
79
 
78
80
  public getTxByHashFromPool(_txHash: TxHash): Promise<Tx | undefined> {
@@ -97,6 +99,10 @@ export class DummyP2P implements P2P {
97
99
  throw new Error('DummyP2P does not implement "iteratePendingTxs"');
98
100
  }
99
101
 
102
+ public iterateEligiblePendingTxs(): AsyncIterableIterator<Tx> {
103
+ throw new Error('DummyP2P does not implement "iterateEligiblePendingTxs"');
104
+ }
105
+
100
106
  public getPendingTxCount(): Promise<number> {
101
107
  throw new Error('DummyP2P does not implement "getPendingTxCount"');
102
108
  }
@@ -125,6 +131,10 @@ export class DummyP2P implements P2P {
125
131
  throw new Error('DummyP2P does not implement "isP2PClient"');
126
132
  }
127
133
 
134
+ public getTxProvider(): ITxProvider {
135
+ throw new Error('DummyP2P does not implement "getTxProvider"');
136
+ }
137
+
128
138
  public getTxsByHash(_txHashes: TxHash[]): Promise<Tx[]> {
129
139
  throw new Error('DummyP2P does not implement "getTxsByHash"');
130
140
  }
@@ -133,8 +143,8 @@ export class DummyP2P implements P2P {
133
143
  throw new Error('DummyP2P does not implement "getCheckpointAttestationsForSlot"');
134
144
  }
135
145
 
136
- public addCheckpointAttestations(_attestations: CheckpointAttestation[]): Promise<void> {
137
- throw new Error('DummyP2P does not implement "addCheckpointAttestations"');
146
+ public addOwnCheckpointAttestations(_attestations: CheckpointAttestation[]): Promise<void> {
147
+ throw new Error('DummyP2P does not implement "addOwnCheckpointAttestations"');
138
148
  }
139
149
 
140
150
  public getL2BlockHash(_number: number): Promise<string | undefined> {
@@ -157,14 +167,6 @@ export class DummyP2P implements P2P {
157
167
  throw new Error('DummyP2P does not implement "sync"');
158
168
  }
159
169
 
160
- public requestTxsByHash(_txHashes: TxHash[]): Promise<Tx[]> {
161
- throw new Error('DummyP2P does not implement "requestTxsByHash"');
162
- }
163
-
164
- public getTxs(_filter: 'all' | 'pending' | 'mined'): Promise<Tx[]> {
165
- throw new Error('DummyP2P does not implement "getTxs"');
166
- }
167
-
168
170
  public getTxsByHashFromPool(_txHashes: TxHash[]): Promise<(Tx | undefined)[]> {
169
171
  throw new Error('DummyP2P does not implement "getTxsByHashFromPool"');
170
172
  }
@@ -173,10 +175,6 @@ export class DummyP2P implements P2P {
173
175
  throw new Error('DummyP2P does not implement "hasTxsInPool"');
174
176
  }
175
177
 
176
- public addTxsToPool(_txs: Tx[]): Promise<number> {
177
- throw new Error('DummyP2P does not implement "addTxs"');
178
- }
179
-
180
178
  public getSyncedLatestBlockNum(): Promise<number> {
181
179
  throw new Error('DummyP2P does not implement "getSyncedLatestBlockNum"');
182
180
  }
@@ -189,8 +187,12 @@ export class DummyP2P implements P2P {
189
187
  throw new Error('DummyP2P does not implement "getSyncedLatestSlot"');
190
188
  }
191
189
 
192
- markTxsAsNonEvictable(_: TxHash[]): Promise<void> {
193
- throw new Error('DummyP2P does not implement "markTxsAsNonEvictable".');
190
+ protectTxs(_txHashes: TxHash[], _blockHeader: BlockHeader): Promise<TxHash[]> {
191
+ throw new Error('DummyP2P does not implement "protectTxs".');
192
+ }
193
+
194
+ prepareForSlot(_slotNumber: SlotNumber): Promise<void> {
195
+ return Promise.resolve();
194
196
  }
195
197
 
196
198
  addReqRespSubProtocol(
@@ -206,4 +208,16 @@ export class DummyP2P implements P2P {
206
208
 
207
209
  //This is no-op
208
210
  public registerThisValidatorAddresses(_address: EthAddress[]): void {}
211
+
212
+ public registerDuplicateProposalCallback(_callback: P2PDuplicateProposalCallback): void {
213
+ throw new Error('DummyP2P does not implement "registerDuplicateProposalCallback"');
214
+ }
215
+
216
+ public registerDuplicateAttestationCallback(_callback: P2PDuplicateAttestationCallback): void {
217
+ throw new Error('DummyP2P does not implement "registerDuplicateAttestationCallback"');
218
+ }
219
+
220
+ public hasBlockProposalsForSlot(_slot: SlotNumber): Promise<boolean> {
221
+ throw new Error('DummyP2P does not implement "hasBlockProposalsForSlot"');
222
+ }
209
223
  }
@@ -3,8 +3,7 @@ import { TestCircuitVerifier } from '@aztec/bb-prover/test';
3
3
  import { CheckpointNumber } from '@aztec/foundation/branded-types';
4
4
  import { Fr } from '@aztec/foundation/curves/bn254';
5
5
  import { createLogger } from '@aztec/foundation/log';
6
- import type { AztecAsyncKVStore } from '@aztec/kv-store';
7
- import { AnchorBlockStore } from '@aztec/pxe/server';
6
+ import { type AnchorBlockStore, type ContractStore, ContractSyncService, type NoteStore } from '@aztec/pxe/server';
8
7
  import { L2Block } from '@aztec/stdlib/block';
9
8
  import { Checkpoint, L1PublishedData, PublishedCheckpoint } from '@aztec/stdlib/checkpoint';
10
9
  import type { AztecNode } from '@aztec/stdlib/interfaces/client';
@@ -26,13 +25,16 @@ export class TXEStateMachine {
26
25
  public synchronizer: TXESynchronizer,
27
26
  public archiver: TXEArchiver,
28
27
  public anchorBlockStore: AnchorBlockStore,
28
+ public contractSyncService: ContractSyncService,
29
29
  ) {}
30
30
 
31
- public static async create(db: AztecAsyncKVStore) {
32
- const archiver = new TXEArchiver(db);
31
+ public static async create(
32
+ archiver: TXEArchiver,
33
+ anchorBlockStore: AnchorBlockStore,
34
+ contractStore: ContractStore,
35
+ noteStore: NoteStore,
36
+ ) {
33
37
  const synchronizer = await TXESynchronizer.create();
34
- const anchorBlockStore = new AnchorBlockStore(db);
35
-
36
38
  const aztecNodeConfig = {} as AztecNodeConfig;
37
39
 
38
40
  const log = createLogger('txe_node');
@@ -48,6 +50,7 @@ export class TXEStateMachine {
48
50
  undefined,
49
51
  undefined,
50
52
  undefined,
53
+ undefined,
51
54
  VERSION,
52
55
  CHAIN_ID,
53
56
  new TXEGlobalVariablesBuilder(),
@@ -58,11 +61,21 @@ export class TXEStateMachine {
58
61
  log,
59
62
  );
60
63
 
61
- return new this(node, synchronizer, archiver, anchorBlockStore);
64
+ const contractSyncService = new ContractSyncService(
65
+ node,
66
+ contractStore,
67
+ noteStore,
68
+ createLogger('txe:contract_sync'),
69
+ );
70
+
71
+ return new this(node, synchronizer, archiver, anchorBlockStore, contractSyncService);
62
72
  }
63
73
 
64
74
  public async handleL2Block(block: L2Block) {
65
- // Create a checkpoint from the block manually
75
+ // Create a checkpoint from the block manually.
76
+ // TXE uses 1-block-per-checkpoint for testing simplicity, so we can use block number as checkpoint number.
77
+ // This uses the deprecated fromBlockNumber method intentionally for the TXE testing environment.
78
+ const checkpointNumber = CheckpointNumber.fromBlockNumber(block.number);
66
79
  const checkpoint = new Checkpoint(
67
80
  block.archive,
68
81
  CheckpointHeader.from({
@@ -79,7 +92,7 @@ export class TXEStateMachine {
79
92
  totalManaUsed: block.header.totalManaUsed,
80
93
  }),
81
94
  [block],
82
- CheckpointNumber.fromBlockNumber(block.number),
95
+ checkpointNumber,
83
96
  );
84
97
 
85
98
  const publishedCheckpoint = new PublishedCheckpoint(
@@ -91,6 +104,9 @@ export class TXEStateMachine {
91
104
  ),
92
105
  [],
93
106
  );
107
+ // Wipe contract sync cache when anchor block changes (mirrors BlockSynchronizer behavior)
108
+ this.contractSyncService.wipe();
109
+
94
110
  await Promise.all([
95
111
  this.synchronizer.handleL2Block(block),
96
112
  this.archiver.addCheckpoints([publishedCheckpoint], undefined),
@@ -1,6 +1,7 @@
1
1
  import type { EpochAndSlot, EpochCacheInterface, EpochCommitteeInfo, SlotTag } from '@aztec/epoch-cache';
2
2
  import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
3
3
  import { EthAddress } from '@aztec/foundation/eth-address';
4
+ import { EmptyL1RollupConstants, type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
4
5
 
5
6
  /**
6
7
  * Mock implementation of the EpochCacheInterface used to satisfy dependencies of AztecNodeService.
@@ -64,4 +65,8 @@ export class MockEpochCache implements EpochCacheInterface {
64
65
  filterInCommittee(_slot: SlotTag, _validators: EthAddress[]): Promise<EthAddress[]> {
65
66
  return Promise.resolve([]);
66
67
  }
68
+
69
+ getL1Constants(): L1RollupConstants {
70
+ return EmptyL1RollupConstants;
71
+ }
67
72
  }