@aztec/pxe 0.35.1 → 0.36.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 +4 -2
  28. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  29. package/dest/pxe_service/pxe_service.js +84 -30
  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 +4 -4
  33. package/dest/simulator_oracle/index.d.ts.map +1 -1
  34. package/dest/simulator_oracle/index.js +9 -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 +104 -29
  49. package/src/pxe_service/test/pxe_test_suite.ts +9 -14
  50. package/src/simulator_oracle/index.ts +10 -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,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,11 +31,11 @@ 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';
@@ -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,6 +208,14 @@ export class PXEService implements PXE {
194
208
  return Promise.resolve(account);
195
209
  }
196
210
 
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
+
197
219
  public async registerRecipient(recipient: CompleteAddress): Promise<void> {
198
220
  const wasAdded = await this.db.addCompleteAddress(recipient);
199
221
  if (wasAdded) {
@@ -205,10 +227,12 @@ export class PXEService implements PXE {
205
227
 
206
228
  public async getRecipients(): Promise<CompleteAddress[]> {
207
229
  // Get complete addresses of both the recipients and the accounts
208
- const addresses = await this.db.getCompleteAddresses();
230
+ const completeAddresses = await this.db.getCompleteAddresses();
209
231
  // 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)));
232
+ const accounts = await this.keyStore.getAccounts();
233
+ const recipients = completeAddresses.filter(
234
+ completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
235
+ );
212
236
  return recipients;
213
237
  }
214
238
 
@@ -396,7 +420,7 @@ export class PXEService implements PXE {
396
420
  txRequest: TxExecutionRequest,
397
421
  simulatePublic: boolean,
398
422
  msgSender: AztecAddress | undefined = undefined,
399
- ) {
423
+ ): Promise<SimulatedTx> {
400
424
  if (!txRequest.functionData.isPrivate) {
401
425
  throw new Error(`Public entrypoints are not allowed`);
402
426
  }
@@ -417,8 +441,7 @@ export class PXEService implements PXE {
417
441
  }
418
442
 
419
443
  if (simulatePublic) {
420
- // Only one transaction, so we can take index 0.
421
- simulatedTx.publicReturnValues = (await this.#simulatePublicCalls(simulatedTx.tx))[0];
444
+ simulatedTx.publicOutput = await this.#simulatePublicCalls(simulatedTx.tx);
422
445
  }
423
446
 
424
447
  if (!msgSender) {
@@ -514,10 +537,10 @@ export class PXEService implements PXE {
514
537
 
515
538
  /**
516
539
  * 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.
540
+ * This includes the contract address, function artifact, and historical tree roots.
518
541
  *
519
542
  * @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.
543
+ * @returns An object containing the contract address, function artifact, and historical tree roots.
521
544
  */
522
545
  async #getSimulationParameters(execRequest: FunctionCall | TxExecutionRequest) {
523
546
  const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin;
@@ -529,7 +552,6 @@ export class PXEService implements PXE {
529
552
  contractAddress,
530
553
  execRequest.functionData.selector,
531
554
  );
532
- const portalContract = await this.contractDataOracle.getPortalContractAddress(contractAddress);
533
555
 
534
556
  return {
535
557
  contractAddress,
@@ -537,18 +559,17 @@ export class PXEService implements PXE {
537
559
  ...functionArtifact,
538
560
  debug,
539
561
  },
540
- portalContract,
541
562
  };
542
563
  }
543
564
 
544
565
  async #simulate(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise<ExecutionResult> {
545
566
  // TODO - Pause syncing while simulating.
546
567
 
547
- const { contractAddress, functionArtifact, portalContract } = await this.#getSimulationParameters(txRequest);
568
+ const { contractAddress, functionArtifact } = await this.#getSimulationParameters(txRequest);
548
569
 
549
570
  this.log.debug('Executing simulator...');
550
571
  try {
551
- const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, portalContract, msgSender);
572
+ const result = await this.simulator.run(txRequest, functionArtifact, contractAddress, msgSender);
552
573
  this.log.verbose(`Simulation completed for ${contractAddress.toString()}:${functionArtifact.name}`);
553
574
  return result;
554
575
  } catch (err) {
@@ -637,8 +658,7 @@ export class PXEService implements PXE {
637
658
  this.log.debug(`Executing kernel prover...`);
638
659
  const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
639
660
 
640
- const encryptedLogs = new EncryptedTxL2Logs(collectEncryptedLogs(executionResult));
641
- const unencryptedLogs = new UnencryptedTxL2Logs(collectUnencryptedLogs(executionResult));
661
+ const { encryptedLogs, unencryptedLogs } = this.patchLogsOrdering(executionResult);
642
662
  const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);
643
663
 
644
664
  // HACK(#1639): Manually patches the ordering of the public call stack
@@ -646,7 +666,7 @@ export class PXEService implements PXE {
646
666
  await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);
647
667
 
648
668
  const tx = new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions);
649
- return new SimulatedTx(tx, [executionResult.returnValues]);
669
+ return new SimulatedTx(tx, executionResult.returnValues);
650
670
  }
651
671
 
652
672
  /**
@@ -756,6 +776,61 @@ export class PXEService implements PXE {
756
776
  );
757
777
  }
758
778
 
779
+ // As above, this is a hack for encrypted/unencrypted logs ordering, now they are sorted. Since the private kernel
780
+ // cannot keep track of side effects that happen after or before a nested call, we override the gathered logs.
781
+ // As a sanity check, we at least verify that the elements are the same, so we are only tweaking their ordering.
782
+ // See yarn-project/end-to-end/src/e2e_ordering.test.ts
783
+ // See https://github.com/AztecProtocol/aztec-packages/issues/1641
784
+ // Added as part of resolving #5017
785
+ private patchLogsOrdering(execResult: ExecutionResult) {
786
+ const encLogs = collectEncryptedLogs(execResult).flatMap(l => l.logs);
787
+ const unencLogs = collectUnencryptedLogs(execResult).flatMap(l => l.logs);
788
+ const getLogs = (res: ExecutionResult, enc: boolean) => {
789
+ const logs: SideEffect[] = enc
790
+ ? res.callStackItem.publicInputs.encryptedLogsHashes.concat(res.nestedExecutions.flatMap(e => getLogs(e, true)))
791
+ : res.callStackItem.publicInputs.unencryptedLogsHashes.concat(
792
+ res.nestedExecutions.flatMap(e => getLogs(e, false)),
793
+ );
794
+
795
+ return logs;
796
+ };
797
+
798
+ const sortSEs = (a: SideEffect, b: SideEffect) => {
799
+ if (a.isEmpty()) {
800
+ return 1;
801
+ } else if (b.isEmpty()) {
802
+ return -1;
803
+ } else {
804
+ return Number(a.counter.toBigInt() - b.counter.toBigInt());
805
+ }
806
+ };
807
+
808
+ const sortedEncLogs = getLogs(execResult, true).sort(sortSEs);
809
+ const sortedUnencLogs = getLogs(execResult, false).sort(sortSEs);
810
+
811
+ const finalEncLogs: EncryptedL2Log[] = [];
812
+ sortedEncLogs.forEach((sideEffect: SideEffect) => {
813
+ if (!sideEffect.isEmpty()) {
814
+ const isLog = (log: EncryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
815
+ const thisLogIndex = encLogs.findIndex(isLog);
816
+ finalEncLogs.push(encLogs[thisLogIndex]);
817
+ }
818
+ });
819
+
820
+ const finalUnencLogs: UnencryptedL2Log[] = [];
821
+ sortedUnencLogs.forEach((sideEffect: SideEffect) => {
822
+ if (!sideEffect.isEmpty()) {
823
+ const isLog = (log: UnencryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value);
824
+ const thisLogIndex = unencLogs.findIndex(isLog);
825
+ finalUnencLogs.push(unencLogs[thisLogIndex]);
826
+ }
827
+ });
828
+
829
+ const encryptedLogs = new EncryptedTxL2Logs([new EncryptedFunctionL2Logs(finalEncLogs)]);
830
+ const unencryptedLogs = new UnencryptedTxL2Logs([new UnencryptedFunctionL2Logs(finalUnencLogs)]);
831
+ return { encryptedLogs, unencryptedLogs };
832
+ }
833
+
759
834
  public async isGlobalStateSynchronized() {
760
835
  return await this.synchronizer.isGlobalStateSynchronized();
761
836
  }
@@ -10,14 +10,11 @@ import {
10
10
  CompleteAddress,
11
11
  Fr,
12
12
  FunctionData,
13
- GasSettings,
14
13
  INITIAL_L2_BLOCK_NUM,
15
14
  Point,
16
15
  TxContext,
17
16
  getContractClassFromArtifact,
18
17
  } from '@aztec/circuits.js';
19
- import { Grumpkin } from '@aztec/circuits.js/barretenberg';
20
- import { ConstantKeyPair } from '@aztec/key-store';
21
18
 
22
19
  export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) => {
23
20
  describe(testName, () => {
@@ -28,10 +25,9 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
28
25
  }, 120_000);
29
26
 
30
27
  it('registers an account and returns it as an account only and not as a recipient', async () => {
31
- const keyPair = ConstantKeyPair.random(new Grumpkin());
32
- const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random());
33
-
34
- await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
28
+ const randomSecretKey = Fr.random();
29
+ const randomPartialAddress = Fr.random();
30
+ const completeAddress = await pxe.registerAccount(randomSecretKey, randomPartialAddress);
35
31
 
36
32
  // Check that the account is correctly registered using the getAccounts and getRecipients methods
37
33
  const accounts = await pxe.getRegisteredAccounts();
@@ -65,11 +61,11 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
65
61
  });
66
62
 
67
63
  it('does not throw when registering the same account twice (just ignores the second attempt)', async () => {
68
- const keyPair = ConstantKeyPair.random(new Grumpkin());
69
- const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random());
64
+ const randomSecretKey = Fr.random();
65
+ const randomPartialAddress = Fr.random();
70
66
 
71
- await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
72
- await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
67
+ await pxe.registerAccount(randomSecretKey, randomPartialAddress);
68
+ await pxe.registerAccount(randomSecretKey, randomPartialAddress);
73
69
  });
74
70
 
75
71
  it('cannot register a recipient with the same aztec address but different pub key or partial address', async () => {
@@ -129,12 +125,11 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
129
125
  functionData.isPrivate = false;
130
126
  const txExecutionRequest = TxExecutionRequest.from({
131
127
  origin: AztecAddress.random(),
132
- argsHash: new Fr(0),
128
+ firstCallArgsHash: new Fr(0),
133
129
  functionData,
134
130
  txContext: TxContext.empty(),
135
- packedArguments: [],
131
+ argsOfCalls: [],
136
132
  authWitnesses: [],
137
- gasSettings: GasSettings.default(),
138
133
  });
139
134
 
140
135
  await expect(async () => await pxe.proveTx(txExecutionRequest, false)).rejects.toThrow(