@aztec/pxe 0.35.1 → 0.37.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 (55) hide show
  1. package/dest/contract_data_oracle/index.d.ts +0 -10
  2. package/dest/contract_data_oracle/index.d.ts.map +1 -1
  3. package/dest/contract_data_oracle/index.js +1 -14
  4. package/dest/database/kv_pxe_database.js +2 -2
  5. package/dest/kernel_oracle/index.d.ts +0 -1
  6. package/dest/kernel_oracle/index.d.ts.map +1 -1
  7. package/dest/kernel_oracle/index.js +2 -2
  8. package/dest/kernel_prover/kernel_prover.d.ts +0 -1
  9. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  10. package/dest/kernel_prover/kernel_prover.js +13 -16
  11. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.d.ts +3 -0
  12. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.d.ts.map +1 -0
  13. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.js +6 -0
  14. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts +4 -0
  15. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.d.ts.map +1 -0
  16. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.js +85 -0
  17. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.d.ts +4 -0
  18. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.d.ts.map +1 -0
  19. package/dest/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.js +10 -0
  20. package/dest/kernel_prover/private_inputs_builders/index.d.ts +4 -0
  21. package/dest/kernel_prover/private_inputs_builders/index.d.ts.map +1 -0
  22. package/dest/kernel_prover/private_inputs_builders/index.js +4 -0
  23. package/dest/note_processor/note_processor.d.ts +2 -2
  24. package/dest/note_processor/note_processor.d.ts.map +1 -1
  25. package/dest/note_processor/note_processor.js +12 -14
  26. package/dest/pxe_service/create_pxe_service.js +4 -3
  27. package/dest/pxe_service/pxe_service.d.ts +6 -4
  28. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  29. package/dest/pxe_service/pxe_service.js +89 -31
  30. package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
  31. package/dest/pxe_service/test/pxe_test_suite.js +11 -14
  32. package/dest/simulator_oracle/index.d.ts +5 -4
  33. package/dest/simulator_oracle/index.d.ts.map +1 -1
  34. package/dest/simulator_oracle/index.js +18 -9
  35. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  36. package/dest/synchronizer/synchronizer.js +9 -9
  37. package/package.json +20 -12
  38. package/src/contract_data_oracle/index.ts +0 -14
  39. package/src/database/kv_pxe_database.ts +1 -1
  40. package/src/kernel_oracle/index.ts +1 -1
  41. package/src/kernel_prover/kernel_prover.ts +26 -56
  42. package/src/kernel_prover/private_inputs_builders/build_private_kernel_inner_hints.ts +17 -0
  43. package/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_hints.ts +194 -0
  44. package/src/kernel_prover/private_inputs_builders/build_private_kernel_tail_outputs.ts +30 -0
  45. package/src/kernel_prover/private_inputs_builders/index.ts +3 -0
  46. package/src/note_processor/note_processor.ts +12 -12
  47. package/src/pxe_service/create_pxe_service.ts +2 -2
  48. package/src/pxe_service/pxe_service.ts +118 -31
  49. package/src/pxe_service/test/pxe_test_suite.ts +9 -14
  50. package/src/simulator_oracle/index.ts +22 -11
  51. package/src/synchronizer/synchronizer.ts +18 -13
  52. package/dest/kernel_prover/hints_builder.d.ts +0 -40
  53. package/dest/kernel_prover/hints_builder.d.ts.map +0 -1
  54. package/dest/kernel_prover/hints_builder.js +0 -103
  55. package/src/kernel_prover/hints_builder.ts +0 -155
