@aztec/pxe 0.76.4 → 0.77.0-testnet-ignition.21

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 (113) hide show
  1. package/dest/bin/index.js +4 -6
  2. package/dest/config/index.d.ts +2 -2
  3. package/dest/config/index.d.ts.map +1 -1
  4. package/dest/config/index.js +20 -23
  5. package/dest/config/package_info.js +4 -2
  6. package/dest/contract_data_oracle/index.d.ts +10 -10
  7. package/dest/contract_data_oracle/index.d.ts.map +1 -1
  8. package/dest/contract_data_oracle/index.js +74 -89
  9. package/dest/contract_data_oracle/private_functions_tree.d.ts +5 -4
  10. package/dest/contract_data_oracle/private_functions_tree.d.ts.map +1 -1
  11. package/dest/contract_data_oracle/private_functions_tree.js +47 -51
  12. package/dest/database/contracts/contract_artifact_db.d.ts +2 -2
  13. package/dest/database/contracts/contract_artifact_db.d.ts.map +1 -1
  14. package/dest/database/contracts/contract_artifact_db.js +3 -2
  15. package/dest/database/contracts/contract_instance_db.d.ts +2 -1
  16. package/dest/database/contracts/contract_instance_db.d.ts.map +1 -1
  17. package/dest/database/contracts/contract_instance_db.js +3 -2
  18. package/dest/database/index.js +0 -1
  19. package/dest/database/kv_pxe_database.d.ts +9 -5
  20. package/dest/database/kv_pxe_database.d.ts.map +1 -1
  21. package/dest/database/kv_pxe_database.js +244 -257
  22. package/dest/database/note_dao.d.ts +13 -10
  23. package/dest/database/note_dao.d.ts.map +1 -1
  24. package/dest/database/note_dao.js +35 -48
  25. package/dest/database/outgoing_note_dao.d.ts +6 -3
  26. package/dest/database/outgoing_note_dao.d.ts.map +1 -1
  27. package/dest/database/outgoing_note_dao.js +25 -37
  28. package/dest/database/pxe_database.d.ts +12 -8
  29. package/dest/database/pxe_database.d.ts.map +1 -1
  30. package/dest/database/pxe_database.js +4 -2
  31. package/dest/database/pxe_database_test_suite.d.ts +1 -1
  32. package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
  33. package/dest/database/pxe_database_test_suite.js +286 -147
  34. package/dest/index.d.ts +1 -6
  35. package/dest/index.d.ts.map +1 -1
  36. package/dest/index.js +1 -7
  37. package/dest/kernel_oracle/index.d.ts +20 -10
  38. package/dest/kernel_oracle/index.d.ts.map +1 -1
  39. package/dest/kernel_oracle/index.js +32 -9
  40. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts +3 -3
  41. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.d.ts.map +1 -1
  42. package/dest/kernel_prover/hints/build_private_kernel_reset_private_inputs.js +71 -67
  43. package/dest/kernel_prover/hints/index.js +0 -1
  44. package/dest/kernel_prover/index.js +0 -1
  45. package/dest/kernel_prover/kernel_prover.d.ts +6 -6
  46. package/dest/kernel_prover/kernel_prover.d.ts.map +1 -1
  47. package/dest/kernel_prover/kernel_prover.js +98 -81
  48. package/dest/kernel_prover/proving_data_oracle.d.ts +15 -7
  49. package/dest/kernel_prover/proving_data_oracle.d.ts.map +1 -1
  50. package/dest/kernel_prover/proving_data_oracle.js +4 -2
  51. package/dest/note_decryption_utils/add_public_values_to_payload.d.ts +3 -2
  52. package/dest/note_decryption_utils/add_public_values_to_payload.d.ts.map +1 -1
  53. package/dest/note_decryption_utils/add_public_values_to_payload.js +11 -12
  54. package/dest/pxe_http/index.js +0 -1
  55. package/dest/pxe_http/pxe_http_server.d.ts +1 -1
  56. package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
  57. package/dest/pxe_http/pxe_http_server.js +9 -7
  58. package/dest/pxe_service/error_enriching.d.ts +3 -3
  59. package/dest/pxe_service/error_enriching.d.ts.map +1 -1
  60. package/dest/pxe_service/error_enriching.js +14 -17
  61. package/dest/pxe_service/index.d.ts +0 -1
  62. package/dest/pxe_service/index.d.ts.map +1 -1
  63. package/dest/pxe_service/index.js +0 -2
  64. package/dest/pxe_service/pxe_service.d.ts +21 -12
  65. package/dest/pxe_service/pxe_service.d.ts.map +1 -1
  66. package/dest/pxe_service/pxe_service.js +279 -333
  67. package/dest/pxe_service/test/pxe_test_suite.d.ts +1 -1
  68. package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
  69. package/dest/pxe_service/test/pxe_test_suite.js +44 -28
  70. package/dest/simulator/index.d.ts +3 -3
  71. package/dest/simulator/index.d.ts.map +1 -1
  72. package/dest/simulator/index.js +1 -3
  73. package/dest/simulator_oracle/index.d.ts +18 -6
  74. package/dest/simulator_oracle/index.d.ts.map +1 -1
  75. package/dest/simulator_oracle/index.js +307 -235
  76. package/dest/simulator_oracle/tagging_utils.d.ts +2 -1
  77. package/dest/simulator_oracle/tagging_utils.d.ts.map +1 -1
  78. package/dest/simulator_oracle/tagging_utils.js +5 -7
  79. package/dest/synchronizer/index.js +0 -1
  80. package/dest/synchronizer/synchronizer.d.ts +7 -4
  81. package/dest/synchronizer/synchronizer.d.ts.map +1 -1
  82. package/dest/synchronizer/synchronizer.js +64 -43
  83. package/dest/utils/create_pxe_service.d.ts +2 -2
  84. package/dest/utils/create_pxe_service.d.ts.map +1 -1
  85. package/dest/utils/create_pxe_service.js +13 -11
  86. package/package.json +18 -19
  87. package/src/bin/index.ts +1 -1
  88. package/src/config/index.ts +3 -3
  89. package/src/contract_data_oracle/index.ts +20 -20
  90. package/src/contract_data_oracle/private_functions_tree.ts +6 -7
  91. package/src/database/contracts/contract_artifact_db.ts +2 -2
  92. package/src/database/contracts/contract_instance_db.ts +2 -1
  93. package/src/database/kv_pxe_database.ts +33 -32
  94. package/src/database/note_dao.ts +11 -8
  95. package/src/database/outgoing_note_dao.ts +7 -4
  96. package/src/database/pxe_database.ts +13 -15
  97. package/src/database/pxe_database_test_suite.ts +8 -11
  98. package/src/index.ts +1 -7
  99. package/src/kernel_oracle/index.ts +55 -22
  100. package/src/kernel_prover/hints/build_private_kernel_reset_private_inputs.ts +14 -11
  101. package/src/kernel_prover/kernel_prover.ts +89 -69
  102. package/src/kernel_prover/proving_data_oracle.ts +20 -20
  103. package/src/note_decryption_utils/add_public_values_to_payload.ts +5 -4
  104. package/src/pxe_http/pxe_http_server.ts +1 -1
  105. package/src/pxe_service/error_enriching.ts +6 -6
  106. package/src/pxe_service/index.ts +0 -1
  107. package/src/pxe_service/pxe_service.ts +121 -224
  108. package/src/pxe_service/test/pxe_test_suite.ts +6 -3
  109. package/src/simulator/index.ts +3 -3
  110. package/src/simulator_oracle/index.ts +77 -47
  111. package/src/simulator_oracle/tagging_utils.ts +2 -1
  112. package/src/synchronizer/synchronizer.ts +31 -12
  113. package/src/utils/create_pxe_service.ts +16 -4
