@aztec/pxe 0.82.3 → 0.83.0

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 (37) hide show
  1. package/dest/config/package_info.js +1 -1
  2. package/dest/pxe_oracle_interface/pxe_oracle_interface.d.ts +15 -20
  3. package/dest/pxe_oracle_interface/pxe_oracle_interface.d.ts.map +1 -1
  4. package/dest/pxe_oracle_interface/pxe_oracle_interface.js +56 -103
  5. package/dest/pxe_service/pxe_service.d.ts +3 -2
  6. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  7. package/dest/pxe_service/pxe_service.js +26 -56
  8. package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts +9 -0
  9. package/dest/storage/capsule_data_provider/capsule_data_provider.d.ts.map +1 -1
  10. package/dest/storage/capsule_data_provider/capsule_data_provider.js +24 -0
  11. package/dest/storage/index.d.ts +1 -0
  12. package/dest/storage/index.d.ts.map +1 -1
  13. package/dest/storage/index.js +1 -0
  14. package/dest/storage/private_event_data_provider/private_event_data_provider.d.ts +37 -0
  15. package/dest/storage/private_event_data_provider/private_event_data_provider.d.ts.map +1 -0
  16. package/dest/storage/private_event_data_provider/private_event_data_provider.js +106 -0
  17. package/dest/storage/sync_data_provider/sync_data_provider.d.ts +1 -1
  18. package/dest/storage/sync_data_provider/sync_data_provider.d.ts.map +1 -1
  19. package/dest/storage/sync_data_provider/sync_data_provider.js +1 -1
  20. package/dest/storage/tagging_data_provider/tagging_data_provider.d.ts +4 -4
  21. package/dest/storage/tagging_data_provider/tagging_data_provider.d.ts.map +1 -1
  22. package/dest/storage/tagging_data_provider/tagging_data_provider.js +29 -12
  23. package/dest/synchronizer/synchronizer.d.ts +4 -6
  24. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  25. package/dest/synchronizer/synchronizer.js +11 -15
  26. package/dest/test/pxe_test_suite.js +1 -1
  27. package/package.json +15 -15
  28. package/src/config/package_info.ts +1 -1
  29. package/src/pxe_oracle_interface/pxe_oracle_interface.ts +113 -143
  30. package/src/pxe_service/pxe_service.ts +35 -76
  31. package/src/storage/capsule_data_provider/capsule_data_provider.ts +26 -0
  32. package/src/storage/index.ts +1 -0
  33. package/src/storage/private_event_data_provider/private_event_data_provider.ts +137 -0
  34. package/src/storage/sync_data_provider/sync_data_provider.ts +2 -2
  35. package/src/storage/tagging_data_provider/tagging_data_provider.ts +44 -13
  36. package/src/synchronizer/synchronizer.ts +11 -14
  37. package/src/test/pxe_test_suite.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import { L1_TO_L2_MSG_TREE_HEIGHT } from '@aztec/constants';
2
- import { Fr, type Point } from '@aztec/foundation/fields';
2
+ import { Fr } from '@aztec/foundation/fields';
3
3
  import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { SerialQueue } from '@aztec/foundation/queue';
5
5
  import { Timer } from '@aztec/foundation/timer';
@@ -20,6 +20,7 @@ import {
20
20
  FunctionCall,
21
21
  FunctionSelector,
22
22
  FunctionType,
23
+ decodeFromAbi,
23
24
  decodeFunctionSignature,
24
25
  encodeArguments,
25
26
  } from '@aztec/stdlib/abi';
@@ -36,7 +37,6 @@ import {
36
37
  getContractClassFromArtifact,
37
38
  } from '@aztec/stdlib/contract';
38
39
  import { SimulationError } from '@aztec/stdlib/errors';
39
- import { EventMetadata, L1EventPayload } from '@aztec/stdlib/event';
40
40
  import type { GasFees } from '@aztec/stdlib/gas';
41
41
  import { siloNullifier } from '@aztec/stdlib/hash';
