@aztec/pxe 0.59.0 → 0.61.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 (76) hide show
  1. package/dest/contract_data_oracle/index.d.ts +1 -0
  2. package/dest/contract_data_oracle/index.d.ts.map +1 -1
  3. package/dest/contract_data_oracle/private_functions_tree.d.ts +1 -0
  4. package/dest/contract_data_oracle/private_functions_tree.d.ts.map +1 -1
  5. package/dest/database/deferred_note_dao.d.ts +7 -19
  6. package/dest/database/deferred_note_dao.d.ts.map +1 -1
  7. package/dest/database/deferred_note_dao.js +8 -18
  8. package/dest/database/incoming_note_dao.d.ts +2 -1
  9. package/dest/database/incoming_note_dao.d.ts.map +1 -1
  10. package/dest/database/incoming_note_dao.js +3 -3
  11. package/dest/database/kv_pxe_database.d.ts +5 -3
  12. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  13. package/dest/database/kv_pxe_database.js +22 -8
  14. package/dest/database/outgoing_note_dao.d.ts +2 -1
  15. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  16. package/dest/database/outgoing_note_dao.js +3 -3
  17. package/dest/database/pxe_database.d.ts +6 -4
  18. package/dest/database/pxe_database.d.ts.map +1 -1
  19. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
  20. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +9 -9
  21. package/dest/kernel_prover/kernel_prover.js +6 -6
  22. package/dest/kernel_prover/test/test_circuit_prover.d.ts +1 -0
  23. package/dest/kernel_prover/test/test_circuit_prover.d.ts.map +1 -1
  24. package/dest/note_processor/note_processor.d.ts +1 -5
  25. package/dest/note_processor/note_processor.d.ts.map +1 -1
  26. package/dest/note_processor/note_processor.js +56 -58
  27. package/dest/note_processor/utils/add_public_values_to_payload.d.ts +10 -0
  28. package/dest/note_processor/utils/add_public_values_to_payload.d.ts.map +1 -0
  29. package/dest/note_processor/utils/add_public_values_to_payload.js +48 -0
  30. package/dest/note_processor/utils/brute_force_note_info.d.ts +8 -3
  31. package/dest/note_processor/utils/brute_force_note_info.d.ts.map +1 -1
  32. package/dest/note_processor/utils/brute_force_note_info.js +6 -3
  33. package/dest/note_processor/utils/produce_note_daos.d.ts.map +1 -1
  34. package/dest/note_processor/utils/produce_note_daos.js +2 -4
  35. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts +3 -3
  36. package/dest/note_processor/utils/produce_note_daos_for_key.d.ts.map +1 -1
  37. package/dest/note_processor/utils/produce_note_daos_for_key.js +7 -61
  38. package/dest/pxe_http/pxe_http_server.js +3 -3
  39. package/dest/pxe_service/error_enriching.d.ts +11 -0
  40. package/dest/pxe_service/error_enriching.d.ts.map +1 -0
  41. package/dest/pxe_service/error_enriching.js +68 -0
  42. package/dest/pxe_service/index.d.ts +1 -0
  43. package/dest/pxe_service/index.d.ts.map +1 -1
  44. package/dest/pxe_service/index.js +2 -1
  45. package/dest/pxe_service/pxe_service.d.ts +1 -1
  46. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  47. package/dest/pxe_service/pxe_service.js +27 -66
  48. package/dest/simulator_oracle/index.d.ts +17 -1
  49. package/dest/simulator_oracle/index.d.ts.map +1 -1
  50. package/dest/simulator_oracle/index.js +40 -1
  51. package/dest/synchronizer/synchronizer.d.ts +1 -1
  52. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  53. package/dest/synchronizer/synchronizer.js +3 -3
  54. package/package.json +14 -14
  55. package/src/database/deferred_note_dao.ts +6 -19
  56. package/src/database/incoming_note_dao.ts +2 -1
  57. package/src/database/kv_pxe_database.ts +26 -6
  58. package/src/database/outgoing_note_dao.ts +2 -1
  59. package/src/database/pxe_database.ts +8 -4
  60. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +8 -14
  61. package/src/kernel_prover/kernel_prover.ts +6 -6
  62. package/src/note_processor/note_processor.ts +89 -88
  63. package/src/note_processor/utils/add_public_values_to_payload.ts +63 -0
  64. package/src/note_processor/utils/brute_force_note_info.ts +11 -3
  65. package/src/note_processor/utils/produce_note_daos.ts +5 -7
  66. package/src/note_processor/utils/produce_note_daos_for_key.ts +19 -114
  67. package/src/pxe_http/pxe_http_server.ts +2 -2
  68. package/src/pxe_service/error_enriching.ts +91 -0
  69. package/src/pxe_service/index.ts +1 -0
  70. package/src/pxe_service/pxe_service.ts +29 -78
  71. package/src/simulator_oracle/index.ts +51 -0
  72. package/src/synchronizer/synchronizer.ts +2 -2
  73. package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts +0 -12
  74. package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts.map +0 -1
  75. package/dest/note_processor/utils/add_nullable_field_to_payload.js +0 -46
  76. package/src/note_processor/utils/add_nullable_field_to_payload.ts +0 -67