@@ -1,13 +1,18 @@
1
- var _SimulatorOracle_instances, _SimulatorOracle_findLeafIndex, _SimulatorOracle_getSiblingPath, _SimulatorOracle_calculateAppTaggingSecret, _SimulatorOracle_getIndexedTaggingSecretsForSenders, _SimulatorOracle_decryptTaggedLogs;
2
- import { __classPrivateFieldGet } from "tslib";
3
- import { L1NotePayload, MerkleTreeId, Note, TxHash, getNonNullifiedL1ToL2MessageWitness, } from '@aztec/circuit-types';
4
- import { Fr, FunctionSelector, IndexedTaggingSecret, MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS, PrivateLog, PublicLog, computeAddressSecret, computeTaggingSecretPoint, } from '@aztec/circuits.js';
5
- import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
6
- import { FunctionType, NoteSelector, encodeArguments, getFunctionArtifact, } from '@aztec/foundation/abi';
1
+ import { MAX_NOTE_HASHES_PER_TX, PRIVATE_LOG_SIZE_IN_FIELDS, PUBLIC_LOG_DATA_SIZE_IN_FIELDS } from '@aztec/constants';
7
2
  import { timesParallel } from '@aztec/foundation/collection';
8
3
  import { poseidon2Hash } from '@aztec/foundation/crypto';
4
+ import { Fr } from '@aztec/foundation/fields';
9
5
  import { createLogger } from '@aztec/foundation/log';
10
- import { MessageLoadOracleInputs, } from '@aztec/simulator/client';
6
+ import { BufferReader } from '@aztec/foundation/serialize';
7
+ import { MessageLoadOracleInputs } from '@aztec/simulator/client';
8
+ import { FunctionSelector, FunctionType, NoteSelector, encodeArguments, getFunctionArtifact } from '@aztec/stdlib/abi';
9
+ import { computeUniqueNoteHash, siloNoteHash, siloNullifier } from '@aztec/stdlib/hash';
10
+ import { computeAddressSecret, computeTaggingSecretPoint } from '@aztec/stdlib/keys';
11
+ import { IndexedTaggingSecret, L1NotePayload, LogWithTxData, PrivateLog, PublicLog } from '@aztec/stdlib/logs';
12
+ import { getNonNullifiedL1ToL2MessageWitness } from '@aztec/stdlib/messaging';
13
+ import { Note } from '@aztec/stdlib/note';
14
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
15
+ import { TxHash } from '@aztec/stdlib/tx';
11
16
  import { ContractDataOracle } from '../contract_data_oracle/index.js';
12
17
  import { NoteDao } from '../database/note_dao.js';
13
18
  import { getOrderedNoteItems } from '../note_decryption_utils/add_public_values_to_payload.js';
@@ -15,10 +20,14 @@ import { getAcirSimulator } from '../simulator/index.js';
15
20
  import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
