@aztec/pxe 0.42.0 → 0.43.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 (47) hide show
  1. package/dest/database/deferred_note_dao.d.ts +2 -2
  2. package/dest/database/deferred_note_dao.d.ts.map +1 -1
  3. package/dest/database/deferred_note_dao.js +2 -2
  4. package/dest/database/incoming_note_dao.d.ts +73 -0
  5. package/dest/database/incoming_note_dao.d.ts.map +1 -0
  6. package/dest/database/incoming_note_dao.js +92 -0
  7. package/dest/database/kv_pxe_database.d.ts +10 -7
  8. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  9. package/dest/database/kv_pxe_database.js +149 -78
  10. package/dest/database/{note_dao.d.ts → outgoing_note_dao.d.ts} +7 -12
  11. package/dest/database/outgoing_note_dao.d.ts.map +1 -0
  12. package/dest/database/outgoing_note_dao.js +83 -0
  13. package/dest/database/pxe_database.d.ts +21 -9
  14. package/dest/database/pxe_database.d.ts.map +1 -1
  15. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  16. package/dest/database/pxe_database_test_suite.js +71 -24
  17. package/dest/kernel_oracle/index.d.ts +1 -1
  18. package/dest/note_processor/note_processor.d.ts +23 -20
  19. package/dest/note_processor/note_processor.d.ts.map +1 -1
  20. package/dest/note_processor/note_processor.js +123 -76
  21. package/dest/note_processor/produce_note_dao.d.ts +13 -4
  22. package/dest/note_processor/produce_note_dao.d.ts.map +1 -1
  23. package/dest/note_processor/produce_note_dao.js +88 -31
  24. package/dest/pxe_service/create_pxe_service.d.ts.map +1 -1
  25. package/dest/pxe_service/create_pxe_service.js +3 -1
  26. package/dest/pxe_service/pxe_service.d.ts +9 -4
  27. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  28. package/dest/pxe_service/pxe_service.js +106 -28
  29. package/dest/simulator_oracle/index.js +2 -2
  30. package/dest/synchronizer/synchronizer.d.ts +2 -2
  31. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  32. package/dest/synchronizer/synchronizer.js +37 -36
  33. package/package.json +14 -14
  34. package/src/database/deferred_note_dao.ts +1 -1
  35. package/src/database/{note_dao.ts → incoming_note_dao.ts} +10 -7
  36. package/src/database/kv_pxe_database.ts +127 -29
  37. package/src/database/outgoing_note_dao.ts +90 -0
  38. package/src/database/pxe_database.ts +23 -9
  39. package/src/database/pxe_database_test_suite.ts +93 -29
  40. package/src/note_processor/note_processor.ts +191 -121
  41. package/src/note_processor/produce_note_dao.ts +164 -50
  42. package/src/pxe_service/create_pxe_service.ts +2 -0
  43. package/src/pxe_service/pxe_service.ts +170 -42
  44. package/src/simulator_oracle/index.ts +1 -1
  45. package/src/synchronizer/synchronizer.ts +48 -52
  46. package/dest/database/note_dao.d.ts.map +0 -1
  47. package/dest/database/note_dao.js +0 -89
@@ -1,9 +1,12 @@
1
1
  import { type L1NotePayload, type TxHash } from '@aztec/circuit-types';
2
2
  import { Fr, type PublicKey } from '@aztec/circuits.js';
3
3
  import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
4
- import { type AcirSimulator } from '@aztec/simulator';
4
+ import { type Logger } from '@aztec/foundation/log';
5
+ import { type AcirSimulator, ContractNotFoundError } from '@aztec/simulator';
5
6
 
6
- import { NoteDao } from '../database/note_dao.js';
7
+ import { DeferredNoteDao } from '../database/deferred_note_dao.js';
8
+ import { IncomingNoteDao } from '../database/incoming_note_dao.js';
9
+ import { OutgoingNoteDao } from '../database/outgoing_note_dao.js';
7
10
 