@@ -0,0 +1,17 @@
1
+ import {
2
+ type MAX_NEW_NOTE_HASHES_PER_CALL,
3
+ type PrivateCircuitPublicInputs,
4
+ PrivateKernelInnerHints,
5
+ } from '@aztec/circuits.js';
6
+ import { type Tuple } from '@aztec/foundation/serialize';
7
+
8
+ export function buildPrivateKernelInnerHints(
9
+ publicInputs: PrivateCircuitPublicInputs,
10
+ noteHashNullifierCounterMap: Map<number, number>,
11
+ ) {
12
+ const nullifierCounters = publicInputs.newNoteHashes.map(
13
+ n => noteHashNullifierCounterMap.get(n.counter) ?? 0,
14
+ ) as Tuple<number, typeof MAX_NEW_NOTE_HASHES_PER_CALL>;
15
+
16
+ return new PrivateKernelInnerHints(nullifierCounters);
17
+ }
@@ -0,0 +1,194 @@
1
+ import {
2
+ type Fr,
3
+ GrumpkinScalar,
4
+ type MAX_ENCRYPTED_LOGS_PER_TX,
5
+ MAX_NEW_NOTE_HASHES_PER_TX,
6
+ MAX_NEW_NULLIFIERS_PER_TX,
7
+ MAX_NOTE_HASH_READ_REQUESTS_PER_TX,
8
+ MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX,
9
+ type MAX_NULLIFIER_READ_REQUESTS_PER_TX,
10
+ type MAX_UNENCRYPTED_LOGS_PER_TX,
11
+ MembershipWitness,
12
+ NULLIFIER_TREE_HEIGHT,
13
+ type NoteHashContext,
14
+ type Nullifier,
15
+ type NullifierKeyValidationRequestContext,
16
+ type PrivateKernelCircuitPublicInputs,
17
+ PrivateKernelTailHints,
18
+ type ReadRequestContext,
19
+ type SideEffect,
20
+ type SideEffectType,
21
+ buildNullifierReadRequestHints,
22
+ buildTransientDataHints,
23
+ countAccumulatedItems,
24
+ sortByCounterGetSortedHints,
25
+ } from '@aztec/circuits.js';
26
+ import { makeTuple } from '@aztec/foundation/array';
27
+ import { type Tuple } from '@aztec/foundation/serialize';
28
+
29
+ import { type ProvingDataOracle } from '../proving_data_oracle.js';
30
+
31
+ /** @deprecated Use sortByCounterGetSortedHints instead */
32
+ function sortSideEffects<T extends SideEffectType, K extends number>(
33
+ sideEffects: Tuple<T, K>,
34
+ ): [Tuple<T, K>, Tuple<number, K>] {
35
+ const sorted = sideEffects
36
+ .map((sideEffect, index) => ({ sideEffect, index }))
37
+ .sort((a, b) => {
38
+ // Empty ones go to the right
39
+ if (a.sideEffect.isEmpty()) {
40
+ return 1;
41
+ }
42
+ return Number(a.sideEffect.counter.toBigInt() - b.sideEffect.counter.toBigInt());
43
+ });
44
+
45
+ const originalToSorted = sorted.map(() => 0);
46
+ sorted.forEach(({ index }, i) => {
47
+ originalToSorted[index] = i;
48
+ });
49
+
50
+ return [sorted.map(({ sideEffect }) => sideEffect) as Tuple<T, K>, originalToSorted as Tuple<number, K>];
51
+ }
52
+
53
+ function isValidNoteHashReadRequest(readRequest: SideEffect, noteHash: NoteHashContext) {
54
+ return (
55
+ noteHash.value.equals(readRequest.value) &&
56
+ noteHash.counter < readRequest.counter.toNumber() &&
57
+ (noteHash.nullifierCounter === 0 || noteHash.nullifierCounter > readRequest.counter.toNumber())
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Performs the matching between an array of read request and an array of note hashes. This produces
63
+ * hints for the private kernel tail circuit to efficiently match a read request with the corresponding
64
+ * note hash. Several read requests might be pointing to the same note hash. It is therefore valid
65
+ * to return more than one hint with the same index.
66
+ *
67
+ * @param noteHashReadRequests - The array of read requests.
68
+ * @param noteHashes - The array of note hashes.
69
+ * @returns An array of hints where each element is the index of the note hash in note hashes array
70
+ * corresponding to the read request. In other words we have readRequests[i] == noteHashes[hints[i]].
71
+ */
72
+ function getNoteHashReadRequestHints(
73
+ noteHashReadRequests: Tuple<SideEffect, typeof MAX_NOTE_HASH_READ_REQUESTS_PER_TX>,
74
+ noteHashes: Tuple<NoteHashContext, typeof MAX_NEW_NOTE_HASHES_PER_TX>,
75
+ ): Tuple<number, typeof MAX_NOTE_HASH_READ_REQUESTS_PER_TX> {
76
+ const hints = makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, () => 0);
77
+ const numReadRequests = countAccumulatedItems(noteHashReadRequests);
78
+ for (let i = 0; i < numReadRequests; i++) {
79
+ const readRequest = noteHashReadRequests[i];
80
+ const noteHashIndex = noteHashes.findIndex((n: NoteHashContext) => isValidNoteHashReadRequest(readRequest, n));
81
+ if (noteHashIndex === -1) {
82
+ throw new Error(`The read request at index ${i} ${readRequest} does not match to any note hash.`);
83
+ }
84
+ hints[i] = noteHashIndex;
85
+ }
86
+ return hints;
87
+ }
88
+
89
+ function getNullifierReadRequestHints(
90
+ nullifierReadRequests: Tuple<ReadRequestContext, typeof MAX_NULLIFIER_READ_REQUESTS_PER_TX>,
91
+ nullifiers: Tuple<Nullifier, typeof MAX_NEW_NULLIFIERS_PER_TX>,
92
+ oracle: ProvingDataOracle,
93
+ ) {
94
+ const getNullifierMembershipWitness = async (nullifier: Fr) => {
95
+ const res = await oracle.getNullifierMembershipWitness(nullifier);
96
+ if (!res) {
97
+ throw new Error(`Cannot find the leaf for nullifier ${nullifier.toBigInt()}.`);
98
+ }
99
+
100
+ const { index, siblingPath, leafPreimage } = res;
101
+ return {
102
+ membershipWitness: new MembershipWitness(
103
+ NULLIFIER_TREE_HEIGHT,
104
+ index,
105
+ siblingPath.toTuple<typeof NULLIFIER_TREE_HEIGHT>(),
106
+ ),
107
+ leafPreimage,
108
+ };
109
+ };
110
+
111
+ return buildNullifierReadRequestHints({ getNullifierMembershipWitness }, nullifierReadRequests, nullifiers);
112
+ }
113
+
114
+ async function getMasterNullifierSecretKeys(
115
+ nullifierKeyValidationRequests: Tuple<
116
+ NullifierKeyValidationRequestContext,
117
+ typeof MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX
118
+ >,
119
+ oracle: ProvingDataOracle,
120
+ ) {
121
+ const keys = makeTuple(MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, GrumpkinScalar.zero);
122
+ for (let i = 0; i < nullifierKeyValidationRequests.length; ++i) {
123
+ const request = nullifierKeyValidationRequests[i];
124
+ if (request.isEmpty()) {
125
+ break;
126
+ }
127
+ keys[i] = await oracle.getMasterNullifierSecretKey(request.masterNullifierPublicKey);
128
+ }
129
+ return keys;
130
+ }
131
+
132
+ export async function buildPrivateKernelTailHints(
133
+ publicInputs: PrivateKernelCircuitPublicInputs,
134
+ oracle: ProvingDataOracle,
135
+ ) {
136
+ const noteHashReadRequestHints = getNoteHashReadRequestHints(
137
+ publicInputs.validationRequests.noteHashReadRequests,
138
+ publicInputs.end.newNoteHashes,
139
+ );
140
+
141
+ const nullifierReadRequestHints = await getNullifierReadRequestHints(
142
+ publicInputs.validationRequests.nullifierReadRequests,
143
+ publicInputs.end.newNullifiers,
144
+ oracle,
145
+ );
146
+
147
+ const masterNullifierSecretKeys = await getMasterNullifierSecretKeys(
148
+ publicInputs.validationRequests.nullifierKeyValidationRequests,
149
+ oracle,
150
+ );
151
+
152
+ const [sortedNoteHashes, sortedNoteHashesIndexes] = sortByCounterGetSortedHints(
153
+ publicInputs.end.newNoteHashes,
154
+ MAX_NEW_NOTE_HASHES_PER_TX,
155
+ );
156
+
157
+ const [sortedNullifiers, sortedNullifiersIndexes] = sortByCounterGetSortedHints(
158
+ publicInputs.end.newNullifiers,
159
+ MAX_NEW_NULLIFIERS_PER_TX,
160
+ );
161
+
162
+ const [sortedEncryptedLogHashes, sortedEncryptedLogHashesIndexes] = sortSideEffects<
163
+ SideEffect,
164
+ typeof MAX_ENCRYPTED_LOGS_PER_TX
165
+ >(publicInputs.end.encryptedLogsHashes);
166
+
167
+ const [sortedUnencryptedLogHashes, sortedUnencryptedLogHashesIndexes] = sortSideEffects<
168
+ SideEffect,
169
+ typeof MAX_UNENCRYPTED_LOGS_PER_TX
170
+ >(publicInputs.end.unencryptedLogsHashes);
171
+
172
+ const [transientNullifierIndexesForNoteHashes, transientNoteHashIndexesForNullifiers] = buildTransientDataHints(
173
+ sortedNoteHashes,
174
+ sortedNullifiers,
175
+ MAX_NEW_NOTE_HASHES_PER_TX,
176
+ MAX_NEW_NULLIFIERS_PER_TX,
177
+ );
178
+
179
+ return new PrivateKernelTailHints(
180
+ transientNullifierIndexesForNoteHashes,
181
+ transientNoteHashIndexesForNullifiers,
182
+ noteHashReadRequestHints,
183
+ nullifierReadRequestHints,
184
+ masterNullifierSecretKeys,
185
+ sortedNoteHashes,
186
+ sortedNoteHashesIndexes,
187
+ sortedNullifiers,
188
+ sortedNullifiersIndexes,
189
+ sortedEncryptedLogHashes,
190
+ sortedEncryptedLogHashesIndexes,
191
+ sortedUnencryptedLogHashes,
192
+ sortedUnencryptedLogHashesIndexes,
193
+ );
194
+ }
@@ -0,0 +1,30 @@
1
+ import {
2
+ MAX_NEW_NOTE_HASHES_PER_TX,
3
+ MAX_NEW_NULLIFIERS_PER_TX,
4
+ NoteHashContext,
5
+ Nullifier,
6
+ PrivateKernelTailOutputs,
7
+ } from '@aztec/circuits.js';
8
+ import { padArrayEnd } from '@aztec/foundation/collection';
9
+ import { type Tuple } from '@aztec/foundation/serialize';
10
+
11
+ export function buildPrivateKernelTailOutputs(
12
+ prevNoteHashes: Tuple<NoteHashContext, typeof MAX_NEW_NOTE_HASHES_PER_TX>,
13
+ prevNullifiers: Tuple<Nullifier, typeof MAX_NEW_NULLIFIERS_PER_TX>,
14
+ ) {
15
+ // Propagate note hashes that are not linked to a nullifier.
16
+ // Note that note hashes can't link to the first nullifier (counter == 0).
17
+ const noteHashes = padArrayEnd(
18
+ prevNoteHashes.filter(n => !n.nullifierCounter),
19
+ NoteHashContext.empty(),
20
+ MAX_NEW_NOTE_HASHES_PER_TX,
21
+ );
22
+
23
+ const nullifiers = padArrayEnd(
24
+ prevNullifiers.filter(n => n.noteHash.isZero()),
25
+ Nullifier.empty(),
26
+ MAX_NEW_NULLIFIERS_PER_TX,
27
+ );
28
+
29
+ return new PrivateKernelTailOutputs(noteHashes, nullifiers);
30
+ }
@@ -0,0 +1,3 @@
1
+ export { buildPrivateKernelInnerHints } from './build_private_kernel_inner_hints.js';
2
+ export { buildPrivateKernelTailHints } from './build_private_kernel_tail_hints.js';
3
+ export { buildPrivateKernelTailOutputs } from './build_private_kernel_tail_outputs.js';
@@ -8,7 +8,6 @@ import {
8
8
  } from '@aztec/circuit-types';
9
9
  import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
10
10
  import { INITIAL_L2_BLOCK_NUM, MAX_NEW_NOTE_HASHES_PER_TX, type PublicKey } from '@aztec/circuits.js';
11
- import { Grumpkin } from '@aztec/circuits.js/barretenberg';
12
11
  import { type Fr } from '@aztec/foundation/fields';
13
12
  import { createDebugLogger } from '@aztec/foundation/log';
14
13
  import { Timer } from '@aztec/foundation/timer';
@@ -49,7 +48,7 @@ export class NoteProcessor {
49
48
  /**
50
49
  * The public counterpart to the private key to be used in note decryption.
51
50
  */
52
- public readonly publicKey: PublicKey,
51
+ public readonly masterIncomingViewingPublicKey: PublicKey,
53
52
  private keyStore: KeyStore,
54
53
  private db: PxeDatabase,
55
54
  private node: AztecNode,
@@ -78,7 +77,7 @@ export class NoteProcessor {
78
77
  }
79
78
 
80
79
  private getSyncedToBlock(): number {
81
- return this.db.getSynchedBlockNumberForPublicKey(this.publicKey) ?? this.startingBlock - 1;
80
+ return this.db.getSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey) ?? this.startingBlock - 1;
82
81
  }
83
82
 
84
83
  /**
@@ -99,7 +98,6 @@ export class NoteProcessor {
99
98
  return;
100
99
  }
101
100
 
102
- const curve = new Grumpkin();
103
101
  const blocksAndNotes: ProcessedData[] = [];
104
102
  // Keep track of notes that we couldn't process because the contract was not found.
105
103
  const deferredNoteDaos: DeferredNoteDao[] = [];
@@ -116,7 +114,9 @@ export class NoteProcessor {
116
114
  // We are using set for `userPertainingTxIndices` to avoid duplicates. This would happen in case there were
117
115
  // multiple encrypted logs in a tx pertaining to a user.
118
116
  const noteDaos: NoteDao[] = [];
119
- const privateKey = await this.keyStore.getAccountPrivateKey(this.publicKey);
117
+ const secretKey = await this.keyStore.getMasterIncomingViewingSecretKeyForPublicKey(
118
+ this.masterIncomingViewingPublicKey,
119
+ );
120
120
 
121
121
  // Iterate over all the encrypted logs and try decrypting them. If successful, store the note.
122
122
  for (let indexOfTxInABlock = 0; indexOfTxInABlock < txLogs.length; ++indexOfTxInABlock) {
@@ -130,7 +130,7 @@ export class NoteProcessor {
130
130
  for (const functionLogs of txFunctionLogs) {
131
131
  for (const log of functionLogs.logs) {
132
132
  this.stats.seen++;
133
- const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, privateKey, curve);
133
+ const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, secretKey);
134
134
  if (taggedNote?.notePayload) {
135
135
  const { notePayload: payload } = taggedNote;
136
136
  // We have successfully decrypted the data.
@@ -138,7 +138,7 @@ export class NoteProcessor {
138
138
  try {
139
139
  const noteDao = await produceNoteDao(
140
140
  this.simulator,
141
- this.publicKey,
141
+ this.masterIncomingViewingPublicKey,
142
142
  payload,
143
143
  txHash,
144
144
  newNoteHashes,
@@ -152,7 +152,7 @@ export class NoteProcessor {
152
152
  this.stats.deferred++;
153
153
  this.log.warn(e.message);
154
154
  const deferredNoteDao = new DeferredNoteDao(
155
- this.publicKey,
155
+ this.masterIncomingViewingPublicKey,
156
156
  payload.note,
157
157
  payload.contractAddress,
158
158
  payload.storageSlot,
@@ -164,7 +164,7 @@ export class NoteProcessor {
164
164
  deferredNoteDaos.push(deferredNoteDao);
165
165
  } else {
166
166
  this.stats.failed++;
167
- this.log.warn(`Could not process note because of "${e}". Discarding note...`);
167
+ this.log.error(`Could not process note because of "${e}". Discarding note...`);
168
168
  }
169
169
  }
170
170
  }
@@ -182,7 +182,7 @@ export class NoteProcessor {
182
182
  await this.processDeferredNotes(deferredNoteDaos);
183
183
 
184
184
  const syncedToBlock = l2Blocks[l2Blocks.length - 1].number;
185
- await this.db.setSynchedBlockNumberForPublicKey(this.publicKey, syncedToBlock);
185
+ await this.db.setSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey, syncedToBlock);
186
186
 
187
187
  this.log.debug(`Synched block ${syncedToBlock}`);
188
188
  }
@@ -212,7 +212,7 @@ export class NoteProcessor {
212
212
  const newNullifiers: Fr[] = blocksAndNotes.flatMap(b =>
213
213
  b.block.body.txEffects.flatMap(txEffect => txEffect.nullifiers),
214
214
  );
215
- const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.publicKey);
215
+ const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.masterIncomingViewingPublicKey);
216
216
  removedNotes.forEach(noteDao => {
217
217
  this.log.verbose(
218
218
  `Removed note for contract ${noteDao.contractAddress} at slot ${
@@ -260,7 +260,7 @@ export class NoteProcessor {
260
260
  try {
261
261
  const noteDao = await produceNoteDao(
262
262
  this.simulator,
263
- this.publicKey,
263
+ this.masterIncomingViewingPublicKey,
264
264
  payload,
265
265
  txHash,
266
266
  newNoteHashes,
@@ -1,5 +1,4 @@
1
1
  import { type AztecNode } from '@aztec/circuit-types';
2
- import { Grumpkin } from '@aztec/circuits.js/barretenberg';
3
2
  import { randomBytes } from '@aztec/foundation/crypto';
4
3
  import { TestKeyStore } from '@aztec/key-store';
5
4
  import { AztecLmdbStore } from '@aztec/kv-store/lmdb';
@@ -7,6 +6,7 @@ import { initStoreForRollup } from '@aztec/kv-store/utils';
7
6
  import { getCanonicalClassRegisterer } from '@aztec/protocol-contracts/class-registerer';
8
7
  import { getCanonicalGasToken } from '@aztec/protocol-contracts/gas-token';
9
8
  import { getCanonicalInstanceDeployer } from '@aztec/protocol-contracts/instance-deployer';
9
+ import { getCanonicalKeyRegistry } from '@aztec/protocol-contracts/key-registry';
10
10
  import { getCanonicalMultiCallEntrypointContract } from '@aztec/protocol-contracts/multi-call-entrypoint';
11
11
 
12
12
  import { join } from 'path';
@@ -38,7 +38,6 @@ export async function createPXEService(
38
38
  const l1Contracts = await aztecNode.getL1ContractAddresses();
39
39
 
40
40
  const keyStore = new TestKeyStore(
41
- new Grumpkin(),
42
41
  await initStoreForRollup(AztecLmdbStore.open(keyStorePath), l1Contracts.rollupAddress),
43
42
  );
44
43
  const db = new KVPxeDatabase(await initStoreForRollup(AztecLmdbStore.open(pxeDbPath), l1Contracts.rollupAddress));
@@ -49,6 +48,7 @@ export async function createPXEService(
49
48
  getCanonicalInstanceDeployer(),
50
49
  getCanonicalMultiCallEntrypointContract(),
51
50
  getCanonicalGasToken(l1Contracts.gasPortalAddress),
51
+ getCanonicalKeyRegistry(),
52
52
  ]) {
53
53
  await server.registerContract(contract);
54
54
  }
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  type AuthWitness,
3
3
  type AztecNode,
4
+ EncryptedFunctionL2Logs,
5
+ type EncryptedL2Log,
4
6
  EncryptedTxL2Logs,
5
7
  ExtendedNote,
6
8
  type FunctionCall,
@@ -18,6 +20,8 @@ import {
18
20
  type TxExecutionRequest,
19
21
  type TxHash,
20
22
  type TxReceipt,
23
+ UnencryptedFunctionL2Logs,
24
+ type UnencryptedL2Log,
21
25
  UnencryptedTxL2Logs,
22
26
  isNoirCallStackUnresolved,
23
27
  } from '@aztec/circuit-types';
@@ -27,18 +31,18 @@ import {
27
31
  CallRequest,
28
32
  CompleteAddress,
29
33
  FunctionData,
30
- type GrumpkinPrivateKey,
31
34
  MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
32
35
  type PartialAddress,
33
36
  type PrivateKernelTailCircuitPublicInputs,
34
37
  type PublicCallRequest,
38
+ type SideEffect,
35
39
  computeContractClassId,
36
40
  getContractClassFromArtifact,
37
41
  } from '@aztec/circuits.js';
38
42
  import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/hash';
39
43
  import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
40
44
  import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
41
- import { Fr } from '@aztec/foundation/fields';
45
+ import { Fr, type Point } from '@aztec/foundation/fields';
42
46
  import { SerialQueue } from '@aztec/foundation/fifo';
43
47
  import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
44
48
  import { Timer } from '@aztec/foundation/timer';
@@ -105,7 +109,8 @@ export class PXEService implements PXE {
105
109
  }
106
110
 
107
111
  private async restoreNoteProcessors() {
108
- const publicKeys = await this.keyStore.getAccounts();
112
+ const accounts = await this.keyStore.getAccounts();
113
+ const publicKeys = accounts.map(async account => await this.keyStore.getMasterIncomingViewingPublicKey(account));
109
114
  const publicKeysSet = new Set(publicKeys.map(k => k.toString()));
110
115
 
111
116
  const registeredAddresses = await this.db.getCompleteAddresses();
@@ -165,27 +170,36 @@ export class PXEService implements PXE {
165
170
  return artifact && getContractClassFromArtifact(artifact);
166
171
  }
167
172
 
168
- public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise<CompleteAddress> {
169
- const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress);
170
- const wasAdded = await this.db.addCompleteAddress(completeAddress);
171
- if (wasAdded) {
172
- const pubKey = await this.keyStore.addAccount(privKey);
173
- this.synchronizer.addAccount(pubKey, this.keyStore, this.config.l2StartingBlock);
173
+ public async registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
174
+ const accounts = await this.keyStore.getAccounts();
175
+ const account = await this.keyStore.addAccount(secretKey, partialAddress);
176
+ const completeAddress = new CompleteAddress(
177
+ account,
178
+ await this.keyStore.getMasterIncomingViewingPublicKey(account),
179
+ partialAddress,
180
+ );
181
+ if (accounts.includes(account)) {
182
+ this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
183
+ return completeAddress;
184
+ } else {
185
+ const masterIncomingViewingPublicKey = await this.keyStore.getMasterIncomingViewingPublicKey(account);
186
+ this.synchronizer.addAccount(masterIncomingViewingPublicKey, this.keyStore, this.config.l2StartingBlock);
174
187
  this.log.info(`Registered account ${completeAddress.address.toString()}`);
175
188
  this.log.debug(`Registered account\n ${completeAddress.toReadableString()}`);
176
- } else {
177
- this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
178
189
  }
190
+
191
+ await this.db.addCompleteAddress(completeAddress);
179
192
  return completeAddress;
180
193
  }
181
194
 
182
195
  public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
183
196
  // Get complete addresses of both the recipients and the accounts
184
- const addresses = await this.db.getCompleteAddresses();
197
+ const completeAddresses = await this.db.getCompleteAddresses();
185
198
  // Filter out the addresses not corresponding to accounts
186
- const accountPubKeys = await this.keyStore.getAccounts();
187
- const accounts = addresses.filter(address => accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
188
- return accounts;
199
+ const accounts = await this.keyStore.getAccounts();
200
+ return completeAddresses.filter(completeAddress =>
201
+ accounts.find(address => address.equals(completeAddress.address)),
202
+ );
189
203
  }
190
204
 
191
205
  public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
@@ -194,8 +208,28 @@ export class PXEService implements PXE {
194
208
  return Promise.resolve(account);
195
209
  }
196
210
 
197
- public async registerRecipient(recipient: CompleteAddress): Promise<void> {
211
+ public async getRegisteredAccountPublicKeysHash(address: AztecAddress): Promise<Fr | undefined> {
212
+ const accounts = await this.keyStore.getAccounts();
213
+ if (!accounts.some(account => account.equals(address))) {
214
+ return undefined;
215
+ }
216
+ return this.keyStore.getPublicKeysHash(address);
217
+ }
218
+
219
+ public async registerRecipient(recipient: CompleteAddress, publicKeys: Point[] = []): Promise<void> {
198
220
  const wasAdded = await this.db.addCompleteAddress(recipient);
221
+
222
+ // TODO #5834: This should be refactored to be okay with only adding complete address
223
+ if (publicKeys.length !== 0) {
224
+ await this.keyStore.addPublicKeysForAccount(
225
+ recipient.address,
226
+ publicKeys[0],
227
+ publicKeys[1],
228
+ publicKeys[2],
229
+ publicKeys[3],
230
+ );
231
+ }
232
+
199
233
  if (wasAdded) {
200
234
  this.log.info(`Added recipient:\n ${recipient.toReadableString()}`);
201
235
  } else {
@@ -205,10 +239,12 @@ export class PXEService implements PXE {
205
239
 
206
240
  public async getRecipients(): Promise<CompleteAddress[]> {
207
241
  // Get complete addresses of both the recipients and the accounts
208
- const addresses = await this.db.getCompleteAddresses();
242
+ const completeAddresses = await this.db.getCompleteAddresses();
209
243
  // Filter out the addresses corresponding to accounts
210
- const accountPubKeys = await this.keyStore.getAccounts();
211
- const recipients = addresses.filter(address => !accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
244
+ const accounts = await this.keyStore.getAccounts();
245
+ const recipients = completeAddresses.filter(
246
+ completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
247
+ );
212
248
  return recipients;
213
249
  }
214
250
 
@@ -396,7 +432,7 @@ export class PXEService implements PXE {
396
432
  txRequest: TxExecutionRequest,
397
433
  simulatePublic: boolean,
398
434
  msgSender: AztecAddress | undefined = undefined,
399
- ) {
435
+ ): Promise<SimulatedTx> {
400
436
  if (!txRequest.functionData.isPrivate) {
401
437
  throw new Error(`Public entrypoints are not allowed`);
402
438
  }
@@ -417,8 +453,7 @@ export class PXEService implements PXE {
417
453
  }
418
454
 
419
455
  if (simulatePublic) {
420
- // Only one transaction, so we can take index 0.
421
- simulatedTx.publicReturnValues = (await this.#simulatePublicCalls(simulatedTx.tx))[0];
456
+ simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
422
457
  }
423
458
 
424
459
  if (!msgSender) {
@@ -514,10 +549,10 @@ export class PXEService implements PXE {
514
549
 
515
550
  /**
516
551
  * Retrieves the simulation parameters required to run an ACIR simulation.
517
- * This includes the contract address, function artifact, portal contract address, and historical tree roots.
552
+ * This includes the contract address, function artifact, and historical tree roots.
518
553
  *
519
554
  * @param execRequest - The transaction request object containing details of the contract call.
520
- * @returns An object containing the contract address, function artifact, portal contract address, and historical tree roots.
555
+ * @returns An object containing the contract address, function artifact, and historical tree roots.
521
556
  */
522
557
  async #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) {
523
558
  const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin;
@@ -529,7 +564,6 @@ export class PXEService implements PXE {
529
564
  contractAddress,
530
565
  execRequest.functionData.selector,
531
566
  );
532
- const portalContract = await this.contractDataOracle.getPortalContractAddress(contractAddress);
533
567
 
534
568
  return {
535
569
  contractAddress,
@@ -537,18 +571,17 @@ export class PXEService implements PXE {
537
571
  ...functionArtifact,
538
572
  debug,
539
573
  },
540
- portalContract,
541
574
  };
542
575
  }