16
21
  /**
17
22
  * A data oracle that provides information needed for simulating a transaction.
18
- */
19
- export class SimulatorOracle {
20
- constructor(contractDataOracle, db, keyStore, aztecNode, simulationProvider, log = createLogger('pxe:simulator_oracle')) {
21
- _SimulatorOracle_instances.add(this);
23
+ */ export class SimulatorOracle {
24
+ contractDataOracle;
25
+ db;
26
+ keyStore;
27
+ aztecNode;
28
+ simulationProvider;
29
+ log;
30
+ constructor(contractDataOracle, db, keyStore, aztecNode, simulationProvider, log = createLogger('pxe:simulator_oracle')){
22
31
  this.contractDataOracle = contractDataOracle;
23
32
  this.db = db;
24
33
  this.keyStore = keyStore;
@@ -33,7 +42,7 @@ export class SimulatorOracle {
33
42
  const completeAddress = await this.db.getCompleteAddress(account);
34
43
  if (!completeAddress) {
35
44
  throw new Error(`No public key registered for address ${account}.
36
- Register it by calling pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/reference/common_errors/aztecnr-errors#simulation-error-no-public-key-registered-for-address-0x0-register-it-by-calling-pxeregisterrecipient-or-pxeregisteraccount`);
45
+ Register it by calling pxe.registerAccount(...).\nSee docs for context: https://docs.aztec.network/developers/reference/debugging/aztecnr-errors#simulation-error-no-public-key-registered-for-address-0x0-register-it-by-calling-pxeregisterrecipient-or-pxeregisteraccount`);
37
46
  }
38
47
  return completeAddress;
39
48
  }
@@ -56,41 +65,40 @@ export class SimulatorOracle {
56
65
  contractAddress,
57
66
  storageSlot,
58
67
  status,
59
- scopes,
68
+ scopes
60
69
  });
61
- return noteDaos.map(({ contractAddress, storageSlot, nonce, note, noteHash, siloedNullifier, index }) => ({
62
- contractAddress,
63
- storageSlot,
64
- nonce,
65
- note,
66
- noteHash,
67
- siloedNullifier,
68
- // PXE can use this index to get full MembershipWitness
69
- index,
70
- }));
70
+ return noteDaos.map(({ contractAddress, storageSlot, nonce, note, noteHash, siloedNullifier, index })=>({
71
+ contractAddress,
72
+ storageSlot,
73
+ nonce,
74
+ note,
75
+ noteHash,
76
+ siloedNullifier,
77
+ // PXE can use this index to get full MembershipWitness
78
+ index
79
+ }));
71
80
  }
72
81
  async getFunctionArtifact(contractAddress, selector) {
73
82
  const artifact = await this.contractDataOracle.getFunctionArtifact(contractAddress, selector);
74
83
  const debug = await this.contractDataOracle.getFunctionDebugMetadata(contractAddress, selector);
75
84
  return {
76
85
  ...artifact,
77
- debug,
86
+ debug
78
87
  };
79
88
  }
80
89
  async getFunctionArtifactByName(contractAddress, functionName) {
81
90
  const instance = await this.contractDataOracle.getContractInstance(contractAddress);
82
- const artifact = await this.contractDataOracle.getContractArtifact(instance.contractClassId);
91
+ const artifact = await this.contractDataOracle.getContractArtifact(instance.currentContractClassId);
83
92
  return artifact && getFunctionArtifact(artifact, functionName);
84
93
  }
85
94
  /**
86
- * Fetches a message from the db, given its key.
87
- * @param contractAddress - Address of a contract by which the message was emitted.
88
- * @param messageHash - Hash of the message.
89
- * @param secret - Secret used to compute a nullifier.
90
- * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
91
- * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
92
- */
93
- async getL1ToL2MembershipWitness(contractAddress, messageHash, secret) {
95
+ * Fetches a message from the db, given its key.
96
+ * @param contractAddress - Address of a contract by which the message was emitted.
97
+ * @param messageHash - Hash of the message.
98
+ * @param secret - Secret used to compute a nullifier.
99
+ * @dev Contract address and secret are only used to compute the nullifier to get non-nullified messages
100
+ * @returns The l1 to l2 membership witness (index of message in the tree and sibling path).
101
+ */ async getL1ToL2MembershipWitness(contractAddress, messageHash, secret) {
94
102
  const [messageIndex, siblingPath] = await getNonNullifiedL1ToL2MessageWitness(this.aztecNode, contractAddress, messageHash, secret);
95
103
  // Assuming messageIndex is what you intended to use for the index in MessageLoadOracleInputs
96
104
  return new MessageLoadOracleInputs(messageIndex, siblingPath);
@@ -100,27 +108,49 @@ export class SimulatorOracle {
100
108
  throw new Error('Unimplemented in private!');
101
109
  }
102
110
  /**
103
- * Gets the index of a commitment in the note hash tree.
104
- * @param commitment - The commitment.
105
- * @returns - The index of the commitment. Undefined if it does not exist in the tree.
106
- */
107
- async getCommitmentIndex(commitment) {
108
- return await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_findLeafIndex).call(this, 'latest', MerkleTreeId.NOTE_HASH_TREE, commitment);
111
+ * Gets the index of a commitment in the note hash tree.
112
+ * @param commitment - The commitment.
113
+ * @returns - The index of the commitment. Undefined if it does not exist in the tree.
114
+ */ async getCommitmentIndex(commitment) {
115
+ return await this.#findLeafIndex('latest', MerkleTreeId.NOTE_HASH_TREE, commitment);
109
116
  }
110
117
  // We need this in public as part of the EXISTS calls - but isn't used in private
111
118
  getCommitmentValue(_leafIndex) {
112
119
  throw new Error('Unimplemented in private!');
113
120
  }
114
121
  async getNullifierIndex(nullifier) {
115
- return await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_findLeafIndex).call(this, 'latest', MerkleTreeId.NULLIFIER_TREE, nullifier);
122
+ return await this.#findLeafIndex('latest', MerkleTreeId.NULLIFIER_TREE, nullifier);
123
+ }
124
+ async #findLeafIndex(blockNumber, treeId, leafValue) {
125
+ const [leafIndex] = await this.aztecNode.findLeavesIndexes(blockNumber, treeId, [
126
+ leafValue
127
+ ]);
128
+ return leafIndex;
116
129
  }
117
130
  async getMembershipWitness(blockNumber, treeId, leafValue) {
118
- const leafIndex = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_findLeafIndex).call(this, blockNumber, treeId, leafValue);
131
+ const leafIndex = await this.#findLeafIndex(blockNumber, treeId, leafValue);
119
132
  if (!leafIndex) {
120
133
  throw new Error(`Leaf value: ${leafValue} not found in ${MerkleTreeId[treeId]}`);
121
134
  }
122
- const siblingPath = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_getSiblingPath).call(this, blockNumber, treeId, leafIndex);
123
- return [new Fr(leafIndex), ...siblingPath];
135
+ const siblingPath = await this.#getSiblingPath(blockNumber, treeId, leafIndex);
136
+ return [
137
+ new Fr(leafIndex),
138
+ ...siblingPath
139
+ ];
140
+ }
141
+ async #getSiblingPath(blockNumber, treeId, leafIndex) {
142
+ switch(treeId){
143
+ case MerkleTreeId.NULLIFIER_TREE:
144
+ return (await this.aztecNode.getNullifierSiblingPath(blockNumber, leafIndex)).toFields();
145
+ case MerkleTreeId.NOTE_HASH_TREE:
146
+ return (await this.aztecNode.getNoteHashSiblingPath(blockNumber, leafIndex)).toFields();
147
+ case MerkleTreeId.PUBLIC_DATA_TREE:
148
+ return (await this.aztecNode.getPublicDataSiblingPath(blockNumber, leafIndex)).toFields();
149
+ case MerkleTreeId.ARCHIVE:
150
+ return (await this.aztecNode.getArchiveSiblingPath(blockNumber, leafIndex)).toFields();
151
+ default:
152
+ throw new Error('Not implemented');
153
+ }
124
154
  }