8
11
  /**
9
12
  * Decodes a note from a transaction that we know was intended for us.
@@ -11,96 +14,207 @@ import { NoteDao } from '../database/note_dao.js';
11
14
  * Accepts a set of excluded indices, which are indices that have been assigned a note in the same tx.
12
15
  * Inserts the index of the note into the excludedIndices set if the note is successfully decoded.
13
16
  *
14
- * @param publicKey - The public counterpart to the private key to be used in note decryption.
17
+ * @param ivpkM - The public counterpart to the secret key to be used in the decryption of incoming note logs.
18
+ * @param ovpkM - The public counterpart to the secret key to be used in the decryption of outgoing note logs.
15
19
  * @param payload - An instance of l1NotePayload.
16
20
  * @param txHash - The hash of the transaction that created the note. Equivalent to the first nullifier of the transaction.
17
21
  * @param newNoteHashes - New note hashes in this transaction, one of which belongs to this note.
18
22
  * @param dataStartIndexForTx - The next available leaf index for the note hash tree for this transaction.
19
23
  * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same l1NotePayload, we need to find a different index for each replicate.
20
24
  * @param simulator - An instance of AcirSimulator.
21
- * @returns an instance of NoteDao, or throws. inserts the index of the note into the excludedIndices set.
25
+ * @returns An object containing the incoming, outgoing, and deferred notes.
22
26
  */