543
576
 
544
577
  async #simulate(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise<ExecutionResult> {
545
578
  // TODO - Pause syncing while simulating.
546
579
 
547
- const { contractAddress, functionArtifact, portalContract } = await this.#getSimulationParameters(txRequest);
580
+ const { contractAddress, functionArtifact } = await this.#getSimulationParameters(txRequest);
548
581
 
549
582
  this.log.debug('Executing simulator...');
550
583
  try {
551
- const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, portalContract, msgSender);
584
+ const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender);
552
585
  this.log.verbose(`Simulation completed for ${contractAddress.toString()}:${functionArtifact.name}`);
553
586
  return result;
554
587
  } catch (err) {
@@ -637,8 +670,7 @@ export class PXEService implements PXE {
637
670
  this.log.debug(`Executing kernel prover...`);
638
671
  const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
639
672
 
640
- const encryptedLogs = new EncryptedTxL2Logs(collectEncryptedLogs(executionResult));
641
- const unencryptedLogs = new UnencryptedTxL2Logs(collectUnencryptedLogs(executionResult));
673
+ const { encryptedLogs, unencryptedLogs } = this.patchLogsOrdering(executionResult);
642
674
  const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);
643
675
 
644
676
  // HACK(#1639): Manually patches the ordering of the public call stack
@@ -646,7 +678,7 @@ export class PXEService implements PXE {
646
678
  await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);
647
679
 
648
680
  const tx = new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions);