125
155
  async getNullifierMembershipWitnessAtLatestBlock(nullifier) {
126
156
  return this.getNullifierMembershipWitness(await this.getBlockNumber(), nullifier);
@@ -138,76 +168,118 @@ export class SimulatorOracle {
138
168
  return await this.aztecNode.getPublicDataTreeWitness(blockNumber, leafSlot);
139
169
  }
140
170
  /**
141
- * Retrieve the databases view of the Block Header object.
142
- * This structure is fed into the circuits simulator and is used to prove against certain historical roots.
143
- *
144
- * @returns A Promise that resolves to a BlockHeader object.
145
- */
146
- getBlockHeader() {
171
+ * Retrieve the databases view of the Block Header object.
172
+ * This structure is fed into the circuits simulator and is used to prove against certain historical roots.
173
+ *
174
+ * @returns A Promise that resolves to a BlockHeader object.
175
+ */ getBlockHeader() {
147
176
  return this.db.getBlockHeader();
148
177
  }
149
178
  /**
150
- * Fetches the current block number.
151
- * @returns The block number.
152
- */
153
- async getBlockNumber() {
179
+ * Fetches the current block number.
180
+ * @returns The block number.
181
+ */ async getBlockNumber() {
154
182
  return await this.aztecNode.getBlockNumber();
155
183
  }
156
184
  getDebugFunctionName(contractAddress, selector) {
157
185
  return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
158
186
  }
159
187
  /**
160
- * Returns the full contents of your address book.
161
- * This is used when calculating tags for incoming notes by deriving the shared secret, the contract-siloed tagging secret, and
162
- * finally the index specified tag. We will then query the node with this tag for each address in the address book.
163
- * @returns The full list of the users contact addresses.
164
- */
165
- getSenders() {
188
+ * Returns the full contents of your address book.
189
+ * This is used when calculating tags for incoming notes by deriving the shared secret, the contract-siloed tagging secret, and
190
+ * finally the index specified tag. We will then query the node with this tag for each address in the address book.
191
+ * @returns The full list of the users contact addresses.
192
+ */ getSenders() {
166
193
  return this.db.getSenderAddresses();
167
194
  }
168
195
  /**
169
- * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
170
- * Includes the next index to be used used for tagging with this secret.
171
- * @param contractAddress - The contract address to silo the secret for
172
- * @param sender - The address sending the note
173
- * @param recipient - The address receiving the note
174
- * @returns An indexed tagging secret that can be used to tag notes.
175
- */
176
- async getIndexedTaggingSecretAsSender(contractAddress, sender, recipient) {
196
+ * Returns the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
197
+ * Includes the next index to be used used for tagging with this secret.
198
+ * @param contractAddress - The contract address to silo the secret for
199
+ * @param sender - The address sending the note
200
+ * @param recipient - The address receiving the note
201
+ * @returns An indexed tagging secret that can be used to tag notes.
202
+ */ async getIndexedTaggingSecretAsSender(contractAddress, sender, recipient) {
177
203
  await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
178
- const appTaggingSecret = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_calculateAppTaggingSecret).call(this, contractAddress, sender, recipient);
179
- const [index] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
204
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
205
+ const [index] = await this.db.getTaggingSecretsIndexesAsSender([
206
+ appTaggingSecret
207
+ ]);
180
208
  return new IndexedTaggingSecret(appTaggingSecret, index);
181
209
  }
182
210
  /**
183
- * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
184
- * @param contractAddress - The contract address to silo the secret for
185
- * @param sender - The address sending the note
186
- * @param recipient - The address receiving the note
187
- */
188
- async incrementAppTaggingSecretIndexAsSender(contractAddress, sender, recipient) {
189
- const secret = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_calculateAppTaggingSecret).call(this, contractAddress, sender, recipient);
211
+ * Increments the tagging secret for a given sender and recipient pair. For this to work, the ivsk_m of the sender must be known.
212
+ * @param contractAddress - The contract address to silo the secret for
213
+ * @param sender - The address sending the note
214
+ * @param recipient - The address receiving the note
215
+ */ async incrementAppTaggingSecretIndexAsSender(contractAddress, sender, recipient) {
216
+ const secret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
190
217
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
191
218
  this.log.debug(`Incrementing app tagging secret at ${contractName}(${contractAddress})`, {
192
219
  secret,
193
220
  sender,
194
221
  recipient,
195
222
  contractName,
196
- contractAddress,
223
+ contractAddress
197
224
  });
198
- const [index] = await this.db.getTaggingSecretsIndexesAsSender([secret]);
199
- await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(secret, index + 1)]);
225
+ const [index] = await this.db.getTaggingSecretsIndexesAsSender([
226
+ secret
227
+ ]);
228
+ await this.db.setTaggingSecretsIndexesAsSender([
229
+ new IndexedTaggingSecret(secret, index + 1)
230
+ ]);
231
+ }
232
+ async #calculateAppTaggingSecret(contractAddress, sender, recipient) {
233
+ const senderCompleteAddress = await this.getCompleteAddress(sender);
234
+ const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
235
+ const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
236
+ // Silo the secret so it can't be used to track other app's notes
237
+ const appSecret = poseidon2Hash([
238
+ secretPoint.x,
239
+ secretPoint.y,
240
+ contractAddress
241
+ ]);
242
+ return appSecret;
243
+ }
244
+ /**
245
+ * Returns the indexed tagging secrets for a given recipient and all the senders in the address book
246
+ * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
247
+ * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
248
+ * so we're keeping it private for now.
249
+ * @param contractAddress - The contract address to silo the secret for
250
+ * @param recipient - The address receiving the notes
251
+ * @returns A list of indexed tagging secrets
252
+ */ async #getIndexedTaggingSecretsForSenders(contractAddress, recipient) {
253
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
254
+ const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
255
+ // We implicitly add all PXE accounts as senders, this helps us decrypt tags on notes that we send to ourselves
256
+ // (recipient = us, sender = us)
257
+ const senders = [
258
+ ...await this.db.getSenderAddresses(),
259
+ ...await this.keyStore.getAccounts()
260
+ ].filter((address, index, self)=>index === self.findIndex((otherAddress)=>otherAddress.equals(address)));
261
+ const appTaggingSecrets = await Promise.all(senders.map(async (contact)=>{
262
+ const sharedSecret = await computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
263
+ return poseidon2Hash([
264
+ sharedSecret.x,
265
+ sharedSecret.y,
266
+ contractAddress
267
+ ]);
268
+ }));
269
+ const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
270
+ return appTaggingSecrets.map((secret, i)=>new IndexedTaggingSecret(secret, indexes[i]));
200
271
  }
201
272
  /**
202
- * Updates the local index of the shared tagging secret of a sender / recipient pair
203
- * if a log with a larger index is found from the node.
204
- * @param contractAddress - The address of the contract that the logs are tagged for
205
- * @param sender - The address of the sender, we must know the sender's ivsk_m.
206
- * @param recipient - The address of the recipient.
207
- */
208
- async syncTaggedLogsAsSender(contractAddress, sender, recipient) {
209
- const appTaggingSecret = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_calculateAppTaggingSecret).call(this, contractAddress, sender, recipient);
210
- const [oldIndex] = await this.db.getTaggingSecretsIndexesAsSender([appTaggingSecret]);
273
+ * Updates the local index of the shared tagging secret of a sender / recipient pair
274
+ * if a log with a larger index is found from the node.
275
+ * @param contractAddress - The address of the contract that the logs are tagged for
276
+ * @param sender - The address of the sender, we must know the sender's ivsk_m.
277
+ * @param recipient - The address of the recipient.
278
+ */ async syncTaggedLogsAsSender(contractAddress, sender, recipient) {
279
+ const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
280
+ const [oldIndex] = await this.db.getTaggingSecretsIndexesAsSender([
281
+ appTaggingSecret
282
+ ]);
211
283
  // This algorithm works such that:
212
284
  // 1. If we find minimum consecutive empty logs in a window of logs we set the index to the index of the last log
213
285
  // we found and quit.
@@ -215,51 +287,55 @@ export class SimulatorOracle {
215
287
  // and repeat the process.
216
288
  const MIN_CONSECUTIVE_EMPTY_LOGS = 10;
217
289
  const WINDOW_SIZE = MIN_CONSECUTIVE_EMPTY_LOGS * 2;
218
- let [numConsecutiveEmptyLogs, currentIndex] = [0, oldIndex];
290
+ let [numConsecutiveEmptyLogs, currentIndex] = [
291
+ 0,
292
+ oldIndex
293
+ ];
219
294
  do {
220
295
  // We compute the tags for the current window of indexes
221
- const currentTags = await timesParallel(WINDOW_SIZE, i => {
296
+ const currentTags = await timesParallel(WINDOW_SIZE, (i)=>{
222
297
  const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i);
223
298
  return indexedAppTaggingSecret.computeSiloedTag(recipient, contractAddress);
224
299
  });
225
300
  // We fetch the logs for the tags
226
301
  const possibleLogs = await this.aztecNode.getLogsByTags(currentTags);
227
302
  // We find the index of the last log in the window that is not empty
228
- const indexOfLastLog = possibleLogs.findLastIndex(possibleLog => possibleLog.length !== 0);
303
+ const indexOfLastLog = possibleLogs.findLastIndex((possibleLog)=>possibleLog.length !== 0);
229
304
  if (indexOfLastLog === -1) {
230
- // We haven't found any logs in the current window so we stop looking
231
305
  break;
232
306
  }
233
307
  // We move the current index to that of the last log we found
234
308
  currentIndex += indexOfLastLog + 1;
235
309
  // We compute the number of consecutive empty logs we found and repeat the process if we haven't found enough.
236
310
  numConsecutiveEmptyLogs = WINDOW_SIZE - indexOfLastLog - 1;
237
- } while (numConsecutiveEmptyLogs < MIN_CONSECUTIVE_EMPTY_LOGS);
311
+ }while (numConsecutiveEmptyLogs < MIN_CONSECUTIVE_EMPTY_LOGS)
238
312
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
239
313
  if (currentIndex !== oldIndex) {
240
- await this.db.setTaggingSecretsIndexesAsSender([new IndexedTaggingSecret(appTaggingSecret, currentIndex)]);
314
+ await this.db.setTaggingSecretsIndexesAsSender([
315
+ new IndexedTaggingSecret(appTaggingSecret, currentIndex)
316
+ ]);
241
317
  this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, {
242
318
  sender,
243
319
  secret: appTaggingSecret,
244
320
  index: currentIndex,
245
321
  contractName,
246
- contractAddress,
322
+ contractAddress
247
323
  });
248
- }
249
- else {
324
+ } else {
250
325
  this.log.debug(`No new logs found for sender ${sender} at contract ${contractName}(${contractAddress})`);
251
326
  }
252
327
  }