23
- export async function produceNoteDao(
27
+ export async function produceNoteDaos(
24
28
  simulator: AcirSimulator,
25
- publicKey: PublicKey,
29
+ ivpkM: PublicKey | undefined,
30
+ ovpkM: PublicKey | undefined,
26
31
  payload: L1NotePayload,
27
32
  txHash: TxHash,
28
33
  newNoteHashes: Fr[],
29
34
  dataStartIndexForTx: number,
30
35
  excludedIndices: Set<number>,
31
- ): Promise<NoteDao> {
32
- const { commitmentIndex, nonce, innerNoteHash, siloedNullifier } = await findNoteIndexAndNullifier(
33
- simulator,
34
- newNoteHashes,
35
- txHash,
36
- payload,
37
- excludedIndices,
38
- );
39
- const index = BigInt(dataStartIndexForTx + commitmentIndex);
40
- excludedIndices?.add(commitmentIndex);
41
- return new NoteDao(
42
- payload.note,
43
- payload.contractAddress,
44
- payload.storageSlot,
45
- payload.noteTypeId,
46
- txHash,
47
- nonce,
48
- innerNoteHash,
49
- siloedNullifier,
50
- index,
51
- publicKey,
52
- );
36
+ log: Logger,
37
+ ): Promise<{
38
+ incomingNote: IncomingNoteDao | undefined;
39
+ outgoingNote: OutgoingNoteDao | undefined;
40
+ incomingDeferredNote: DeferredNoteDao | undefined;
41
+ outgoingDeferredNote: DeferredNoteDao | undefined;
42
+ }> {
43
+ if (!ivpkM && !ovpkM) {
44
+ throw new Error('Both ivpkM and ovpkM are undefined. Cannot create note.');
45
+ }
46
+
47
+ let incomingNote: IncomingNoteDao | undefined;
48
+ let outgoingNote: OutgoingNoteDao | undefined;
49
+ let incomingDeferredNote: DeferredNoteDao | undefined;
50
+ let outgoingDeferredNote: DeferredNoteDao | undefined;
51
+
52
+ try {
53
+ if (ivpkM) {
54
+ const { noteHashIndex, nonce, innerNoteHash, siloedNullifier } = await findNoteIndexAndNullifier(
55
+ simulator,
56
+ newNoteHashes,
57
+ txHash,
58
+ payload,
59
+ excludedIndices,
60
+ true, // For incoming we compute a nullifier (recipient of incoming is the party that nullifies).
61
+ );
62
+ const index = BigInt(dataStartIndexForTx + noteHashIndex);
63
+ excludedIndices?.add(noteHashIndex);
64
+
65
+ incomingNote = new IncomingNoteDao(
66
+ payload.note,
67
+ payload.contractAddress,
68
+ payload.storageSlot,
69
+ payload.noteTypeId,
70
+ txHash,
71
+ nonce,
72
+ innerNoteHash,
73
+ siloedNullifier,
74
+ index,
75
+ ivpkM,
76
+ );
77
+ }
78
+ } catch (e) {
79
+ if (e instanceof ContractNotFoundError) {
80
+ log.warn(e.message);
81
+
82
+ if (ivpkM) {
83
+ incomingDeferredNote = new DeferredNoteDao(
84
+ ivpkM,
85
+ payload.note,
86
+ payload.contractAddress,
87
+ payload.storageSlot,
88
+ payload.noteTypeId,
89
+ txHash,
90
+ newNoteHashes,
91
+ dataStartIndexForTx,
92
+ );
93
+ }
94
+ } else {
95
+ log.error(`Could not process note because of "${e}". Discarding note...`);
96
+ }
97
+ }
98
+
99
+ try {
100
+ if (ovpkM) {
101
+ if (incomingNote) {
102
+ // Incoming note is defined meaning that this PXE has both the incoming and outgoing keys. We can skip computing
103
+ // note hash and note index since we already have them in the incoming note.
104
+ outgoingNote = new OutgoingNoteDao(
105
+ payload.note,
106
+ payload.contractAddress,
107
+ payload.storageSlot,
108
+ payload.noteTypeId,
109
+ txHash,
110
+ incomingNote.nonce,
111
+ incomingNote.innerNoteHash,
112
+ incomingNote.index,
113
+ ovpkM,
114
+ );
115
+ } else {
116
+ const { noteHashIndex, nonce, innerNoteHash } = await findNoteIndexAndNullifier(
117
+ simulator,
118
+ newNoteHashes,
119
+ txHash,
120
+ payload,
121
+ excludedIndices,
122
+ false, // For outgoing we do not compute a nullifier.
123
+ );
124
+ const index = BigInt(dataStartIndexForTx + noteHashIndex);
125
+ excludedIndices?.add(noteHashIndex);
126
+ outgoingNote = new OutgoingNoteDao(
127
+ payload.note,
128
+ payload.contractAddress,
129
+ payload.storageSlot,
130
+ payload.noteTypeId,
131
+ txHash,
132
+ nonce,
133
+ innerNoteHash,
134
+ index,
135
+ ovpkM,
136
+ );
137
+ }
138
+ }
139
+ } catch (e) {
140
+ if (e instanceof ContractNotFoundError) {
141
+ log.warn(e.message);
142
+
143
+ if (ovpkM) {
144
+ outgoingDeferredNote = new DeferredNoteDao(
145
+ ovpkM,
146
+ payload.note,
147
+ payload.contractAddress,
148
+ payload.storageSlot,
149
+ payload.noteTypeId,
150
+ txHash,
151
+ newNoteHashes,
152
+ dataStartIndexForTx,
153
+ );
154
+ }
155
+ } else {
156
+ log.error(`Could not process note because of "${e}". Discarding note...`);
157
+ }
158
+ }
159
+
160
+ return {
161
+ incomingNote,
162
+ outgoingNote,
163
+ incomingDeferredNote,
164
+ outgoingDeferredNote,
165
+ };
53
166
  }
54
167
 
55
168
  /**
56
- * Find the index of the note in the note hash tree by computing the note hash with different nonce and see which
57
- * commitment for the current tx matches this value.
58
- * Compute a nullifier for a given l1NotePayload.
59
- * The nullifier is calculated using the private key of the account,
60
- * contract address, and the note associated with the l1NotePayload.
61
- * This method assists in identifying spent commitments in the private state.
62
- * @param commitments - Commitments in the tx. One of them should be the note's commitment.
63
- * @param txHash - First nullifier in the tx.
64
- * @param l1NotePayload - An instance of l1NotePayload.
169
+ * Finds nonce, index, inner hash and siloed nullifier for a given note.
170
+ * @dev Finds the index in the note hash tree by computing the note hash with different nonce and see which hash for
171
+ * the current tx matches this value.
172
+ * @remarks This method assists in identifying spent notes in the note hash tree.
173
+ * @param noteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
174
+ * @param txHash - Hash of a tx the note was emitted in.
175
+ * @param l1NotePayload - The note payload.
65
176
  * @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
66
177
  * l1NotePayload. We need to find a different index for each replicate.
67
- * @returns Information for a decrypted note, including the index of its commitment, nonce, inner note
68
- * hash, and the siloed nullifier. Throw if cannot find the nonce for the note.
178
+ * @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
179
+ * @returns Nonce, index, inner hash and siloed nullifier for a given note.
180
+ * @throws If cannot find the nonce for the note.
69
181
  */
70
182
  async function findNoteIndexAndNullifier(
71
183
  simulator: AcirSimulator,
72
- commitments: Fr[],
184
+ noteHashes: Fr[],
73
185
  txHash: TxHash,
74
186
  { contractAddress, storageSlot, noteTypeId, note }: L1NotePayload,
75
187
  excludedIndices: Set<number>,
188
+ computeNullifier: boolean,
76
189
  ) {
77
- let commitmentIndex = 0;
190
+ let noteHashIndex = 0;
78
191
  let nonce: Fr | undefined;
79
192
  let innerNoteHash: Fr | undefined;
80
193
  let siloedNoteHash: Fr | undefined;
81
194
  let innerNullifier: Fr | undefined;
82
195
  const firstNullifier = Fr.fromBuffer(txHash.toBuffer());
83
196
 
84
- for (; commitmentIndex < commitments.length; ++commitmentIndex) {
85
- if (excludedIndices.has(commitmentIndex)) {
197
+ for (; noteHashIndex < noteHashes.length; ++noteHashIndex) {
198
+ if (excludedIndices.has(noteHashIndex)) {
86
199
  continue;
87
200
  }
88
201
 
89
- const commitment = commitments[commitmentIndex];
90
- if (commitment.equals(Fr.ZERO)) {
202
+ const noteHash = noteHashes[noteHashIndex];
203
+ if (noteHash.equals(Fr.ZERO)) {
91
204
  break;
92
205
  }
93
206
 
94
- const expectedNonce = computeNoteHashNonce(firstNullifier, commitmentIndex);
95
- ({ innerNoteHash, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndNullifier(
207
+ const expectedNonce = computeNoteHashNonce(firstNullifier, noteHashIndex);
208
+ ({ innerNoteHash, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier(
96
209
  contractAddress,
97
210
  expectedNonce,
98
211
  storageSlot,
99
212
  noteTypeId,
213
+ computeNullifier,
100
214
  note,
101
215
  ));
102
216
 
103
- if (commitment.equals(siloedNoteHash)) {
217
+ if (noteHash.equals(siloedNoteHash)) {
104
218
  nonce = expectedNonce;
105
219
  break;
106
220
  }
@@ -109,11 +223,11 @@ async function findNoteIndexAndNullifier(
109
223
  if (!nonce) {
110
224
  // NB: this used to warn the user that a decrypted log didn't match any notes.
111
225
  // This was previously fine as we didn't chop transient note logs, but now we do (#1641 complete).
112
- throw new Error('Cannot find a matching commitment for the note.');
226
+ throw new Error('Cannot find a matching note hash for the note.');
113
227
  }
114
228
 
115
229
  return {
116
- commitmentIndex,
230
+ noteHashIndex,
117
231
  nonce,
118
232
  innerNoteHash: innerNoteHash!,
119
233
  siloedNullifier: siloNullifier(contractAddress, innerNullifier!),
@@ -5,6 +5,7 @@ import { createDebugLogger } from '@aztec/foundation/log';
5
5
  import { KeyStore } from '@aztec/key-store';
6
6
  import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
7
7
  import { initStoreForRollup } from '@aztec/kv-store/utils';
8
+ import { getCanonicalAuthRegistry } from '@aztec/protocol-contracts/auth-registry';
8
9
  import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
9
10
  import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
10
11
  import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
@@ -67,6 +68,7 @@ export async function createPXEService(
67
68
  getCanonicalMultiCallEntrypointContract(),
68
69
  getCanonicalGasToken(),
69
70
  getCanonicalKeyRegistry(),
71
+ getCanonicalAuthRegistry(),
70
72
  ]) {
71
73
  await server.registerContract(contract);
72
74
  }
@@ -3,18 +3,22 @@ import {
3
3
  type AztecNode,
4
4
  EncryptedNoteTxL2Logs,
5
5
  EncryptedTxL2Logs,
6
+ type EventMetadata,
6
7
  ExtendedNote,
7
8
  type FunctionCall,
8
9
  type GetUnencryptedLogsResponse,
10
+ type IncomingNotesFilter,
11
+ L1EventPayload,
9
12
  type L2Block,
10
13
  type LogFilter,
11
14
  MerkleTreeId,
12
- type NoteFilter,
15
+ type OutgoingNotesFilter,
13
16
  type PXE,
14
17
  type PXEInfo,
15
18
  type ProofCreator,
16
19
  SimulatedTx,
17
20
  SimulationError,
21
+ TaggedLog,
18
22
  Tx,
19
23
  type TxEffect,
20
24
  type TxExecutionRequest,
@@ -32,11 +36,11 @@ import {
32
36
  } from '@aztec/circuits.js';
33
37
  import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
34
38
  import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
35
- import { type Fq, Fr } from '@aztec/foundation/fields';
39
+ import { type Fq, Fr, type Point } from '@aztec/foundation/fields';
36
40
  import { SerialQueue } from '@aztec/foundation/fifo';
37
41
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
38
42
  import { type KeyStore } from '@aztec/key-store';
39
- import { getCanonicalClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
43
+ import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer';
40
44
  import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
41
45
  import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
42
46
  import { getCanonicalKeyRegistryAddress } from '@aztec/protocol-contracts/key-registry';
@@ -57,10 +61,11 @@ import { type NodeInfo } from '@aztec/types/interfaces';
57
61
 
58
62
  import { type PXEServiceConfig, getPackageInfo } from '../config/index.js';
59
63
  import { ContractDataOracle } from '../contract_data_oracle/index.js';
64
+ import { IncomingNoteDao } from '../database/incoming_note_dao.js';
60
65
  import { type PxeDatabase } from '../database/index.js';
61
- import { NoteDao } from '../database/note_dao.js';
62
66
  import { KernelOracle } from '../kernel_oracle/index.js';
63
67
  import { KernelProver } from '../kernel_prover/kernel_prover.js';
68
+ import { TestProofCreator } from '../kernel_prover/test/test_circuit_prover.js';
64
69
  import { getAcirSimulator } from '../simulator/index.js';
65
70
  import { Synchronizer } from '../synchronizer/index.js';
66
71
 
@@ -77,6 +82,8 @@ export class PXEService implements PXE {
77
82
  // ensures that state is not changed while simulating
78
83
  private jobQueue = new SerialQueue();
79
84
 
85
+ private fakeProofCreator = new TestProofCreator();
86
+
80
87
  constructor(
81
88
  private keyStore: KeyStore,
82
89
  private node: AztecNode,
@@ -121,11 +128,7 @@ export class PXEService implements PXE {
121
128
  }
122
129
 
123
130
  count++;
124
- this.synchronizer.addAccount(
125
- address.publicKeys.masterIncomingViewingPublicKey,
126
- this.keyStore,
127
- this.config.l2StartingBlock,
128
- );
131
+ await this.synchronizer.addAccount(address.address, this.keyStore, this.config.l2StartingBlock);
129
132
  }
130
133
 
131
134
  if (count > 0) {
@@ -177,6 +180,10 @@ export class PXEService implements PXE {
177
180
  return artifact && getContractClassFromArtifact(artifact);
178
181
  }
179
182
 
183
+ public getContractArtifact(id: Fr): Promise<ContractArtifact | undefined> {
184
+ return this.db.getContractArtifact(id);
185
+ }
186
+
180
187
  public async registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
181
188
  const accounts = await this.keyStore.getAccounts();
182
189
  const accountCompleteAddress = await this.keyStore.addAccount(secretKey, partialAddress);
@@ -184,10 +191,7 @@ export class PXEService implements PXE {
184
191
  this.log.info(`Account:\n "${accountCompleteAddress.address.toString()}"\n already registered.`);
185
192
  return accountCompleteAddress;
186
193
  } else {
187
- const masterIncomingViewingPublicKey = await this.keyStore.getMasterIncomingViewingPublicKey(
188
- accountCompleteAddress.address,
189
- );
190
- this.synchronizer.addAccount(masterIncomingViewingPublicKey, this.keyStore, this.config.l2StartingBlock);
194
+ await this.synchronizer.addAccount(accountCompleteAddress.address, this.keyStore, this.config.l2StartingBlock);
191
195
  this.log.info(`Registered account ${accountCompleteAddress.address.toString()}`);
192
196
  this.log.debug(`Registered account\n ${accountCompleteAddress.toReadableString()}`);
193
197
  }
@@ -258,6 +262,7 @@ export class PXEService implements PXE {
258
262
  );
259
263
  }
260
264
  await this.db.addContractArtifact(contractClassId, artifact);
265
+ await this.node.addContractArtifact(instance.address, artifact);
261
266
  } else {
262
267
  // Otherwise, make sure there is an artifact already registered for that class id
263
268
  artifact = await this.db.getContractArtifact(instance.contractClassId);
@@ -284,19 +289,40 @@ export class PXEService implements PXE {
284
289
  return await this.node.getPublicStorageAt(contract, slot);
285
290
  }
286
291
 
287
- public async getNotes(filter: NoteFilter): Promise<ExtendedNote[]> {
288
- const noteDaos = await this.db.getNotes(filter);
292
+ public async getIncomingNotes(filter: IncomingNotesFilter): Promise<ExtendedNote[]> {
293
+ const noteDaos = await this.db.getIncomingNotes(filter);
289
294
 
290
- // TODO(benesjan): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
291
- // key derivation will affect all this
295
+ // TODO(#6531): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
296
+ // key rotation will affect this
292
297
  const extendedNotes = noteDaos.map(async dao => {
293
298
  let owner = filter.owner;
294
299
  if (owner === undefined) {
295
300
  const completeAddresses = (await this.db.getCompleteAddresses()).find(address =>
296
- address.publicKeys.masterIncomingViewingPublicKey.equals(dao.publicKey),
301
+ address.publicKeys.masterIncomingViewingPublicKey.equals(dao.ivpkM),
297
302
  );
298
303
  if (completeAddresses === undefined) {
299
- throw new Error(`Cannot find complete address for public key ${dao.publicKey.toString()}`);
304
+ throw new Error(`Cannot find complete address for IvpkM ${dao.ivpkM.toString()}`);
305
+ }
306
+ owner = completeAddresses.address;
307
+ }
308
+ return new ExtendedNote(dao.note, owner, dao.contractAddress, dao.storageSlot, dao.noteTypeId, dao.txHash);
309
+ });
310
+ return Promise.all(extendedNotes);
311
+ }
312
+
313
+ public async getOutgoingNotes(filter: OutgoingNotesFilter): Promise<ExtendedNote[]> {
314
+ const noteDaos = await this.db.getOutgoingNotes(filter);
315
+
316
+ // TODO(#6532): Refactor --> This type conversion is ugly but I decided to keep it this way for now because
317
+ // key rotation will affect this
318
+ const extendedNotes = noteDaos.map(async dao => {
319
+ let owner = filter.owner;
320
+ if (owner === undefined) {
321
+ const completeAddresses = (await this.db.getCompleteAddresses()).find(address =>
322
+ address.publicKeys.masterOutgoingViewingPublicKey.equals(dao.ovpkM),
323
+ );
324
+ if (completeAddresses === undefined) {
325
+ throw new Error(`Cannot find complete address for OvpkM ${dao.ovpkM.toString()}`);
300
326
  }
301
327
  owner = completeAddresses.address;
302
328
  }
@@ -317,13 +343,15 @@ export class PXEService implements PXE {
317
343
  }
318
344
 
319
345
  for (const nonce of nonces) {
320
- const { innerNoteHash, siloedNoteHash, innerNullifier } = await this.simulator.computeNoteHashAndNullifier(
321
- note.contractAddress,
322
- nonce,
323
- note.storageSlot,
324
- note.noteTypeId,
325
- note.note,
326
- );
346
+ const { innerNoteHash, siloedNoteHash, innerNullifier } =
347
+ await this.simulator.computeNoteHashAndOptionallyANullifier(
348
+ note.contractAddress,
349
+ nonce,
350
+ note.storageSlot,
351
+ note.noteTypeId,
352
+ true,
353
+ note.note,
354
+ );
327
355
 
328
356
  const index = await this.node.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, siloedNoteHash);
329
357
  if (index === undefined) {
@@ -337,7 +365,7 @@ export class PXEService implements PXE {
337
365
  }
338
366
 
339
367
  await this.db.addNote(
340
- new NoteDao(
368
+ new IncomingNoteDao(
341
369
  note.note,
342
370
  note.contractAddress,
343
371
  note.storageSlot,
@@ -353,6 +381,54 @@ export class PXEService implements PXE {
353
381
  }
354
382
  }
355
383
 
384
+ public async addNullifiedNote(note: ExtendedNote) {
385
+ const owner = await this.db.getCompleteAddress(note.owner);
386
+ if (!owner) {
387
+ throw new Error(`Unknown account: ${note.owner.toString()}`);
388
+ }
389
+
390
+ const nonces = await this.getNoteNonces(note);
391
+ if (nonces.length === 0) {
392
+ throw new Error(`Cannot find the note in tx: ${note.txHash}.`);
393
+ }
394
+
395
+ for (const nonce of nonces) {
396
+ const { innerNoteHash, siloedNoteHash, innerNullifier } =
397
+ await this.simulator.computeNoteHashAndOptionallyANullifier(
398
+ note.contractAddress,
399
+ nonce,
400
+ note.storageSlot,
401
+ note.noteTypeId,
402
+ false,
403
+ note.note,
404
+ );
405
+
406
+ if (!innerNullifier.equals(Fr.ZERO)) {
407
+ throw new Error('Unexpectedly received non-zero nullifier.');
408
+ }
409
+
410
+ const index = await this.node.findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, siloedNoteHash);
411
+ if (index === undefined) {
412
+ throw new Error('Note does not exist.');
413
+ }
414
+
415
+ await this.db.addNullifiedNote(
416
+ new IncomingNoteDao(
417
+ note.note,
418
+ note.contractAddress,
419
+ note.storageSlot,
420
+ note.noteTypeId,
421
+ note.txHash,
422
+ nonce,
423
+ innerNoteHash,
424
+ Fr.ZERO, // We are not able to derive
425
+ index,
426
+ owner.publicKeys.masterIncomingViewingPublicKey,
427
+ ),
428
+ );
429
+ }
430
+ }
431
+
356
432
  /**
357
433
  * Finds the nonce(s) for a given note.
358
434
  * @param note - The note to find the nonces for.
@@ -372,11 +448,12 @@ export class PXEService implements PXE {
372
448
  // Remove this once notes added from public also include nonces.
373
449
  {
374
450
  const publicNoteNonce = Fr.ZERO;
375
- const { siloedNoteHash } = await this.simulator.computeNoteHashAndNullifier(
451
+ const { siloedNoteHash } = await this.simulator.computeNoteHashAndOptionallyANullifier(
376
452
  note.contractAddress,
377
453
  publicNoteNonce,
378
454
  note.storageSlot,
379
455
  note.noteTypeId,
456
+ false,
380
457
  note.note,
381
458
  );
382
459
  if (tx.noteHashes.some(hash => hash.equals(siloedNoteHash))) {
@@ -393,11 +470,12 @@ export class PXEService implements PXE {
393
470
  }
394
471
 
395
472
  const nonce = computeNoteHashNonce(firstNullifier, i);
396
- const { siloedNoteHash } = await this.simulator.computeNoteHashAndNullifier(
473
+ const { siloedNoteHash } = await this.simulator.computeNoteHashAndOptionallyANullifier(
397
474
  note.contractAddress,
398
475
  nonce,
399
476
  note.storageSlot,
400
477
  note.noteTypeId,
478
+ false,
401
479
  note.note,
402
480
  );
403
481
  if (hash.equals(siloedNoteHash)) {
@@ -416,8 +494,14 @@ export class PXEService implements PXE {
416
494
  return await this.node.getBlock(blockNumber);
417
495
  }
418
496
 
419
- public async proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean) {
420
- return (await this.simulateTx(txRequest, simulatePublic)).tx;
497
+ public proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
498
+ return this.jobQueue.put(async () => {
499
+ const simulatedTx = await this.#simulateAndProve(txRequest, this.proofCreator, undefined);
500
+ if (simulatePublic) {
501
+ simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
502
+ }
503
+ return simulatedTx.tx;
504
+ });
421
505
  }
422
506
 
423
507
  public async simulateTx(
@@ -426,16 +510,15 @@ export class PXEService implements PXE {
426
510
  msgSender: AztecAddress | undefined = undefined,
427
511
  ): Promise<SimulatedTx> {
428
512
  return await this.jobQueue.put(async () => {
429
- const simulatedTx = await this.#simulateAndProve(txRequest, msgSender);
430
- // We log only if the msgSender is undefined, as simulating with a different msgSender
431
- // is unlikely to be a real transaction, and likely to be only used to read data.
432
- // Meaning that it will not necessarily have produced a nullifier (and thus have no TxHash)
433
- // If we log, the `getTxHash` function will throw.
434
-
513
+ const simulatedTx = await this.#simulateAndProve(txRequest, this.fakeProofCreator, msgSender);
435
514
  if (simulatePublic) {
436
515
  simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
437
516
  }
438
517
 
518
+ // We log only if the msgSender is undefined, as simulating with a different msgSender
519
+ // is unlikely to be a real transaction, and likely to be only used to read data.
520
+ // Meaning that it will not necessarily have produced a nullifier (and thus have no TxHash)
521
+ // If we log, the `getTxHash` function will throw.
439
522
  if (!msgSender) {
440
523
  this.log.info(`Executed local simulation for ${simulatedTx.tx.getTxHash()}`);
441
524
  }
@@ -539,7 +622,7 @@ export class PXEService implements PXE {
539
622
  return Promise.resolve({
540
623
  pxeVersion: this.packageVersion,
541
624
  protocolContractAddresses: {
542
- classRegisterer: getCanonicalClassRegistererAddress(),
625
+ classRegisterer: ClassRegistererAddress,
543
626
  gasToken: getCanonicalGasToken().address,
544
627
  instanceDeployer: getCanonicalInstanceDeployer().address,
545
628
  keyRegistry: getCanonicalKeyRegistryAddress(),
@@ -652,18 +735,22 @@ export class PXEService implements PXE {
652
735
  * the function will also include the new contract's public functions in the transaction object.
653
736
  *
654
737
  * @param txExecutionRequest - The transaction request to be simulated and proved.
655
- * @param signature - The ECDSA signature for the transaction request.
738
+ * @param proofCreator - The proof creator to use for proving the execution.
656
739
  * @param msgSender - (Optional) The message sender to use for the simulation.
657
- * @returns An object tract contains:
740
+ * @returns An object that contains:
658
741
  * A private transaction object containing the proof, public inputs, and encrypted logs.
659
742
  * The return values of the private execution
660
743
  */
661
- async #simulateAndProve(txExecutionRequest: TxExecutionRequest, msgSender?: AztecAddress) {
744
+ async #simulateAndProve(
745
+ txExecutionRequest: TxExecutionRequest,
746
+ proofCreator: ProofCreator,
747
+ msgSender?: AztecAddress,
748
+ ): Promise<SimulatedTx> {
662
749
  // Get values that allow us to reconstruct the block hash
663
750
  const executionResult = await this.#simulate(txExecutionRequest, msgSender);
664
751
 
665
752
  const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node);
666
- const kernelProver = new KernelProver(kernelOracle, this.proofCreator);
753
+ const kernelProver = new KernelProver(kernelOracle, proofCreator);
667
754
  this.log.debug(`Executing kernel prover...`);
668
755
  const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
669
756
 
@@ -746,4 +833,45 @@ export class PXEService implements PXE {
746
833
  public async isContractPubliclyDeployed(address: AztecAddress): Promise<boolean> {
747
834
  return !!(await this.node.getContract(address));
748
835
  }
836
+
837
+ public async getEvents<T>(from: number, limit: number, eventMetadata: EventMetadata<T>, ivpk: Point): Promise<T[]> {
838
+ const blocks = await this.node.getBlocks(from, limit);
839
+
840
+ const txEffects = blocks.flatMap(block => block.body.txEffects);
841
+ const encryptedTxLogs = txEffects.flatMap(txEffect => txEffect.encryptedLogs);
842
+
843
+ const encryptedLogs = encryptedTxLogs.flatMap(encryptedTxLog => encryptedTxLog.unrollLogs());
844
+
845
+ const ivsk = await this.keyStore.getMasterSecretKey(ivpk);
846
+
847
+ const visibleEvents = encryptedLogs
848
+ .map(encryptedLog => TaggedLog.decryptAsIncoming(encryptedLog, ivsk, L1EventPayload))
849
+ .filter(item => item !== undefined) as TaggedLog<L1EventPayload>[];
850
+
851
+ const decodedEvents = visibleEvents
852
+ .map(visibleEvent => {
853
+ if (visibleEvent.payload === undefined) {
854
+ return undefined;
855
+ }
856
+ if (!FunctionSelector.fromField(visibleEvent.payload.eventTypeId).equals(eventMetadata.functionSelector)) {
857
+ return undefined;
858
+ }
859
+ if (visibleEvent.payload.event.items.length !== eventMetadata.fieldNames.length) {
860
+ throw new Error(
861
+ 'Something is weird here, we have matching FunctionSelectors, but the actual payload has mismatched length',
862
+ );
863
+ }
864
+
865
+ return eventMetadata.fieldNames.reduce(
866
+ (acc, curr, i) => ({
867
+ ...acc,
868
+ [curr]: visibleEvent.payload.event.items[i],
869
+ }),
870
+ {} as T,
871
+ );
872
+ })
873
+ .filter(visibleEvent => visibleEvent !== undefined) as T[];
874
+
875
+ return decodedEvents;
876
+ }
749
877
  }