649
- return new SimulatedTx(tx, [executionResult.returnValues]);
681
+ return new SimulatedTx(tx, executionResult.returnValues);
650
682
  }
651
683
 
652
684
  /**
@@ -756,6 +788,61 @@ export class PXEService implements PXE {
756
788
  );
757
789
  }
758
790
 
791
+ // As above, this is a hack for encrypted/unencrypted logs ordering, now they are sorted. Since the private kernel
792
+ // cannot keep track of side effects that happen after or before a nested call, we override the gathered logs.
793
+ // As a sanity check, we at least verify that the elements are the same, so we are only tweaking their ordering.
794
+ // See yarn-project/end-to-end/src/e2e_ordering.test.ts
795
+ // See https://github.com/AztecProtocol/aztec-packages/issues/1641
796
+ // Added as part of resolving #5017
797
+ private patchLogsOrdering(execResult: ExecutionResult) {
798
+ const encLogs = collectEncryptedLogs(execResult).flatMap(l => l.logs);
799
+ const unencLogs = collectUnencryptedLogs(execResult).flatMap(l => l.logs);
800
+ const getLogs = (res: ExecutionResult, enc: boolean) => {
801
+ const logs: SideEffect[] = enc
802
+ ? res.callStackItem.publicInputs.encryptedLogsHashes.concat(res.nestedExecutions.flatMap(e => getLogs(e, true)))
803
+ : res.callStackItem.publicInputs.unencryptedLogsHashes.concat(
804
+ res.nestedExecutions.flatMap(e => getLogs(e, false)),
805
+ );
806
+
807
+ return logs;
808
+ };
809
+
810
+ const sortSEs = (a: SideEffect, b: SideEffect) => {
811
+ if (a.isEmpty()) {
812
+ return 1;
813
+ } else if (b.isEmpty()) {
814
+ return -1;
815
+ } else {
816
+ return Number(a.counter.toBigInt() - b.counter.toBigInt());
817
+ }
818
+ };
819
+
820
+ const sortedEncLogs = getLogs(execResult, true).sort(sortSEs);
821
+ const sortedUnencLogs = getLogs(execResult, false).sort(sortSEs);
822
+
823
+ const finalEncLogs: EncryptedL2Log[] = [];
824
+ sortedEncLogs.forEach((sideEffect: SideEffect) => {
825
+ if (!sideEffect.isEmpty()) {
826
+ const isLog = (log: EncryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
827
+ const thisLogIndex = encLogs.findIndex(isLog);
828
+ finalEncLogs.push(encLogs[thisLogIndex]);
829
+ }
830
+ });
831
+
832
+ const finalUnencLogs: UnencryptedL2Log[] = [];
833
+ sortedUnencLogs.forEach((sideEffect: SideEffect) => {
834
+ if (!sideEffect.isEmpty()) {
835
+ const isLog = (log: UnencryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
836
+ const thisLogIndex = unencLogs.findIndex(isLog);
837
+ finalUnencLogs.push(unencLogs[thisLogIndex]);
838
+ }
839
+ });
840
+
841
+ const encryptedLogs = new EncryptedTxL2Logs([new EncryptedFunctionL2Logs(finalEncLogs)]);
842
+ const unencryptedLogs = new UnencryptedTxL2Logs([new UnencryptedFunctionL2Logs(finalUnencLogs)]);
843
+ return { encryptedLogs, unencryptedLogs };
844
+ }
845
+
759
846
  public async isGlobalStateSynchronized() {
760
847
  return await this.synchronizer.isGlobalStateSynchronized();
761
848
  }