253
328
  /**
254
- * Synchronizes the logs tagged with scoped addresses and all the senders in the address book.
255
- * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs
256
- * to sync.
257
- * @param contractAddress - The address of the contract that the logs are tagged for
258
- * @param recipient - The address of the recipient
259
- * @returns A list of encrypted logs tagged with the recipient's address
260
- */
261
- async syncTaggedLogs(contractAddress, maxBlockNumber, scopes) {
262
- this.log.verbose('Searching for tagged logs', { contract: contractAddress });
329
+ * Synchronizes the logs tagged with scoped addresses and all the senders in the address book.
330
+ * Returns the unsynched logs and updates the indexes of the secrets used to tag them until there are no more logs
331
+ * to sync.
332
+ * @param contractAddress - The address of the contract that the logs are tagged for
333
+ * @param recipient - The address of the recipient
334
+ * @returns A list of encrypted logs tagged with the recipient's address
335
+ */ async syncTaggedLogs(contractAddress, maxBlockNumber, scopes) {
336
+ this.log.verbose('Searching for tagged logs', {
337
+ contract: contractAddress
338
+ });
263
339
  // Ideally this algorithm would be implemented in noir, exposing its building blocks as oracles.
264
340
  // However it is impossible at the moment due to the language not supporting nested slices.
265
341
  // This nesting is necessary because for a given set of tags we don't
@@ -271,10 +347,10 @@ export class SimulatorOracle {
271
347
  // that a logs will be received ordered by a given tax index and that the tags won't be reused).
272
348
  const logsMap = new Map();
273
349
  const contractName = await this.contractDataOracle.getDebugContractName(contractAddress);
274
- for (const recipient of recipients) {
350
+ for (const recipient of recipients){
275
351
  const logsForRecipient = [];
276
352
  // Get all the secrets for the recipient and sender pairs (#9365)
277
- const secrets = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_getIndexedTaggingSecretsForSenders).call(this, contractAddress, recipient);
353
+ const secrets = await this.#getIndexedTaggingSecretsForSenders(contractAddress, recipient);
278
354
  // We fetch logs for a window of indexes in a range:
279
355
  // <latest_log_index - WINDOW_HALF_SIZE, latest_log_index + WINDOW_HALF_SIZE>.
280
356
  //
@@ -283,32 +359,32 @@ export class SimulatorOracle {
283
359
  // for logs the first time we don't receive any logs for a tag, we might never receive anything from that sender again.
284
360
  // Also there's a possibility that we have advanced our index, but the sender has reused it, so we might have missed
285
361
  // some logs. For these reasons, we have to look both back and ahead of the stored index.
286
- let secretsAndWindows = secrets.map(secret => {
362
+ let secretsAndWindows = secrets.map((secret)=>{
287
363
  return {
288
364
  appTaggingSecret: secret.appTaggingSecret,
289
365
  leftMostIndex: Math.max(0, secret.index - WINDOW_HALF_SIZE),
290
- rightMostIndex: secret.index + WINDOW_HALF_SIZE,
366
+ rightMostIndex: secret.index + WINDOW_HALF_SIZE
291
367
  };
292
368
  });
293
369
  // As we iterate we store the largest index we have seen for a given secret to later on store it in the db.
294
370
  const newLargestIndexMapToStore = {};
295
371
  // The initial/unmodified indexes of the secrets stored in a key-value map where key is the app tagging secret.
296
372
  const initialIndexesMap = getInitialIndexesMap(secrets);
297
- while (secretsAndWindows.length > 0) {
373
+ while(secretsAndWindows.length > 0){
298
374
  const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
299
- const tagsForTheWholeWindow = await Promise.all(secretsForTheWholeWindow.map(secret => secret.computeSiloedTag(recipient, contractAddress)));
375
+ const tagsForTheWholeWindow = await Promise.all(secretsForTheWholeWindow.map((secret)=>secret.computeSiloedTag(recipient, contractAddress)));
300
376
  // We store the new largest indexes we find in the iteration in the following map to later on construct
301
377
  // a new set of secrets and windows to fetch logs for.
302
378
  const newLargestIndexMapForIteration = {};
303
379
  // Fetch the logs for the tags and iterate over them
304
380
  const logsByTags = await this.aztecNode.getLogsByTags(tagsForTheWholeWindow);
305
- logsByTags.forEach((logsByTag, logIndex) => {
381
+ logsByTags.forEach((logsByTag, logIndex)=>{
306
382
  if (logsByTag.length > 0) {
307
383
  // Check that public logs have the correct contract address
308
- const checkedLogsbyTag = logsByTag.filter(l => !l.isFromPublic || PublicLog.fromBuffer(l.logData).contractAddress.equals(contractAddress));
384
+ const checkedLogsbyTag = logsByTag.filter((l)=>!l.isFromPublic || PublicLog.fromBuffer(l.logData).contractAddress.equals(contractAddress));
309
385
  if (checkedLogsbyTag.length < logsByTag.length) {
310
- const discarded = logsByTag.filter(log => checkedLogsbyTag.find(filteredLog => filteredLog.equals(log)) === undefined);
311
- this.log.warn(`Discarded ${logsByTag.length - checkedLogsbyTag.length} public logs with mismatched contract address ${contractAddress}:`, discarded.map(l => PublicLog.fromBuffer(l.logData)));
386
+ const discarded = logsByTag.filter((log)=>checkedLogsbyTag.find((filteredLog)=>filteredLog.equals(log)) === undefined);
387
+ this.log.warn(`Discarded ${logsByTag.length - checkedLogsbyTag.length} public logs with mismatched contract address ${contractAddress}:`, discarded.map((l)=>PublicLog.fromBuffer(l.logData)));
312
388
  }
313
389
  // The logs for the given tag exist so we store them for later processing
314
390
  logsForRecipient.push(...checkedLogsbyTag);
@@ -320,16 +396,12 @@ export class SimulatorOracle {
320
396
  recipient,
321
397
  secret: secretCorrespondingToLog.appTaggingSecret,
322
398
  contractName,
323
- contractAddress,
399
+ contractAddress
324
400
  });
325
- if (secretCorrespondingToLog.index >= initialIndex &&
326
- (newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined ||
327
- secretCorrespondingToLog.index >=
328
- newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()])) {
401
+ if (secretCorrespondingToLog.index >= initialIndex && (newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined || secretCorrespondingToLog.index >= newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()])) {
329
402
  // We have found a new largest index so we store it for later processing (storing it in the db + fetching
330
403
  // the difference of the window sets of current and the next iteration)
331
- newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] =
332
- secretCorrespondingToLog.index + 1;
404
+ newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] = secretCorrespondingToLog.index + 1;
333
405
  this.log.debug(`Incrementing index to ${secretCorrespondingToLog.index + 1} at contract ${contractName}(${contractAddress})`);
334
406
  }
335
407
  }
@@ -338,19 +410,18 @@ export class SimulatorOracle {
338
410
  // for. Note that it's very unlikely that a new log from the current window would appear between the iterations
339
411
  // so we fetch the logs only for the difference of the window sets.
340
412
  const newSecretsAndWindows = [];
341
- for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) {
342
- const secret = secrets.find(secret => secret.appTaggingSecret.toString() === appTaggingSecret);
413
+ for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)){
414
+ const secret = secrets.find((secret)=>secret.appTaggingSecret.toString() === appTaggingSecret);
343
415
  if (secret) {
344
416
  newSecretsAndWindows.push({
345
417
  appTaggingSecret: secret.appTaggingSecret,
346
418
  // We set the left most index to the new index to avoid fetching the same logs again
347
419
  leftMostIndex: newIndex,
348
- rightMostIndex: newIndex + WINDOW_HALF_SIZE,
420
+ rightMostIndex: newIndex + WINDOW_HALF_SIZE
349
421
  });
350
422
  // We store the new largest index in the map to later store it in the db.
351
423
  newLargestIndexMapToStore[appTaggingSecret] = newIndex;
352
- }
353
- else {
424
+ } else {
354
425
  throw new Error(`Secret not found for appTaggingSecret ${appTaggingSecret}. This is a bug as it should never happen!`);
355
426
  }
356
427
  }
@@ -358,24 +429,59 @@ export class SimulatorOracle {
358
429
  secretsAndWindows = newSecretsAndWindows;
359
430
  }
360
431
  // We filter the logs by block number and store them in the map.
361
- logsMap.set(recipient.toString(), logsForRecipient.filter(log => log.blockNumber <= maxBlockNumber));
432
+ logsMap.set(recipient.toString(), logsForRecipient.filter((log)=>log.blockNumber <= maxBlockNumber));
362
433
  // At this point we have processed all the logs for the recipient so we store the new largest indexes in the db.
363
- await this.db.setTaggingSecretsIndexesAsRecipient(Object.entries(newLargestIndexMapToStore).map(([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index)));
434
+ await this.db.setTaggingSecretsIndexesAsRecipient(Object.entries(newLargestIndexMapToStore).map(([appTaggingSecret, index])=>new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index)));
364
435
  }
365
436
  return logsMap;
366
437
  }
367
438
  /**
368
- * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database.
369
- * @param logs - The logs to process.
370
- * @param recipient - The recipient of the logs.
371
- */
372
- async processTaggedLogs(logs, recipient, simulator) {
373
- const decryptedLogs = await __classPrivateFieldGet(this, _SimulatorOracle_instances, "m", _SimulatorOracle_decryptTaggedLogs).call(this, logs, recipient);
439
+ * Decrypts logs tagged for a recipient and returns them.
440
+ * @param scopedLogs - The logs to decrypt.
441
+ * @param recipient - The recipient of the logs.
442
+ * @returns The decrypted notes.
443
+ */ async #decryptTaggedLogs(scopedLogs, recipient) {
444
+ const recipientCompleteAddress = await this.getCompleteAddress(recipient);
445
+ const ivskM = await this.keyStore.getMasterSecretKey(recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey);
446
+ const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
447
+ // Since we could have notes with the same index for different txs, we need
448
+ // to keep track of them scoping by txHash
449
+ const excludedIndices = new Map();
450
+ const decrypted = [];
451
+ for (const scopedLog of scopedLogs){
452
+ const payload = scopedLog.isFromPublic ? await L1NotePayload.decryptAsIncomingFromPublic(PublicLog.fromBuffer(scopedLog.logData), addressSecret) : await L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
453
+ if (!payload) {
454
+ this.log.verbose('Unable to decrypt log');
455
+ continue;
456
+ }
457
+ if (!excludedIndices.has(scopedLog.txHash.toString())) {
458
+ excludedIndices.set(scopedLog.txHash.toString(), new Set());
459
+ }
460
+ const note = await getOrderedNoteItems(this.db, payload);
461
+ const plaintext = [
462
+ payload.storageSlot,
463
+ payload.noteTypeId.toField(),
464
+ ...note.items
465
+ ];
466
+ decrypted.push({
467
+ plaintext,
468
+ txHash: scopedLog.txHash,
469
+ contractAddress: payload.contractAddress
470
+ });
471
+ }
472
+ return decrypted;
473
+ }
474
+ /**
475
+ * Processes the tagged logs returned by syncTaggedLogs by decrypting them and storing them in the database.
476
+ * @param logs - The logs to process.
477
+ * @param recipient - The recipient of the logs.
478
+ */ async processTaggedLogs(logs, recipient, simulator) {
479
+ const decryptedLogs = await this.#decryptTaggedLogs(logs, recipient);
374
480
  // We've produced the full NoteDao, which we'd be able to simply insert into the database. However, this is
375
481
  // only a temporary measure as we migrate from the PXE-driven discovery into the new contract-driven approach. We
376
482
  // discard most of the work done up to this point and reconstruct the note plaintext to then hand over to the
377
483
  // contract for further processing.
378
- for (const decryptedLog of decryptedLogs) {
484
+ for (const decryptedLog of decryptedLogs){
379
485
  // Log processing requires the note hashes in the tx in which the note was created. We are now assuming that the
380
486
  // note was included in the same block in which the log was delivered - note that partial notes will not work this
381
487
  // way.
@@ -391,32 +497,71 @@ export class SimulatorOracle {
391
497
  // Called when notes are delivered, usually as a result to a call to the process_log contract function
392
498
  async deliverNote(contractAddress, storageSlot, nonce, content, noteHash, nullifier, txHash, recipient) {
393
499
  const noteDao = await this.produceNoteDao(contractAddress, storageSlot, nonce, content, noteHash, nullifier, txHash, recipient);
394
- await this.db.addNotes([noteDao], recipient);
500
+ await this.db.addNotes([
501
+ noteDao
502
+ ], recipient);
395
503
  this.log.verbose('Added note', {
396
504
  contract: noteDao.contractAddress,
397
505
  slot: noteDao.storageSlot,
398
- nullifier: noteDao.siloedNullifier.toString,
506
+ nullifier: noteDao.siloedNullifier.toString
399
507
  });
400
508
  }
509
+ async getLogByTag(tag) {
510
+ const logs = await this.aztecNode.getLogsByTags([
511
+ tag
512
+ ]);
513
+ const logsForTag = logs[0];
514
+ this.log.debug(`Got ${logsForTag.length} logs for tag ${tag}`);
515
+ if (logsForTag.length == 0) {
516
+ return null;
517
+ } else if (logsForTag.length > 1) {
518
+ // TODO(#11627): handle this case
519
+ throw new Error(`Got ${logsForTag.length} logs for tag ${tag}. getLogByTag currently only supports a single log per tag`);
520
+ }
521
+ const log = logsForTag[0];
522
+ // getLogsByTag doesn't have all of the information that we need (notably note hashes and the first nullifier), so
523
+ // we need to make a second call to the node for `getTxEffect`.
524
+ // TODO(#9789): bundle this information in the `getLogsByTag` call.
525
+ const txEffect = await this.aztecNode.getTxEffect(log.txHash);
526
+ if (txEffect == undefined) {
527
+ throw new Error(`Unexpected: failed to retrieve tx effects for tx ${log.txHash} which is known to exist`);
528
+ }
529
+ const reader = BufferReader.asReader(log.logData);
530
+ const logArray = reader.readArray(PUBLIC_LOG_DATA_SIZE_IN_FIELDS, Fr);
531
+ // Public logs always take up all available fields by padding with zeroes, and the length of the originally emitted
532
+ // log is lost. Until this is improved, we simply remove all of the zero elements (which are expected to be at the
533
+ // end).
534
+ // TODO(#11636): use the actual log length.
535
+ const trimmedLog = logArray.filter((x)=>!x.isZero());
536
+ return new LogWithTxData(trimmedLog, log.txHash.hash, txEffect.data.noteHashes, txEffect.data.nullifiers[0]);
537
+ }
401
538
  async removeNullifiedNotes(contractAddress) {
402
- this.log.verbose('Removing nullified notes', { contract: contractAddress });
403
- for (const recipient of await this.keyStore.getAccounts()) {
404
- const currentNotesForRecipient = await this.db.getNotes({ contractAddress, owner: recipient });
405
- const nullifiersToCheck = currentNotesForRecipient.map(note => note.siloedNullifier);
539
+ this.log.verbose('Searching for nullifiers of known notes', {
540
+ contract: contractAddress
541
+ });
542
+ for (const recipient of (await this.keyStore.getAccounts())){
543
+ const currentNotesForRecipient = await this.db.getNotes({
544
+ contractAddress,
545
+ owner: recipient
546
+ });
547
+ const nullifiersToCheck = currentNotesForRecipient.map((note)=>note.siloedNullifier);
406
548
  const nullifierIndexes = await this.aztecNode.findNullifiersIndexesWithBlock('latest', nullifiersToCheck);
407
- const foundNullifiers = nullifiersToCheck
408
- .map((nullifier, i) => {
549
+ const foundNullifiers = nullifiersToCheck.map((nullifier, i)=>{
409
550
  if (nullifierIndexes[i] !== undefined) {
410
- return { ...nullifierIndexes[i], ...{ data: nullifier } };
551
+ return {
552
+ ...nullifierIndexes[i],
553
+ ...{
554
+ data: nullifier
555
+ }
556
+ };
411
557
  }
412
- })
413
- .filter(nullifier => nullifier !== undefined);
558
+ }).filter((nullifier)=>nullifier !== undefined);
414
559
  const nullifiedNotes = await this.db.removeNullifiedNotes(foundNullifiers, await recipient.toAddressPoint());
415
- nullifiedNotes.forEach(noteDao => {
560
+ nullifiedNotes.forEach((noteDao)=>{
416
561
  this.log.verbose(`Removed note for contract ${noteDao.contractAddress} at slot ${noteDao.storageSlot}`, {
417
562
  contract: noteDao.contractAddress,
418
563
  slot: noteDao.storageSlot,
419
- nullifier: noteDao.siloedNullifier.toString(),
564
+ nullifier: noteDao.siloedNullifier.toString()
420
565
  });
421
566
  });
422
567
  }
@@ -437,7 +582,9 @@ export class SimulatorOracle {
437
582
  // locally synced block number which *should* be recent enough to be available. We avoid querying at 'latest' since
438
583
  // we want to avoid accidentally processing notes that only exist ahead in time of the locally synced state.
439
584
  const syncedBlockNumber = await this.db.getBlockNumber();
440
- const uniqueNoteHashTreeIndex = (await this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NOTE_HASH_TREE, [uniqueNoteHash]))[0];
585
+ const uniqueNoteHashTreeIndex = (await this.aztecNode.findLeavesIndexes(syncedBlockNumber, MerkleTreeId.NOTE_HASH_TREE, [
586
+ uniqueNoteHash
587
+ ]))[0];
441
588
  if (uniqueNoteHashTreeIndex === undefined) {
442
589
  throw new Error(`Note hash ${noteHash} (uniqued as ${uniqueNoteHash}) is not present on the tree at block ${syncedBlockNumber} (from tx ${txHash})`);
443
590
  }
@@ -448,10 +595,11 @@ export class SimulatorOracle {
448
595
  if (!artifact) {
449
596
  throw new Error(`Mandatory implementation of "process_log" missing in noir contract ${contractAddress.toString()}.`);
450
597
  }
598
+ const selector = await FunctionSelector.fromNameAndParameters(artifact);
451
599
  const execRequest = {
452
600
  name: artifact.name,
453
601
  to: contractAddress,
454
- selector: await FunctionSelector.fromNameAndParameters(artifact),
602
+ selector,
455
603
  type: FunctionType.UNCONSTRAINED,
456
604
  isStatic: artifact.isStatic,
457
605
  args: encodeArguments(artifact, [
@@ -459,12 +607,11 @@ export class SimulatorOracle {
459
607
  txHash.toString(),
460
608
  toBoundedVec(noteHashes, MAX_NOTE_HASHES_PER_TX),
461
609
  firstNullifier,
462
- recipient,
610
+ recipient
463
611
  ]),
464
- returnTypes: artifact.returnTypes,
612
+ returnTypes: artifact.returnTypes
465
613
  };
466
- await (simulator ??
467
- getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle)).runUnconstrained(execRequest, artifact, contractAddress, []);
614
+ await (simulator ?? getAcirSimulator(this.db, this.aztecNode, this.keyStore, this.simulationProvider, this.contractDataOracle)).runUnconstrained(execRequest, contractAddress, selector, []);
468
615
  }
469
616
  storeCapsule(contractAddress, slot, capsule) {
470
617
  return this.db.storeCapsule(contractAddress, slot, capsule);
@@ -479,84 +626,9 @@ export class SimulatorOracle {
479
626
  return this.db.copyCapsule(contractAddress, srcSlot, dstSlot, numEntries);
480
627
  }
481
628
  }
482
- _SimulatorOracle_instances = new WeakSet(), _SimulatorOracle_findLeafIndex = async function _SimulatorOracle_findLeafIndex(blockNumber, treeId, leafValue) {
483
- const [leafIndex] = await this.aztecNode.findLeavesIndexes(blockNumber, treeId, [leafValue]);
484
- return leafIndex;
485
- }, _SimulatorOracle_getSiblingPath = async function _SimulatorOracle_getSiblingPath(blockNumber, treeId, leafIndex) {
486
- switch (treeId) {
487
- case MerkleTreeId.NULLIFIER_TREE:
488
- return (await this.aztecNode.getNullifierSiblingPath(blockNumber, leafIndex)).toFields();
489
- case MerkleTreeId.NOTE_HASH_TREE:
490
- return (await this.aztecNode.getNoteHashSiblingPath(blockNumber, leafIndex)).toFields();
491
- case MerkleTreeId.PUBLIC_DATA_TREE:
492
- return (await this.aztecNode.getPublicDataSiblingPath(blockNumber, leafIndex)).toFields();
493
- case MerkleTreeId.ARCHIVE:
494
- return (await this.aztecNode.getArchiveSiblingPath(blockNumber, leafIndex)).toFields();
495
- default:
496
- throw new Error('Not implemented');
497
- }
498
- }, _SimulatorOracle_calculateAppTaggingSecret = async function _SimulatorOracle_calculateAppTaggingSecret(contractAddress, sender, recipient) {
499
- const senderCompleteAddress = await this.getCompleteAddress(sender);
500
- const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
501
- const secretPoint = await computeTaggingSecretPoint(senderCompleteAddress, senderIvsk, recipient);
502
- // Silo the secret so it can't be used to track other app's notes
503
- const appSecret = poseidon2Hash([secretPoint.x, secretPoint.y, contractAddress]);
504
- return appSecret;
505
- }, _SimulatorOracle_getIndexedTaggingSecretsForSenders =
506
- /**
507
- * Returns the indexed tagging secrets for a given recipient and all the senders in the address book
508
- * This method should be exposed as an oracle call to allow aztec.nr to perform the orchestration
509
- * of the syncTaggedLogs and processTaggedLogs methods. However, it is not possible to do so at the moment,
510
- * so we're keeping it private for now.
511
- * @param contractAddress - The contract address to silo the secret for
512
- * @param recipient - The address receiving the notes
513
- * @returns A list of indexed tagging secrets
514
- */
515
- async function _SimulatorOracle_getIndexedTaggingSecretsForSenders(contractAddress, recipient) {
516
- const recipientCompleteAddress = await this.getCompleteAddress(recipient);
517
- const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
518
- // We implicitly add all PXE accounts as senders, this helps us decrypt tags on notes that we send to ourselves
519
- // (recipient = us, sender = us)
520
- const senders = [...(await this.db.getSenderAddresses()), ...(await this.keyStore.getAccounts())].filter((address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)));
521
- const appTaggingSecrets = await Promise.all(senders.map(async (contact) => {
522
- const sharedSecret = await computeTaggingSecretPoint(recipientCompleteAddress, recipientIvsk, contact);
523
- return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
524
- }));
525
- const indexes = await this.db.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets);
526
- return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
527
- }, _SimulatorOracle_decryptTaggedLogs =
528
- /**
529
- * Decrypts logs tagged for a recipient and returns them.
530
- * @param scopedLogs - The logs to decrypt.
531
- * @param recipient - The recipient of the logs.
532
- * @returns The decrypted notes.
533
- */
534
- async function _SimulatorOracle_decryptTaggedLogs(scopedLogs, recipient) {
535
- const recipientCompleteAddress = await this.getCompleteAddress(recipient);
536
- const ivskM = await this.keyStore.getMasterSecretKey(recipientCompleteAddress.publicKeys.masterIncomingViewingPublicKey);
537
- const addressSecret = await computeAddressSecret(await recipientCompleteAddress.getPreaddress(), ivskM);
538
- // Since we could have notes with the same index for different txs, we need
539
- // to keep track of them scoping by txHash
540
- const excludedIndices = new Map();
541
- const decrypted = [];
542
- for (const scopedLog of scopedLogs) {
543
- const payload = scopedLog.isFromPublic
544
- ? await L1NotePayload.decryptAsIncomingFromPublic(PublicLog.fromBuffer(scopedLog.logData), addressSecret)
545
- : await L1NotePayload.decryptAsIncoming(PrivateLog.fromBuffer(scopedLog.logData), addressSecret);
546
- if (!payload) {
547
- this.log.verbose('Unable to decrypt log');
548
- continue;
549
- }
550
- if (!excludedIndices.has(scopedLog.txHash.toString())) {
551
- excludedIndices.set(scopedLog.txHash.toString(), new Set());
552
- }
553
- const note = await getOrderedNoteItems(this.db, payload);
554
- const plaintext = [payload.storageSlot, payload.noteTypeId.toField(), ...note.items];
555
- decrypted.push({ plaintext, txHash: scopedLog.txHash, contractAddress: payload.contractAddress });
556
- }
557
- return decrypted;
558
- };
559
629
  function toBoundedVec(array, maxLength) {
560
- return { storage: array.concat(Array(maxLength - array.length).fill(new Fr(0))), len: array.length };
630
+ return {
631
+ storage: array.concat(Array(maxLength - array.length).fill(new Fr(0))),
632
+ len: array.length
633
+ };
561
634
  }
562
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2ltdWxhdG9yX29yYWNsZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE9BQU8sRUFJTCxhQUFhLEVBR2IsWUFBWSxFQUNaLElBQUksRUFJSixNQUFNLEVBRU4sbUNBQW1DLEdBQ3BDLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUtMLEVBQUUsRUFDRixnQkFBZ0IsRUFDaEIsb0JBQW9CLEVBR3BCLHNCQUFzQixFQUN0QiwwQkFBMEIsRUFDMUIsVUFBVSxFQUNWLFNBQVMsRUFDVCxvQkFBb0IsRUFDcEIseUJBQXlCLEdBQzFCLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUFFLHFCQUFxQixFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM3RixPQUFPLEVBRUwsWUFBWSxFQUNaLFlBQVksRUFDWixlQUFlLEVBQ2YsbUJBQW1CLEdBQ3BCLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzdELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFckQsT0FBTyxFQUdMLHVCQUF1QixHQUV4QixNQUFNLHlCQUF5QixDQUFDO0FBRWpDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRXRFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNsRCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSwwREFBMEQsQ0FBQztBQUMvRixPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsb0NBQW9DLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxv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