42
42
  import type {
@@ -49,7 +49,6 @@ import type {
49
49
  PrivateKernelProver,
50
50
  } from '@aztec/stdlib/interfaces/client';
51
51
  import type { PrivateKernelExecutionProofOutput, PrivateKernelTailCircuitPublicInputs } from '@aztec/stdlib/kernel';
52
- import { computeAddressSecret } from '@aztec/stdlib/keys';
53
52
  import type { LogFilter } from '@aztec/stdlib/logs';
54
53
  import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
55
54
  import { type NotesFilter, UniqueNote } from '@aztec/stdlib/note';
@@ -82,6 +81,7 @@ import { AddressDataProvider } from '../storage/address_data_provider/address_da
82
81
  import { CapsuleDataProvider } from '../storage/capsule_data_provider/capsule_data_provider.js';
83
82
  import { ContractDataProvider } from '../storage/contract_data_provider/contract_data_provider.js';
84
83
  import { NoteDataProvider } from '../storage/note_data_provider/note_data_provider.js';
84
+ import { PrivateEventDataProvider } from '../storage/private_event_data_provider/private_event_data_provider.js';
85
85
  import { SyncDataProvider } from '../storage/sync_data_provider/sync_data_provider.js';
86
86
  import { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js';
87
87
  import { Synchronizer } from '../synchronizer/index.js';
@@ -101,6 +101,7 @@ export class PXEService implements PXE {
101
101
  private syncDataProvider: SyncDataProvider,
102
102
  private taggingDataProvider: TaggingDataProvider,
103
103
  private addressDataProvider: AddressDataProvider,
104
+ private privateEventDataProvider: PrivateEventDataProvider,
104
105
  private simulator: AcirSimulator,
105
106
  private packageVersion: string,
106
107
  private proverEnabled: boolean,
@@ -134,6 +135,7 @@ export class PXEService implements PXE {
134
135
  const packageVersion = getPackageInfo().version;
135
136
  const proverEnabled = !!config.proverEnabled;
136
137
  const addressDataProvider = new AddressDataProvider(store);
138
+ const privateEventDataProvider = new PrivateEventDataProvider(store);
137
139
  const contractDataProvider = new ContractDataProvider(store);
138
140
  const noteDataProvider = await NoteDataProvider.create(store);
139
141
  const syncDataProvider = new SyncDataProvider(store);
@@ -153,13 +155,13 @@ export class PXEService implements PXE {
153
155
  const pxeOracleInterface = new PXEOracleInterface(
154
156
  node,
155
157
  keyStore,
156
- simulationProvider,
157
158
  contractDataProvider,
158
159
  noteDataProvider,
159
160
  capsuleDataProvider,
160
161
  syncDataProvider,
161
162
  taggingDataProvider,
162
163
  addressDataProvider,
164
+ privateEventDataProvider,
163
165
  log,
164
166
  );
165
167
  const simulator = new AcirSimulator(pxeOracleInterface, simulationProvider);
@@ -175,6 +177,7 @@ export class PXEService implements PXE {
175
177
  syncDataProvider,
176
178
  taggingDataProvider,
177
179
  addressDataProvider,
180
+ privateEventDataProvider,
178
181
  simulator,
179
182
  packageVersion,
180
183
  proverEnabled,
@@ -188,7 +191,7 @@ export class PXEService implements PXE {
188
191
 
189
192
  await pxeService.#registerProtocolContracts();
190
193
  const info = await pxeService.getNodeInfo();
191
- log.info(`Started PXE connected to chain ${info.l1ChainId} version ${info.protocolVersion}`);
194
+ log.info(`Started PXE connected to chain ${info.l1ChainId} version ${info.rollupVersion}`);
192
195
  return pxeService;
193
196
  }
194
197
 
@@ -846,20 +849,19 @@ export class PXEService implements PXE {
846
849
  }
847
850
 
848
851
  public async getNodeInfo(): Promise<NodeInfo> {
849
- const [nodeVersion, protocolVersion, chainId, enr, contractAddresses, protocolContractAddresses] =
850
- await Promise.all([
851
- this.node.getNodeVersion(),
852
- this.node.getVersion(),
853
- this.node.getChainId(),
854
- this.node.getEncodedEnr(),
855
- this.node.getL1ContractAddresses(),
856
- this.node.getProtocolContractAddresses(),
857
- ]);
852
+ const [nodeVersion, rollupVersion, chainId, enr, contractAddresses, protocolContractAddresses] = await Promise.all([
853
+ this.node.getNodeVersion(),
854
+ this.node.getVersion(),
855
+ this.node.getChainId(),
856
+ this.node.getEncodedEnr(),
857
+ this.node.getL1ContractAddresses(),
858
+ this.node.getProtocolContractAddresses(),
859
+ ]);
858
860
 
859
861
  const nodeInfo: NodeInfo = {
860
862
  nodeVersion,
861
863
  l1ChainId: chainId,
862
- protocolVersion,
864
+ rollupVersion,
863
865
  enr,
864
866
  l1ContractAddresses: contractAddresses,
865
867
  protocolContractAddresses: protocolContractAddresses,
@@ -881,78 +883,35 @@ export class PXEService implements PXE {
881
883
  }
882
884
 
883
885
  public async getPrivateEvents<T>(
886
+ contractAddress: AztecAddress,
884
887
  eventMetadataDef: EventMetadataDefinition,
885
888
  from: number,
886
- limit: number,
887
- // TODO (#9272): Make this better, we should be able to only pass an address now
888
- vpks: Point[],
889
+ numBlocks: number,
890
+ recipients: AztecAddress[],
889
891
  ): Promise<T[]> {
890
- const eventMetadata = new EventMetadata<T>(eventMetadataDef);
891
- if (vpks.length === 0) {
892
- throw new Error('Tried to get encrypted events without supplying any viewing public keys');
892
+ if (recipients.length === 0) {
893
+ throw new Error('Recipients are required to get private events');
893
894
  }
894
895
 
895
- const blocks = await this.node.getBlocks(from, limit);
896
-
897
- const txEffects = blocks.flatMap(block => block.body.txEffects);
898
- const privateLogs = txEffects.flatMap(txEffect => txEffect.privateLogs);
896
+ this.log.verbose(`Getting private events for ${contractAddress.toString()} from ${from} to ${from + numBlocks}`);
899
897
 
900
- const vsks = await Promise.all(
901
- vpks.map(async vpk => {
902
- const [keyPrefix, account] = await this.keyStore.getKeyPrefixAndAccount(vpk);
903
- let secretKey = await this.keyStore.getMasterSecretKey(vpk);
904
- if (keyPrefix === 'iv') {
905
- const registeredAccount = (await this.getRegisteredAccounts()).find(completeAddress =>
906
- completeAddress.address.equals(account),
907
- );
908
- if (!registeredAccount) {
909
- throw new Error('No registered account');
910
- }
898
+ // TODO(#13113): This is a temporary hack to ensure that the notes are synced before getting the events.
899
+ await this.simulateUnconstrained('sync_notes', [], contractAddress);
911
900
 
912
- const preaddress = await registeredAccount.getPreaddress();
913
-
914
- secretKey = await computeAddressSecret(preaddress, secretKey);
915
- }
916
-
917
- return secretKey;
918
- }),
901
+ const events = await this.privateEventDataProvider.getPrivateEvents(
902
+ contractAddress,
903
+ from,
904
+ numBlocks,
905
+ recipients,
906
+ eventMetadataDef.eventSelector,
919
907
  );
920
908
 
921
- const visibleEvents = (
922
- await Promise.all(
923
- privateLogs.map(async log => {
924
- for (const sk of vsks) {
925
- // TODO: Verify that the first field of the log is the tag siloed with contract address.
926
- // Or use tags to query logs, like we do with notes.
927
- const decryptedEvent = await L1EventPayload.decryptAsIncoming(log, sk);
928
- if (decryptedEvent !== undefined) {
929
- return [decryptedEvent];
930
- }
931
- }
932
-
933
- return [];
934
- }),
935
- )
936
- ).flat();
937
-
938
- const decodedEvents = visibleEvents
939
- .map(visibleEvent => {
940
- if (visibleEvent === undefined) {
941
- return undefined;
942
- }
943
- if (!visibleEvent.eventTypeId.equals(eventMetadata.eventSelector)) {
944
- return undefined;
945
- }
946
-
947
- return eventMetadata.decode(visibleEvent);
948
- })
949
- .filter(visibleEvent => visibleEvent !== undefined) as T[];
909
+ const decodedEvents = events.map((event: Fr[]): T => decodeFromAbi([eventMetadataDef.abiType], event) as T);
950
910
 
951
911
  return decodedEvents;
952
912
  }
953
913
 
954
914
  async getPublicEvents<T>(eventMetadataDef: EventMetadataDefinition, from: number, limit: number): Promise<T[]> {
955
- const eventMetadata = new EventMetadata<T>(eventMetadataDef);
956
915
  const { logs } = await this.node.getPublicLogs({
957
916
  fromBlock: from,
958
917
  toBlock: from + limit,
@@ -961,10 +920,10 @@ export class PXEService implements PXE {
961
920
  const decodedEvents = logs
962
921
  .map(log => {
963
922
  // +1 for the event selector
964
- const expectedLength = eventMetadata.fieldNames.length + 1;
923
+ const expectedLength = eventMetadataDef.fieldNames.length + 1;
965
924
  const logFields = log.log.log.slice(0, expectedLength);
966
925
  // We are assuming here that event logs are the last 4 bytes of the event. This is not enshrined but is a function of aztec.nr raw log emission.
967
- if (!EventSelector.fromField(logFields[logFields.length - 1]).equals(eventMetadata.eventSelector)) {
926
+ if (!EventSelector.fromField(logFields[logFields.length - 1]).equals(eventMetadataDef.eventSelector)) {
968
927
  return undefined;
969
928
  }
970
929
  // If any of the remaining fields, are non-zero, the payload does match expected:
@@ -974,7 +933,7 @@ export class PXEService implements PXE {
974
933
  );
975
934
  }
976
935
 
977
- return eventMetadata.decode(log.log);
936
+ return decodeFromAbi([eventMetadataDef.abiType], log.log.log) as T;
978
937
  })
979
938
  .filter(log => log !== undefined) as T[];
980
939
 
@@ -67,6 +67,32 @@ export class CapsuleDataProvider implements DataProvider {
67
67
  }
68
68
  }
69
69
 
70
+ /**
71
+ * Appends multiple capsules to a capsule array stored at the base slot.
72
+ * The array length is stored at the base slot, and elements are stored in consecutive slots after it.
73
+ * All operations are performed in a single transaction.
74
+ * @param contractAddress - The contract address that owns the capsule array
75
+ * @param baseSlot - The slot where the array length is stored
76
+ * @param capsules - Array of capsule data to append
77
+ */
78
+ appendToCapsuleArray(contractAddress: AztecAddress, baseSlot: Fr, capsules: Fr[][]): Promise<void> {
79
+ return this.#store.transactionAsync(async () => {
80
+ // Load current length, defaulting to 0 if not found
81
+ const lengthData = await this.loadCapsule(contractAddress, baseSlot);
82
+ const currentLength = lengthData ? lengthData[0].toBigInt() : 0n;
83
+
84
+ // Store each capsule at consecutive slots after baseSlot + 1 + currentLength
85
+ for (let i = 0; i < capsules.length; i++) {
86
+ const nextSlot = baseSlot.add(new Fr(1)).add(new Fr(currentLength + BigInt(i)));
87
+ await this.storeCapsule(contractAddress, nextSlot, capsules[i]);
88
+ }
89
+
90
+ // Update length to include all new capsules
91
+ const newLength = currentLength + BigInt(capsules.length);
92
+ await this.storeCapsule(contractAddress, baseSlot, [new Fr(newLength)]);
93
+ });
94
+ }
95
+
70
96
  public async getSize() {
71
97
  return (await toArray(this.#capsules.valuesAsync())).reduce(
72
98
  (sum, value) => sum + value.length * Fr.SIZE_IN_BYTES,
@@ -6,3 +6,4 @@ export * from './sync_data_provider/index.js';
6
6
  export * from './tagging_data_provider/index.js';
7
7
  export * from './data_provider.js';
8
8
  export * from './metadata.js';
9
+ export * from './private_event_data_provider/private_event_data_provider.js';
@@ -0,0 +1,137 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
4
+ import type { AztecAsyncArray, AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
5
+ import type { EventSelector } from '@aztec/stdlib/abi';
6
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
7
+ import type { TxHash } from '@aztec/stdlib/tx';
8
+
9
+ import type { DataProvider } from '../data_provider.js';
10
+
11
+ interface PrivateEventEntry {
12
+ logContent: Buffer;
13
+ blockNumber: number;
14
+ logIndexInTx: number;
15
+ }
16
+
17
+ /**
18
+ * Stores decrypted private event logs.
19
+ */
20
+ export class PrivateEventDataProvider implements DataProvider {
21
+ #store: AztecAsyncKVStore;
22
+ /** Array storing the actual private event log entries containing the log content and block number */
23
+ #eventLogs: AztecAsyncArray<PrivateEventEntry>;
24
+ /** Map from contract_address_recipient_eventSelector to array of indices into #eventLogs for efficient lookup */
25
+ #eventLogIndex: AztecAsyncMap<string, number[]>;
26
+ /**
27
+ * Map from txHash_logIndexInTx to boolean indicating if log has been seen.
28
+ * @dev A single transaction can have multiple logs.
29
+ */
30
+ #seenLogs: AztecAsyncMap<string, boolean>;
31
+
32
+ logger = createLogger('private_event_data_provider');
33
+
34
+ constructor(store: AztecAsyncKVStore) {
35
+ this.#store = store;
36
+ this.#eventLogs = this.#store.openArray('private_event_logs');
37
+ this.#eventLogIndex = this.#store.openMap('private_event_log_index');
38
+ this.#seenLogs = this.#store.openMap('seen_logs');
39
+ }
40
+
41
+ /**
42
+ * Store a private event log.
43
+ * @param contractAddress - The address of the contract that emitted the event.
44
+ * @param recipient - The recipient of the event.
45
+ * @param eventSelector - The event selector of the event.
46
+ * @param logContent - The content of the event.
47
+ * @param txHash - The transaction hash of the event log.
48
+ * @param logIndexInTx - The index of the log within the transaction.
49
+ * @param blockNumber - The block number in which the event was emitted.
50
+ */
51
+ storePrivateEventLog(
52
+ contractAddress: AztecAddress,
53
+ recipient: AztecAddress,
54
+ eventSelector: EventSelector,
55
+ logContent: Fr[],
56
+ txHash: TxHash,
57
+ logIndexInTx: number,
58
+ blockNumber: number,
59
+ ): Promise<void> {
60
+ return this.#store.transactionAsync(async () => {
61
+ const key = `${contractAddress.toString()}_${recipient.toString()}_${eventSelector.toString()}`;
62
+
63
+ // We identify a unique log by its transaction hash and index within that transaction
64
+ const txKey = `${txHash.toString()}_${logIndexInTx}`;
65
+
66
+ // Check if this exact log has already been stored
67
+ const hasBeenSeen = await this.#seenLogs.getAsync(txKey);
68
+ if (hasBeenSeen) {
69
+ this.logger.verbose('Ignoring duplicate event log', { txHash: txHash.toString(), logIndexInTx });
70
+ return;
71
+ }
72
+
73
+ this.logger.verbose('storing private event log', { contractAddress, recipient, logContent, blockNumber });
74
+
75
+ const index = await this.#eventLogs.lengthAsync();
76
+ await this.#eventLogs.push({ logContent: serializeToBuffer(logContent), blockNumber, logIndexInTx });
77
+
78
+ const existingIndices = (await this.#eventLogIndex.getAsync(key)) || [];
79
+ await this.#eventLogIndex.set(key, [...existingIndices, index]);
80
+
81
+ // Mark this log as seen
82
+ await this.#seenLogs.set(txKey, true);
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Returns the private events given search parameters.
88
+ * @param contractAddress - The address of the contract to get events from.
89
+ * @param from - The block number to search from.
90
+ * @param numBlocks - The amount of blocks to search.
91
+ * @param recipients - The addresses that decrypted the logs.
92
+ * @param eventSelector - The event selector to filter by.
93
+ * @returns - The event log contents.
94
+ */
95
+ public async getPrivateEvents(
96
+ contractAddress: AztecAddress,
97
+ from: number,
98
+ numBlocks: number,
99
+ recipients: AztecAddress[],
100
+ eventSelector: EventSelector,
101
+ ): Promise<Fr[][]> {
102
+ const events: Array<{ logContent: Fr[]; blockNumber: number; logIndexInTx: number }> = [];
103
+
104
+ for (const recipient of recipients) {
105
+ const key = `${contractAddress.toString()}_${recipient.toString()}_${eventSelector.toString()}`;
106
+ const indices = (await this.#eventLogIndex.getAsync(key)) || [];
107
+
108
+ for (const index of indices) {
109
+ const entry = await this.#eventLogs.atAsync(index);
110
+ if (!entry || entry.blockNumber < from || entry.blockNumber >= from + numBlocks) {
111
+ continue;
112
+ }
113
+
114
+ // Convert buffer back to Fr array
115
+ const reader = BufferReader.asReader(entry.logContent);
116
+ const numFields = entry.logContent.length / Fr.SIZE_IN_BYTES;
117
+ const logContent = reader.readArray(numFields, Fr);
118
+
119
+ events.push({ logContent, blockNumber: entry.blockNumber, logIndexInTx: entry.logIndexInTx });
120
+ }
121
+ }
122
+
123
+ // Sort by block number first, then by logIndexInTx (note that we currently don't order by txs within a block)
124
+ events.sort((a, b) => {
125
+ if (a.blockNumber !== b.blockNumber) {
126
+ return a.blockNumber - b.blockNumber;
127
+ }
128
+ return a.logIndexInTx - b.logIndexInTx;
129
+ });
130
+
131
+ return events.map(e => e.logContent);
132
+ }
133
+
134
+ getSize(): Promise<number> {
135
+ return this.#eventLogs.lengthAsync();
136
+ }
137
+ }
@@ -16,10 +16,10 @@ export class SyncDataProvider implements DataProvider {
16
16
  await this.#synchronizedHeader.set(header.toBuffer());
17
17
  }
18
18
 
19
- async getBlockNumber(): Promise<number | undefined> {
19
+ async getBlockNumber(): Promise<number> {
20
20
  const headerBuffer = await this.#synchronizedHeader.getAsync();
21
21
  if (!headerBuffer) {
22
- return undefined;
22
+ throw new Error(`Trying to get block number with a not-yet-synchronized PXE - this should never happen`);
23
23
  }
24
24
 
25
25
  return Number(BlockHeader.fromBuffer(headerBuffer).globalVariables.blockNumber.toBigInt());
@@ -23,32 +23,63 @@ export class TaggingDataProvider {
23
23
  this.#taggingSecretIndexesForRecipients = this.#store.openMap('tagging_secret_indexes_for_recipients');
24
24
  }
25
25
 
26
- async setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
27
- await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders);
26
+ setTaggingSecretsIndexesAsSender(indexedSecrets: IndexedTaggingSecret[], sender: AztecAddress) {
27
+ return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForSenders, sender);
28
28
  }
29
29
 
30
- async setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[]): Promise<void> {
31
- await this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients);
30
+ setTaggingSecretsIndexesAsRecipient(indexedSecrets: IndexedTaggingSecret[], recipient: AztecAddress) {
31
+ return this.#setTaggingSecretsIndexes(indexedSecrets, this.#taggingSecretIndexesForRecipients, recipient);
32
32
  }
33
33
 
34
- async #setTaggingSecretsIndexes(indexedSecrets: IndexedTaggingSecret[], storageMap: AztecAsyncMap<string, number>) {
35
- await Promise.all(
34
+ /**
35
+ * Sets the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address.
36
+ * @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated
37
+ * but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves
38
+ * uni-directional.
39
+ * @param indexedSecrets - The app tagging secrets and indexes to set.
40
+ * @param storageMap - The storage map to set the indexes in.
41
+ * @param inDirectionOf - The address that the secrets are in the direction of.
42
+ */
43
+ #setTaggingSecretsIndexes(
44
+ indexedSecrets: IndexedTaggingSecret[],
45
+ storageMap: AztecAsyncMap<string, number>,
46
+ inDirectionOf: AztecAddress,
47
+ ) {
48
+ return Promise.all(
36
49
  indexedSecrets.map(indexedSecret =>
37
- storageMap.set(indexedSecret.appTaggingSecret.toString(), indexedSecret.index),
50
+ storageMap.set(`${indexedSecret.appTaggingSecret.toString()}_${inDirectionOf.toString()}`, indexedSecret.index),
38
51
  ),
39
52
  );
40
53
  }
41
54
 
42
- async getTaggingSecretsIndexesAsRecipient(appTaggingSecrets: Fr[]) {
43
- return await this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForRecipients);
55
+ getTaggingSecretsIndexesAsRecipient(appTaggingSecrets: Fr[], recipient: AztecAddress) {
56
+ return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForRecipients, recipient);
44
57
  }
45
58
 
46
- async getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[]) {
47
- return await this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders);
59
+ getTaggingSecretsIndexesAsSender(appTaggingSecrets: Fr[], sender: AztecAddress) {
60
+ return this.#getTaggingSecretsIndexes(appTaggingSecrets, this.#taggingSecretIndexesForSenders, sender);
48
61
  }
49
62
 
50
- #getTaggingSecretsIndexes(appTaggingSecrets: Fr[], storageMap: AztecAsyncMap<string, number>): Promise<number[]> {
51
- return Promise.all(appTaggingSecrets.map(async secret => (await storageMap.getAsync(`${secret.toString()}`)) ?? 0));
63
+ /**
64
+ * Returns the indexes of the tagging secrets for the given app tagging secrets in the direction of the given address.
65
+ * @dev We need to specify the direction because app tagging secrets are direction-less due to the way they are generated
66
+ * but we need to guarantee that the index is stored under a uni-directional key because the tags are themselves
67
+ * uni-directional.
68
+ * @param appTaggingSecrets - The app tagging secrets to get the indexes for.
69
+ * @param storageMap - The storage map to get the indexes from.
70
+ * @param inDirectionOf - The address that the secrets are in the direction of.
71
+ * @returns The indexes of the tagging secrets.
72
+ */
73
+ #getTaggingSecretsIndexes(
74
+ appTaggingSecrets: Fr[],
75
+ storageMap: AztecAsyncMap<string, number>,
76
+ inDirectionOf: AztecAddress,
77
+ ): Promise<number[]> {
78
+ return Promise.all(
79
+ appTaggingSecrets.map(
80
+ async secret => (await storageMap.getAsync(`${secret.toString()}_${inDirectionOf.toString()}`)) ?? 0,
81
+ ),
82
+ );
52
83
  }
53
84
 
54
85
  resetNoteSyncData(): Promise<void> {
@@ -1,4 +1,3 @@
1
- import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants';
2
1
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
2
  import type { L2TipsKVStore } from '@aztec/kv-store/stores';
4
3
  import { L2BlockStream, type L2BlockStreamEvent, type L2BlockStreamEventHandler } from '@aztec/stdlib/block';
@@ -10,13 +9,11 @@ import type { SyncDataProvider } from '../storage/sync_data_provider/sync_data_p
10
9
  import type { TaggingDataProvider } from '../storage/tagging_data_provider/tagging_data_provider.js';
11
10
 
12
11
  /**
13
- * The Synchronizer class manages the synchronization with the aztec node, allowing PXE to retrieve the
14
- * latest block header and handle reorgs.
15
- * It provides methods to trigger a sync and get the block number we are syncec to
16
- * details, and fetch transactions by hash.
12
+ * The Synchronizer class orchestrates synchronization between the PXE and Aztec node, maintaining an up-to-date
13
+ * view of the L2 chain state. It handles block header retrieval, chain reorganizations, and provides an interface
14
+ * for querying sync status.
17
15
  */
18
16
  export class Synchronizer implements L2BlockStreamEventHandler {
19
- private initialSyncBlockNumber = INITIAL_L2_BLOCK_NUM - 1;
20
17
  private log: Logger;
21
18
  private isSyncing: Promise<void> | undefined;
22
19
  protected readonly blockStream: L2BlockStream;
@@ -59,18 +56,18 @@ export class Synchronizer implements L2BlockStreamEventHandler {
59
56
  break;
60
57
  }
61
58
  case 'chain-pruned': {
62
- this.log.warn(`Pruning data after block ${event.blockNumber} due to reorg`);
59
+ this.log.warn(`Pruning data after block ${event.block.number} due to reorg`);
63
60
  // We first unnullify and then remove so that unnullified notes that were created after the block number end up deleted.
64
61
  const lastSynchedBlockNumber = await this.syncDataProvider.getBlockNumber();
65
- await this.noteDataProvider.unnullifyNotesAfter(event.blockNumber, lastSynchedBlockNumber);
66
- await this.noteDataProvider.removeNotesAfter(event.blockNumber);
62
+ await this.noteDataProvider.unnullifyNotesAfter(event.block.number, lastSynchedBlockNumber);
63
+ await this.noteDataProvider.removeNotesAfter(event.block.number);
67
64
  // Remove all note tagging indexes to force a full resync. This is suboptimal, but unless we track the
68
65
  // block number in which each index is used it's all we can do.
69
66
  await this.taggingDataProvider.resetNoteSyncData();
70
67
  // Update the header to the last block.
71
- const newHeader = await this.node.getBlockHeader(event.blockNumber);
68
+ const newHeader = await this.node.getBlockHeader(event.block.number);
72
69
  if (!newHeader) {
73
- this.log.error(`Block header not found for block number ${event.blockNumber} during chain prune`);
70
+ this.log.error(`Block header not found for block number ${event.block.number} during chain prune`);
74
71
  } else {
75
72
  await this.syncDataProvider.setHeader(newHeader);
76
73
  }
@@ -80,7 +77,7 @@ export class Synchronizer implements L2BlockStreamEventHandler {
80
77
  }
81
78
 
82
79
  /**
83
- * Syncs PXE and the node by dowloading the metadata of the latest blocks, allowing simulations to use
80
+ * Syncs PXE and the node by downloading the metadata of the latest blocks, allowing simulations to use
84
81
  * recent data (e.g. notes), and handling any reorgs that might have occurred.
85
82
  */
86
83
  public async sync() {
@@ -115,7 +112,7 @@ export class Synchronizer implements L2BlockStreamEventHandler {
115
112
  await this.blockStream.sync();
116
113
  }
117
114
 
118
- public async getSynchedBlockNumber() {
119
- return (await this.syncDataProvider.getBlockNumber()) ?? this.initialSyncBlockNumber;
115
+ public getSynchedBlockNumber() {
116
+ return this.syncDataProvider.getBlockNumber();
120
117
  }
121
118
  }
@@ -105,7 +105,7 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
105
105
 
106
106
  it('successfully gets node info', async () => {
107
107
  const nodeInfo = await pxe.getNodeInfo();
108
- expect(typeof nodeInfo.protocolVersion).toEqual('number');
108
+ expect(typeof nodeInfo.rollupVersion).toEqual('number');
109
109
  expect(typeof nodeInfo.l1ChainId).toEqual('number');
110
110
  expect(nodeInfo.l1ContractAddresses.rollupAddress.toString()).toMatch(/0x[a-fA-F0-9]+/);
111
111
  });