@@ -46,8 +46,6 @@ export async function produceNoteDaos(
46
46
  incomingDeferredNote: DeferredNoteDao | undefined;
47
47
  outgoingDeferredNote: DeferredNoteDao | undefined;
48
48
  }> {
49
- // WARNING: This code is full of tech debt and will be refactored once we have final design of partial notes
50
- // delivery.
51
49
  if (!ivpkM && !ovpkM) {
52
50
  throw new Error('Both ivpkM and ovpkM are undefined. Cannot create note.');
53
51
  }
@@ -78,11 +76,11 @@ export async function produceNoteDaos(
78
76
  // Incoming note is defined meaning that this PXE has both the incoming and outgoing keys. We can skip computing
79
77
  // note hash and note index since we already have them in the incoming note.
80
78
  outgoingNote = new OutgoingNoteDao(
81
- payload.note,
82
- payload.contractAddress,
83
- payload.storageSlot,
84
- payload.noteTypeId,
85
- txHash,
79
+ incomingNote.note,
80
+ incomingNote.contractAddress,
81
+ incomingNote.storageSlot,
82
+ incomingNote.noteTypeId,
83
+ incomingNote.txHash,
86
84
  incomingNote.nonce,
87
85
  incomingNote.noteHash,
88
86
  incomingNote.index,
@@ -1,11 +1,11 @@
1
- import { type L1NotePayload, type TxHash, UnencryptedTxL2Logs } from '@aztec/circuit-types';
2
- import { Fr, type PublicKey } from '@aztec/circuits.js';
1
+ import { type L1NotePayload, type Note, type TxHash, type UnencryptedTxL2Logs } from '@aztec/circuit-types';
2
+ import { type Fr, type PublicKey } from '@aztec/circuits.js';
3
3
  import { type Logger } from '@aztec/foundation/log';
4
4
  import { type AcirSimulator, ContractNotFoundError } from '@aztec/simulator';
5
5
 
6
6
  import { DeferredNoteDao } from '../../database/deferred_note_dao.js';
7
7
  import { type PxeDatabase } from '../../database/pxe_database.js';
8
- import { addNullableFieldsToPayload } from './add_nullable_field_to_payload.js';
8
+ import { getOrderedNoteItems } from './add_public_values_to_payload.js';
9
9
  import { type NoteInfo, bruteForceNoteInfo } from './brute_force_note_info.js';
10
10
 
11
11
  export async function produceNoteDaosForKey<T>(
@@ -19,61 +19,40 @@ export async function produceNoteDaosForKey<T>(
19
19
  excludedIndices: Set<number>,
20
20
  logger: Logger,
21
21
  unencryptedLogs: UnencryptedTxL2Logs,
22
- daoConstructor: (payload: L1NotePayload, noteInfo: NoteInfo, dataStartIndexForTx: number, pkM: PublicKey) => T,
22
+ daoConstructor: (
23
+ note: Note,
24
+ payload: L1NotePayload,
25
+ noteInfo: NoteInfo,
26
+ dataStartIndexForTx: number,
27
+ pkM: PublicKey,
28
+ ) => T,
23
29
  ): Promise<[T | undefined, DeferredNoteDao | undefined]> {
24
30
  let noteDao: T | undefined;
25
31
  let deferredNoteDao: DeferredNoteDao | undefined;
26
32
 
27
33
  try {
34
+ // We get the note by merging publicly and privately delivered note values.
35
+ const note = await getOrderedNoteItems(db, payload);
36
+
28
37
  const noteInfo = await bruteForceNoteInfo(
29
38
  simulator,
30
39
  noteHashes,
31
40
  txHash,
32
- payload,
41
+ payload.contractAddress,
42
+ payload.storageSlot,
43
+ payload.noteTypeId,
44
+ note,
33
45
  excludedIndices,
34
46
  true, // For incoming we compute a nullifier (recipient of incoming is the party that nullifies).
35
47
  );
36
48
  excludedIndices?.add(noteInfo.noteHashIndex);
37
49
 
38
- noteDao = daoConstructor(payload, noteInfo, dataStartIndexForTx, pkM);
50
+ noteDao = daoConstructor(note, payload, noteInfo, dataStartIndexForTx, pkM);
39
51
  } catch (e) {
40
52
  if (e instanceof ContractNotFoundError) {
41
53
  logger.warn(e.message);
42
54
 
43
- deferredNoteDao = new DeferredNoteDao(
44
- pkM,
45
- payload.note,
46
- payload.contractAddress,
47
- payload.storageSlot,
48
- payload.noteTypeId,
49
- txHash,
50
- noteHashes,
51
- dataStartIndexForTx,
52
- unencryptedLogs,
53
- );
54
- } else if (
55
- (e as any).message.includes('failed to solve blackbox function: embedded_curve_add') ||
56
- (e as any).message.includes('Could not find key prefix.')
57
- ) {
58
- // TODO(#8769): This branch is a temporary partial notes delivery solution that should be eventually replaced.
59
- // Both error messages above occur only when we are dealing with a partial note and are thrown when calling
60
- // `note.compute_note_hash()` or `note.compute_nullifier_without_context()`
61
- // in `compute_note_hash_and_optionally_a_nullifier` function. It occurs with partial notes because in the
62
- // partial flow we receive a note log of a note that is missing some fields here and then we try to compute
63
- // the note hash with MSM while some of the fields are zeroed out (or get a nsk for zero npk_m_hash).
64
- noteDao = await handlePartialNote(
65
- simulator,
66
- db,
67
- pkM,
68
- payload,
69
- txHash,
70
- noteHashes,
71
- dataStartIndexForTx,
72
- excludedIndices,
73
- logger,
74
- unencryptedLogs,
75
- daoConstructor,
76
- );
55
+ deferredNoteDao = new DeferredNoteDao(pkM, payload, txHash, noteHashes, dataStartIndexForTx, unencryptedLogs);
77
56
  } else {
78
57
  logger.error(`Could not process note because of "${e}". Discarding note...`);
79
58
  }
@@ -81,77 +60,3 @@ export async function produceNoteDaosForKey<T>(
81
60
 
82
61
  return [noteDao, deferredNoteDao];
83
62
  }
84
-
85
- async function handlePartialNote<T>(
86
- simulator: AcirSimulator,
87
- db: PxeDatabase,
88
- pkM: PublicKey,
89
- payload: L1NotePayload,
90
- txHash: TxHash,
91
- noteHashes: Fr[],
92
- dataStartIndexForTx: number,
93
- excludedIndices: Set<number>,
94
- logger: Logger,
95
- unencryptedLogs: UnencryptedTxL2Logs,
96
- daoConstructor: (payload: L1NotePayload, noteInfo: NoteInfo, dataStartIndexForTx: number, pkM: PublicKey) => T,
97
- ): Promise<T | undefined> {
98
- let noteDao: T | undefined;
99
-
100
- for (const functionLogs of unencryptedLogs.functionLogs) {
101
- for (const log of functionLogs.logs) {
102
- const { data } = log;
103
- // It is the expectation that partial notes will have the corresponding unencrypted log be multiple
104
- // of Fr.SIZE_IN_BYTES as the nullable fields should be simply concatenated.
105
- if (data.length % Fr.SIZE_IN_BYTES === 0) {
106
- const nullableFields = [];
107
- for (let i = 0; i < data.length; i += Fr.SIZE_IN_BYTES) {
108
- const chunk = data.subarray(i, i + Fr.SIZE_IN_BYTES);
109
- nullableFields.push(Fr.fromBuffer(chunk));
110
- }
111
-
112
- // We insert the nullable fields into the note and then we try to produce the note dao again
113
- const payloadWithNullableFields = await addNullableFieldsToPayload(db, payload, nullableFields);
114
-
115
- let deferredNoteDao: DeferredNoteDao | undefined;
116
- try {
117
- [noteDao, deferredNoteDao] = await produceNoteDaosForKey(
118
- simulator,
119
- db,
120
- pkM,
121
- payloadWithNullableFields,
122
- txHash,
123
- noteHashes,
124
- dataStartIndexForTx,
125
- excludedIndices,
126
- logger,
127
- UnencryptedTxL2Logs.empty(), // We set unencrypted logs to empty to prevent infinite recursion.
128
- daoConstructor,
129
- );
130
- } catch (e) {
131
- // We ignore the key prefix error because that is expected to be triggered when an incorrect value
132
- // is inserted at the position of `npk_m_hash`. This happens commonly because we are brute forcing
133
- // the unencrypted logs.
134
- if (!(e as any).message.includes('Could not find key prefix.')) {
135
- throw e;
136
- }
137
- }
138
-
139
- if (deferredNoteDao) {
140
- // This should not happen as we should first get contract not found error before the blackbox func error.
141
- throw new Error('Partial notes should never be deferred.');
142
- }
143
-
144
- if (noteDao) {
145
- // We managed to complete the partial note so we terminate the search.
146
- break;
147
- }
148
- }
149
- }
150
- }
151
-
152
- if (!noteDao) {
153
- logger.error(`Partial note note found. Discarding note...`);
154
- }
155
-
156
- return noteDao;
157
- }
@@ -26,7 +26,7 @@ import {
26
26
  UnencryptedL2Log,
27
27
  UniqueNote,
28
28
  } from '@aztec/circuit-types';
29
- import { FunctionSelector, PrivateCallStackItem, PublicKeys } from '@aztec/circuits.js';
29
+ import { FunctionSelector, PrivateCircuitPublicInputs, PublicKeys } from '@aztec/circuits.js';
30
30
  import { NoteSelector } from '@aztec/foundation/abi';
31
31
  import { AztecAddress } from '@aztec/foundation/aztec-address';
32
32
  import { Buffer32 } from '@aztec/foundation/buffer';
@@ -74,8 +74,8 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer {
74
74
  NullifierMembershipWitness,
75
75
  TxSimulationResult,
76
76
  TxProvingResult,
77
+ PrivateCircuitPublicInputs,
77
78
  PrivateExecutionResult,
78
- PrivateCallStackItem,
79
79
  CountedPublicExecutionRequest,
80
80
  CountedNoteLog,
81
81
  Tx,
@@ -0,0 +1,91 @@
1
+ import { type SimulationError, isNoirCallStackUnresolved } from '@aztec/circuit-types';
2
+ import { AztecAddress, Fr, FunctionSelector, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js';
3
+ import { type DebugLogger } from '@aztec/foundation/log';
4
+ import { resolveAssertionMessage, resolveOpcodeLocations } from '@aztec/simulator';
5
+
6
+ import { type ContractDataOracle, type PxeDatabase } from '../index.js';
7
+
8
+ /**
9
+ * Adds contract and function names to a simulation error, if they
10
+ * can be found in the PXE database
11
+ * @param err - The error to enrich.
12
+ */
13
+ export async function enrichSimulationError(err: SimulationError, db: PxeDatabase, logger: DebugLogger) {
14
+ // Maps contract addresses to the set of functions selectors that were in error.
15
+ // Map and Set do reference equality for their keys instead of value equality, so we store the string
16
+ // representation to get e.g. different contract address objects with the same address value to match.
17
+ const mentionedFunctions: Map<string, Set<string>> = new Map();
18
+
19
+ err.getCallStack().forEach(({ contractAddress, functionSelector }) => {
20
+ if (!mentionedFunctions.has(contractAddress.toString())) {
21
+ mentionedFunctions.set(contractAddress.toString(), new Set());
22
+ }
23
+ mentionedFunctions.get(contractAddress.toString())!.add(functionSelector.toString());
24
+ });
25
+
26
+ await Promise.all(
27
+ [...mentionedFunctions.entries()].map(async ([contractAddress, selectors]) => {
28
+ const parsedContractAddress = AztecAddress.fromString(contractAddress);
29
+ const contract = await db.getContract(parsedContractAddress);
30
+ if (contract) {
31
+ err.enrichWithContractName(parsedContractAddress, contract.name);
32
+ selectors.forEach(selector => {
33
+ const functionArtifact = contract.functions.find(f => FunctionSelector.fromString(selector).equals(f));
34
+ if (functionArtifact) {
35
+ err.enrichWithFunctionName(
36
+ parsedContractAddress,
37
+ FunctionSelector.fromNameAndParameters(functionArtifact),
38
+ functionArtifact.name,
39
+ );
40
+ } else {
41
+ logger.warn(
42
+ `Could not function artifact in contract ${contract.name} for selector ${selector} when enriching error callstack`,
43
+ );
44
+ }
45
+ });
46
+ } else {
47
+ logger.warn(
48
+ `Could not find contract in database for address: ${parsedContractAddress} when enriching error callstack`,
49
+ );
50
+ }
51
+ }),
52
+ );
53
+ }
54
+
55
+ export async function enrichPublicSimulationError(
56
+ err: SimulationError,
57
+ contractDataOracle: ContractDataOracle,
58
+ db: PxeDatabase,
59
+ logger: DebugLogger,
60
+ ) {
61
+ const callStack = err.getCallStack();
62
+ const originalFailingFunction = callStack[callStack.length - 1];
63
+ // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Properly fix this.
64
+ // To be able to resolve the assertion message, we need to use the information from the public dispatch function,
65
+ // no matter what the call stack selector points to (since we've modified it to point to the target function).
66
+ // We should remove this because the AVM (or public protocol) shouldn't be aware of the public dispatch calling convention.
67
+ const debugInfo = await contractDataOracle.getFunctionDebugMetadata(
68
+ originalFailingFunction.contractAddress,
69
+ FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR)),
70
+ );
71
+ const noirCallStack = err.getNoirCallStack();
72
+ if (debugInfo) {
73
+ if (isNoirCallStackUnresolved(noirCallStack)) {
74
+ const assertionMessage = resolveAssertionMessage(noirCallStack, debugInfo);
75
+ if (assertionMessage) {
76
+ err.setOriginalMessage(err.getOriginalMessage() + `: ${assertionMessage}`);
77
+ }
78
+ try {
79
+ // Public functions are simulated as a single Brillig entry point.
80
+ // Thus, we can safely assume here that the Brillig function id is `0`.
81
+ const parsedCallStack = resolveOpcodeLocations(noirCallStack, debugInfo, 0);
82
+ err.setNoirCallStack(parsedCallStack);
83
+ } catch (err) {
84
+ logger.warn(
85
+ `Could not resolve noir call stack for ${originalFailingFunction.contractAddress.toString()}:${originalFailingFunction.functionSelector.toString()}: ${err}`,
86
+ );
87
+ }
88
+ }
89
+ await enrichSimulationError(err, db, logger);
90
+ }
91
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './pxe_service.js';
2
2
  export * from './create_pxe_service.js';
3
+ export { enrichPublicSimulationError } from './error_enriching.js';
3
4
  export { pxeTestSuite } from './test/pxe_test_suite.js';
@@ -30,19 +30,18 @@ import {
30
30
  TxSimulationResult,
31
31
  UniqueNote,
32
32
  getNonNullifiedL1ToL2MessageWitness,
33
- isNoirCallStackUnresolved,
34
33
  } from '@aztec/circuit-types';
35
34
  import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
36
35
  import {
37
- AztecAddress,
36
+ type AztecAddress,
38
37
  type CompleteAddress,
39
38
  type ContractClassWithId,
40
39
  type ContractInstanceWithAddress,
41
40
  type L1_TO_L2_MSG_TREE_HEIGHT,
42
41
  type NodeInfo,
43
- PUBLIC_DISPATCH_SELECTOR,
44
42
  type PartialAddress,
45
43
  type PrivateKernelTailCircuitPublicInputs,
44
+ computeAddressSecret,
46
45
  computeContractAddressFromInstance,
47
46
  computeContractClassId,
48
47
  getContractClassFromArtifact,
@@ -64,7 +63,7 @@ import {
64
63
  getCanonicalProtocolContract,
65
64
  protocolContractNames,
66
65
  } from '@aztec/protocol-contracts';
67
- import { type AcirSimulator, resolveAssertionMessage, resolveOpcodeLocations } from '@aztec/simulator';
66
+ import { type AcirSimulator } from '@aztec/simulator';
68
67
 
69
68
  import { type PXEServiceConfig, getPackageInfo } from '../config/index.js';
70
69
  import { ContractDataOracle } from '../contract_data_oracle/index.js';
@@ -75,6 +74,7 @@ import { KernelProver } from '../kernel_prover/kernel_prover.js';
75
74
  import { TestPrivateKernelProver } from '../kernel_prover/test/test_circuit_prover.js';
76
75
  import { getAcirSimulator } from '../simulator/index.js';
77
76
  import { Synchronizer } from '../synchronizer/index.js';
77
+ import { enrichPublicSimulationError, enrichSimulationError } from './error_enriching.js';
78
78
 
79
79
  /**
80
80
  * A Private eXecution Environment (PXE) implementation.
@@ -136,7 +136,7 @@ export class PXEService implements PXE {
136
136
  }
137
137
 
138
138
  count++;
139
- await this.synchronizer.addAccount(address, this.keyStore, this.config.l2StartingBlock);
139
+ this.synchronizer.addAccount(address, this.keyStore, this.config.l2StartingBlock);
140
140
  }
141
141
 
142
142
  if (count > 0) {
@@ -195,7 +195,7 @@ export class PXEService implements PXE {
195
195
  this.log.info(`Account:\n "${accountCompleteAddress.address.toString()}"\n already registered.`);
196
196
  return accountCompleteAddress;
197
197
  } else {
198
- await this.synchronizer.addAccount(accountCompleteAddress, this.keyStore, this.config.l2StartingBlock);
198
+ this.synchronizer.addAccount(accountCompleteAddress, this.keyStore, this.config.l2StartingBlock);
199
199
  this.log.info(`Registered account ${accountCompleteAddress.address.toString()}`);
200
200
  this.log.debug(`Registered account\n ${accountCompleteAddress.toReadableString()}`);
201
201
  }
@@ -720,7 +720,7 @@ export class PXEService implements PXE {
720
720
  return result;
721
721
  } catch (err) {
722
722
  if (err instanceof SimulationError) {
723
- await this.#enrichSimulationError(err);
723
+ await enrichSimulationError(err, this.db, this.log);
724
724
  }
725
725
  throw err;
726
726
  }
@@ -746,7 +746,7 @@ export class PXEService implements PXE {
746
746
  return result;
747
747
  } catch (err) {
748
748
  if (err instanceof SimulationError) {
749
- await this.#enrichSimulationError(err);
749
+ await enrichSimulationError(err, this.db, this.log);
750
750
  }
751
751
  throw err;
752
752
  }
@@ -764,36 +764,7 @@ export class PXEService implements PXE {
764
764
  } catch (err) {
765
765
  // Try to fill in the noir call stack since the PXE may have access to the debug metadata
766
766
  if (err instanceof SimulationError) {
767
- const callStack = err.getCallStack();
768
- const originalFailingFunction = callStack[callStack.length - 1];
769
- // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8985): Properly fix this.
770
- // To be able to resolve the assertion message, we need to use the information from the public dispatch function,
771
- // no matter what the call stack selector points to (since we've modified it to point to the target function).
772
- // We should remove this because the AVM (or public protocol) shouldn't be aware of the public dispatch calling convention.
773
- const debugInfo = await this.contractDataOracle.getFunctionDebugMetadata(
774
- originalFailingFunction.contractAddress,
775
- FunctionSelector.fromField(new Fr(PUBLIC_DISPATCH_SELECTOR)),
776
- );
777
- const noirCallStack = err.getNoirCallStack();
778
- if (debugInfo) {
779
- if (isNoirCallStackUnresolved(noirCallStack)) {
780
- const assertionMessage = resolveAssertionMessage(noirCallStack, debugInfo);
781
- if (assertionMessage) {
782
- err.setOriginalMessage(err.getOriginalMessage() + `: ${assertionMessage}`);
783
- }
784
- try {
785
- // Public functions are simulated as a single Brillig entry point.
786
- // Thus, we can safely assume here that the Brillig function id is `0`.
787
- const parsedCallStack = resolveOpcodeLocations(noirCallStack, debugInfo, 0);
788
- err.setNoirCallStack(parsedCallStack);
789
- } catch (err) {
790
- this.log.warn(
791
- `Could not resolve noir call stack for ${originalFailingFunction.contractAddress.toString()}:${originalFailingFunction.functionSelector.toString()}: ${err}`,
792
- );
793
- }
794
- }
795
- await this.#enrichSimulationError(err);
796
- }
767
+ await enrichPublicSimulationError(err, this.contractDataOracle, this.db, this.log);
797
768
  }
798
769
 
799
770
  throw err;
@@ -821,51 +792,13 @@ export class PXEService implements PXE {
821
792
  privateExecutionResult: PrivateExecutionResult,
822
793
  ): Promise<PrivateKernelSimulateOutput<PrivateKernelTailCircuitPublicInputs>> {
823
794
  // use the block the tx was simulated against
824
- const block =
825
- privateExecutionResult.callStackItem.publicInputs.historicalHeader.globalVariables.blockNumber.toNumber();
795
+ const block = privateExecutionResult.publicInputs.historicalHeader.globalVariables.blockNumber.toNumber();
826
796
  const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node, block);
827
797
  const kernelProver = new KernelProver(kernelOracle, proofCreator);
828
798
  this.log.debug(`Executing kernel prover...`);
829
799
  return await kernelProver.prove(txExecutionRequest.toTxRequest(), privateExecutionResult);
830
800
  }
831
801
 
832
- /**
833
- * Adds contract and function names to a simulation error.
834
- * @param err - The error to enrich.
835
- */
836
- async #enrichSimulationError(err: SimulationError) {
837
- // Maps contract addresses to the set of functions selectors that were in error.
838
- // Using strings because map and set don't use .equals()
839
- const mentionedFunctions: Map<string, Set<string>> = new Map();
840
-
841
- err.getCallStack().forEach(({ contractAddress, functionSelector }) => {
842
- if (!mentionedFunctions.has(contractAddress.toString())) {
843
- mentionedFunctions.set(contractAddress.toString(), new Set());
844
- }
845
- mentionedFunctions.get(contractAddress.toString())!.add(functionSelector.toString());
846
- });
847
-
848
- await Promise.all(
849
- [...mentionedFunctions.entries()].map(async ([contractAddress, selectors]) => {
850
- const parsedContractAddress = AztecAddress.fromString(contractAddress);
851
- const contract = await this.db.getContract(parsedContractAddress);
852
- if (contract) {
853
- err.enrichWithContractName(parsedContractAddress, contract.name);
854
- selectors.forEach(selector => {
855
- const functionArtifact = contract.functions.find(f => FunctionSelector.fromString(selector).equals(f));
856
- if (functionArtifact) {
857
- err.enrichWithFunctionName(
858
- parsedContractAddress,
859
- FunctionSelector.fromNameAndParameters(functionArtifact),
860
- functionArtifact.name,
861
- );
862
- }
863
- });
864
- }
865
- }),
866
- );
867
- }
868
-
869
802
  public async isGlobalStateSynchronized() {
870
803
  return await this.synchronizer.isGlobalStateSynchronized();
871
804
  }
@@ -926,6 +859,7 @@ export class PXEService implements PXE {
926
859
  from: number,
927
860
  limit: number,
928
861
  eventMetadata: EventMetadata<T>,
862
+ // TODO (#9272): Make this better, we should be able to only pass an address now
929
863
  vpks: Point[],
930
864
  ): Promise<T[]> {
931
865
  if (vpks.length === 0) {
@@ -939,7 +873,24 @@ export class PXEService implements PXE {
939
873
 
940
874
  const encryptedLogs = encryptedTxLogs.flatMap(encryptedTxLog => encryptedTxLog.unrollLogs());
941
875
 
942
- const vsks = await Promise.all(vpks.map(vpk => this.keyStore.getMasterSecretKey(vpk)));
876
+ const vsks = await Promise.all(
877
+ vpks.map(async vpk => {
878
+ const [keyPrefix, account] = this.keyStore.getKeyPrefixAndAccount(vpk);
879
+ let secretKey = await this.keyStore.getMasterSecretKey(vpk);
880
+ if (keyPrefix === 'iv') {
881
+ const registeredAccount = await this.getRegisteredAccount(account);
882
+ if (!registeredAccount) {
883
+ throw new Error('No registered account');
884
+ }
885
+
886
+ const preaddress = registeredAccount.getPreaddress();
887
+
888
+ secretKey = computeAddressSecret(preaddress, secretKey);
889
+ }
890
+
891
+ return secretKey;
892
+ }),
893
+ );
943
894
 
944
895
  const visibleEvents = encryptedLogs.flatMap(encryptedLog => {
945
896
  for (const sk of vsks) {
@@ -14,10 +14,13 @@ import {
14
14
  type Fr,
15
15
  type FunctionSelector,
16
16
  type Header,
17
+ IndexedTaggingSecret,
17
18
  type KeyValidationRequest,
18
19
  type L1_TO_L2_MSG_TREE_HEIGHT,
20
+ computeTaggingSecret,
19
21
  } from '@aztec/circuits.js';
20
22
  import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
23
+ import { poseidon2Hash } from '@aztec/foundation/crypto';
21
24
  import { createDebugLogger } from '@aztec/foundation/log';
22
25
  import { type KeyStore } from '@aztec/key-store';
23
26
  import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
@@ -226,4 +229,52 @@ export class SimulatorOracle implements DBOracle {
226
229
  public getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string> {
227
230
  return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
228
231
  }
232
+
233
+ /**
234
+ * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
235
+ * Includes the last known index used for tagging with this secret.
236
+ * @param contractAddress - The contract address to silo the secret for
237
+ * @param sender - The address sending the note
238
+ * @param recipient - The address receiving the note
239
+ * @returns A siloed tagging secret that can be used to tag notes.
240
+ */
241
+ public async getAppTaggingSecret(
242
+ contractAddress: AztecAddress,
243
+ sender: AztecAddress,
244
+ recipient: AztecAddress,
245
+ ): Promise<IndexedTaggingSecret> {
246
+ const senderCompleteAddress = await this.getCompleteAddress(sender);
247
+ const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
248
+ const sharedSecret = computeTaggingSecret(senderCompleteAddress, senderIvsk, recipient);
249
+ // Silo the secret to the app so it can't be used to track other app's notes
250
+ const secret = poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
251
+ const [index] = await this.db.getTaggingSecretsIndexes([secret]);
252
+ return new IndexedTaggingSecret(secret, index);
253
+ }
254
+
255
+ /**
256
+ * Returns the siloed tagging secrets for a given recipient and all the senders in the address book
257
+ * @param contractAddress - The contract address to silo the secret for
258
+ * @param recipient - The address receiving the notes
259
+ * @returns A list of siloed tagging secrets
260
+ */
261
+ public async getAppTaggingSecretsForSenders(
262
+ contractAddress: AztecAddress,
263
+ recipient: AztecAddress,
264
+ ): Promise<IndexedTaggingSecret[]> {
265
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
266
+ const completeAddresses = await this.db.getCompleteAddresses();
267
+ // Filter out the addresses corresponding to accounts
268
+ const accounts = await this.keyStore.getAccounts();
269
+ const senders = completeAddresses.filter(
270
+ completeAddress => !accounts.find(account => account.equals(completeAddress.address)),
271
+ );
272
+ const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
273
+ const secrets = senders.map(({ address: sender }) => {
274
+ const sharedSecret = computeTaggingSecret(recipientCompleteAddress, recipientIvsk, sender);
275
+ return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
276
+ });
277
+ const indexes = await this.db.getTaggingSecretsIndexes(secrets);
278
+ return secrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
279
+ }
229
280
  }
@@ -249,14 +249,14 @@ export class Synchronizer {
249
249
  * @param startingBlock - The block where to start scanning for notes for this accounts.
250
250
  * @returns A promise that resolves once the account is added to the Synchronizer.
251
251
  */
252
- public async addAccount(account: CompleteAddress, keyStore: KeyStore, startingBlock: number) {
252
+ public addAccount(account: CompleteAddress, keyStore: KeyStore, startingBlock: number) {
253
253
  const predicate = (x: NoteProcessor) => x.account.equals(account);
254
254
  const processor = this.noteProcessors.find(predicate) ?? this.noteProcessorsToCatchUp.find(predicate);
255
255
  if (processor) {
256
256
  return;
257
257
  }
258
258
 
259
- this.noteProcessorsToCatchUp.push(await NoteProcessor.create(account, keyStore, this.db, this.node, startingBlock));
259
+ this.noteProcessorsToCatchUp.push(NoteProcessor.create(account, keyStore, this.db, this.node, startingBlock));
260
260
  }
261
261
 
262
262
  /**
@@ -1,12 +0,0 @@
1
- import { L1NotePayload } from '@aztec/circuit-types';
2
- import { type Fr } from '@aztec/foundation/fields';
3
- import { type PxeDatabase } from '../../database/pxe_database.js';
4
- /**
5
- * Inserts publicly delivered nullable fields into the note payload.
6
- * @param db - PXE database used to fetch contract instance and artifact.
7
- * @param payload - Note payload to which nullable fields should be added.
8
- * @param nullableFields - List of nullable fields to be added to the note payload.
9
- * @returns Note payload with nullable fields added.
10
- */
11
- export declare function addNullableFieldsToPayload(db: PxeDatabase, payload: L1NotePayload, nullableFields: Fr[]): Promise<L1NotePayload>;
12
- //# sourceMappingURL=add_nullable_field_to_payload.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"add_nullable_field_to_payload.d.ts","sourceRoot":"","sources":["../../../src/note_processor/utils/add_nullable_field_to_payload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAQ,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAGnD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;GAMG;AACH,wBAAsB,0BAA0B,CAC9C,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,EAAE,EAAE,GACnB,OAAO,CAAC,aAAa,CAAC,CAiDxB"}
@@ -1,46 +0,0 @@
1
- import { L1NotePayload, Note } from '@aztec/circuit-types';
2
- import { ContractNotFoundError } from '@aztec/simulator';
3
- /**
4
- * Inserts publicly delivered nullable fields into the note payload.
5
- * @param db - PXE database used to fetch contract instance and artifact.
6
- * @param payload - Note payload to which nullable fields should be added.
7
- * @param nullableFields - List of nullable fields to be added to the note payload.
8
- * @returns Note payload with nullable fields added.
9
- */
10
- export async function addNullableFieldsToPayload(db, payload, nullableFields) {
11
- const instance = await db.getContractInstance(payload.contractAddress);
12
- if (!instance) {
13
- throw new ContractNotFoundError(`Could not find instance for ${payload.contractAddress.toString()}. This should never happen here as the partial notes flow should be triggered only for non-deferred notes.`);
14
- }
15
- const artifact = await db.getContractArtifact(instance.contractClassId);
16
- if (!artifact) {
17
- throw new Error(`Could not find artifact for contract class ${instance.contractClassId.toString()}. This should never happen here as the partial notes flow should be triggered only for non-deferred notes.`);
18
- }
19
- const noteFields = Object.values(artifact.notes).find(note => note.id.equals(payload.noteTypeId))?.fields;
20
- if (!noteFields) {
21
- throw new Error(`Could not find note fields for note type ${payload.noteTypeId.toString()}.`);
22
- }
23
- // We sort note fields by index so that we can iterate over them in order.
24
- noteFields.sort((a, b) => a.index - b.index);
25
- // Now we insert the nullable fields into the note based on its indices defined in the ABI.
26
- const modifiedNoteItems = [...payload.note.items];
27
- let indexInNullable = 0;
28
- for (let i = 0; i < noteFields.length; i++) {
29
- const noteField = noteFields[i];
30
- if (noteField.nullable) {
31
- if (i == noteFields.length - 1) {
32
- // We are processing the last field so we simply insert the rest of the nullable fields at the end
33
- modifiedNoteItems.push(...nullableFields.slice(indexInNullable));
34
- }
35
- else {
36
- const noteFieldLength = noteFields[i + 1].index - noteField.index;
37
- const nullableFieldsToInsert = nullableFields.slice(indexInNullable, indexInNullable + noteFieldLength);
38
- indexInNullable += noteFieldLength;
39
- // Now we insert the nullable fields at the note field index
40
- modifiedNoteItems.splice(noteField.index, 0, ...nullableFieldsToInsert);
41
- }
42
- }
43
- }
44
- return new L1NotePayload(new Note(modifiedNoteItems), payload.contractAddress, payload.storageSlot, payload.noteTypeId);
45
- }
46
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRkX251bGxhYmxlX2ZpZWxkX3RvX3BheWxvYWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbm90ZV9wcm9jZXNzb3IvdXRpbHMvYWRkX251bGxhYmxlX2ZpZWxkX3RvX3BheWxvYWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxJQUFJLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUUzRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUl6RDs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLDBCQUEwQixDQUM5QyxFQUFlLEVBQ2YsT0FBc0IsRUFDdEIsY0FBb0I7SUFFcEIsTUFBTSxRQUFRLEdBQUcsTUFBTSxFQUFFLENBQUMsbUJBQW1CLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ3ZFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNkLE1BQU0sSUFBSSxxQkFBcUIsQ0FDN0IsK0JBQStCLE9BQU8sQ0FBQyxlQUFlLENBQUMsUUFBUSxFQUFFLDRHQUE0RyxDQUM5SyxDQUFDO0lBQ0osQ0FBQztJQUVELE1BQU0sUUFBUSxHQUFHLE1BQU0sRUFBRSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN4RSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDZCxNQUFNLElBQUksS0FBSyxDQUNiLDhDQUE4QyxRQUFRLENBQUMsZUFBZSxDQUFDLFFBQVEsRUFBRSw0R0FBNEcsQ0FDOUwsQ0FBQztJQUNKLENBQUM7SUFFRCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUM7SUFFMUcsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ2hCLE1BQU0sSUFBSSxLQUFLLENBQUMsNENBQTRDLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ2hHLENBQUM7SUFFRCwwRUFBMEU7SUFDMUUsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBRTdDLDJGQUEyRjtJQUMzRixNQUFNLGlCQUFpQixHQUFHLENBQUMsR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2xELElBQUksZUFBZSxHQUFHLENBQUMsQ0FBQztJQUN4QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsVUFBVSxDQUFDLE1BQU0sRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDO1FBQzNDLE1BQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNoQyxJQUFJLFNBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUN2QixJQUFJLENBQUMsSUFBSSxVQUFVLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMvQixrR0FBa0c7Z0JBQ2xHLGlCQUFpQixDQUFDLElBQUksQ0FBQyxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQztZQUNuRSxDQUFDO2lCQUFNLENBQUM7Z0JBQ04sTUFBTSxlQUFlLEdBQUcsVUFBVSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxLQUFLLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQztnQkFDbEUsTUFBTSxzQkFBc0IsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLGVBQWUsRUFBRSxlQUFlLEdBQUcsZUFBZSxDQUFDLENBQUM7Z0JBQ3hHLGVBQWUsSUFBSSxlQUFlLENBQUM7Z0JBQ25DLDREQUE0RDtnQkFDNUQsaUJBQWlCLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxFQUFFLEdBQUcsc0JBQXNCLENBQUMsQ0FBQztZQUMxRSxDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLElBQUksYUFBYSxDQUN0QixJQUFJLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxFQUMzQixPQUFPLENBQUMsZUFBZSxFQUN2QixPQUFPLENBQUMsV0FBVyxFQUNuQixPQUFPLENBQUMsVUFBVSxDQUNuQixDQUFDO0FBQ0osQ0FBQyJ9