@aztec/pxe 0.60.0 → 0.62.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.
- package/dest/database/deferred_note_dao.d.ts +8 -21
- package/dest/database/deferred_note_dao.d.ts.map +1 -1
- package/dest/database/deferred_note_dao.js +9 -19
- package/dest/database/incoming_note_dao.d.ts +3 -3
- package/dest/database/incoming_note_dao.d.ts.map +1 -1
- package/dest/database/incoming_note_dao.js +6 -6
- package/dest/database/kv_pxe_database.d.ts +11 -6
- package/dest/database/kv_pxe_database.d.ts.map +1 -1
- package/dest/database/kv_pxe_database.js +75 -42
- package/dest/database/outgoing_note_dao.d.ts +1 -1
- package/dest/database/outgoing_note_dao.d.ts.map +1 -1
- package/dest/database/outgoing_note_dao.js +3 -3
- package/dest/database/pxe_database.d.ts +37 -5
- package/dest/database/pxe_database.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.d.ts.map +1 -1
- package/dest/database/pxe_database_test_suite.js +11 -11
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -1
- package/dest/note_processor/note_processor.d.ts +1 -5
- package/dest/note_processor/note_processor.d.ts.map +1 -1
- package/dest/note_processor/note_processor.js +57 -58
- package/dest/note_processor/utils/add_public_values_to_payload.d.ts +10 -0
- package/dest/note_processor/utils/add_public_values_to_payload.d.ts.map +1 -0
- package/dest/note_processor/utils/add_public_values_to_payload.js +48 -0
- package/dest/note_processor/utils/brute_force_note_info.d.ts +8 -3
- package/dest/note_processor/utils/brute_force_note_info.d.ts.map +1 -1
- package/dest/note_processor/utils/brute_force_note_info.js +6 -3
- package/dest/note_processor/utils/produce_note_daos.d.ts +2 -2
- package/dest/note_processor/utils/produce_note_daos.d.ts.map +1 -1
- package/dest/note_processor/utils/produce_note_daos.js +8 -10
- package/dest/note_processor/utils/produce_note_daos_for_key.d.ts +3 -3
- package/dest/note_processor/utils/produce_note_daos_for_key.d.ts.map +1 -1
- package/dest/note_processor/utils/produce_note_daos_for_key.js +7 -61
- package/dest/pxe_http/pxe_http_server.d.ts.map +1 -1
- package/dest/pxe_http/pxe_http_server.js +23 -21
- package/dest/pxe_service/pxe_service.d.ts +15 -7
- package/dest/pxe_service/pxe_service.d.ts.map +1 -1
- package/dest/pxe_service/pxe_service.js +59 -42
- package/dest/pxe_service/test/pxe_test_suite.d.ts.map +1 -1
- package/dest/pxe_service/test/pxe_test_suite.js +2 -32
- package/dest/simulator_oracle/index.d.ts +41 -2
- package/dest/simulator_oracle/index.d.ts.map +1 -1
- package/dest/simulator_oracle/index.js +104 -2
- package/dest/synchronizer/synchronizer.d.ts +1 -1
- package/dest/synchronizer/synchronizer.d.ts.map +1 -1
- package/dest/synchronizer/synchronizer.js +8 -8
- package/package.json +16 -14
- package/src/database/deferred_note_dao.ts +7 -20
- package/src/database/incoming_note_dao.ts +6 -5
- package/src/database/kv_pxe_database.ts +91 -45
- package/src/database/outgoing_note_dao.ts +4 -3
- package/src/database/pxe_database.ts +42 -4
- package/src/database/pxe_database_test_suite.ts +12 -20
- package/src/index.ts +1 -1
- package/src/note_processor/note_processor.ts +91 -87
- package/src/note_processor/utils/add_public_values_to_payload.ts +63 -0
- package/src/note_processor/utils/brute_force_note_info.ts +11 -3
- package/src/note_processor/utils/produce_note_daos.ts +11 -13
- package/src/note_processor/utils/produce_note_daos_for_key.ts +19 -114
- package/src/pxe_http/pxe_http_server.ts +22 -19
- package/src/pxe_service/pxe_service.ts +71 -46
- package/src/pxe_service/test/pxe_test_suite.ts +1 -53
- package/src/simulator_oracle/index.ts +131 -1
- package/src/synchronizer/synchronizer.ts +7 -7
- package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts +0 -12
- package/dest/note_processor/utils/add_nullable_field_to_payload.d.ts.map +0 -1
- package/dest/note_processor/utils/add_nullable_field_to_payload.js +0 -46
- package/src/note_processor/utils/add_nullable_field_to_payload.ts +0 -67
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { type AztecNode, L1NotePayload, type L2Block } from '@aztec/circuit-types';
|
|
2
2
|
import { type NoteProcessorStats } from '@aztec/circuit-types/stats';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type CompleteAddress,
|
|
5
|
+
INITIAL_L2_BLOCK_NUM,
|
|
6
|
+
MAX_NOTE_HASHES_PER_TX,
|
|
7
|
+
computeAddressSecret,
|
|
8
|
+
computePoint,
|
|
9
|
+
} from '@aztec/circuits.js';
|
|
4
10
|
import { type Fr } from '@aztec/foundation/fields';
|
|
5
11
|
import { type Logger, createDebugLogger } from '@aztec/foundation/log';
|
|
6
12
|
import { Timer } from '@aztec/foundation/timer';
|
|
@@ -48,10 +54,6 @@ export class NoteProcessor {
|
|
|
48
54
|
|
|
49
55
|
private constructor(
|
|
50
56
|
public readonly account: CompleteAddress,
|
|
51
|
-
/** The public counterpart to the secret key to be used in the decryption of incoming note logs. */
|
|
52
|
-
private readonly ivpkM: PublicKey,
|
|
53
|
-
/** The public counterpart to the secret key to be used in the decryption of outgoing note logs. */
|
|
54
|
-
private readonly ovpkM: PublicKey,
|
|
55
57
|
private keyStore: KeyStore,
|
|
56
58
|
private db: PxeDatabase,
|
|
57
59
|
private node: AztecNode,
|
|
@@ -60,7 +62,7 @@ export class NoteProcessor {
|
|
|
60
62
|
private log: Logger,
|
|
61
63
|
) {}
|
|
62
64
|
|
|
63
|
-
public static
|
|
65
|
+
public static create(
|
|
64
66
|
account: CompleteAddress,
|
|
65
67
|
keyStore: KeyStore,
|
|
66
68
|
db: PxeDatabase,
|
|
@@ -69,10 +71,7 @@ export class NoteProcessor {
|
|
|
69
71
|
simulator = getAcirSimulator(db, node, keyStore),
|
|
70
72
|
log = createDebugLogger('aztec:note_processor'),
|
|
71
73
|
) {
|
|
72
|
-
|
|
73
|
-
const ovpkM = await keyStore.getMasterOutgoingViewingPublicKey(account.address);
|
|
74
|
-
|
|
75
|
-
return new NoteProcessor(account, ivpkM, ovpkM, keyStore, db, node, startingBlock, simulator, log);
|
|
74
|
+
return new NoteProcessor(account, keyStore, db, node, startingBlock, simulator, log);
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
/**
|
|
@@ -95,7 +94,7 @@ export class NoteProcessor {
|
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
private getSyncedToBlock(): number {
|
|
98
|
-
return this.db.
|
|
97
|
+
return this.db.getSynchedBlockNumberForAccount(this.account.address) ?? this.startingBlock - 1;
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
/**
|
|
@@ -114,13 +113,17 @@ export class NoteProcessor {
|
|
|
114
113
|
const deferredIncomingNotes: DeferredNoteDao[] = [];
|
|
115
114
|
const deferredOutgoingNotes: DeferredNoteDao[] = [];
|
|
116
115
|
|
|
117
|
-
const ivskM = await this.keyStore.getMasterSecretKey(this.
|
|
118
|
-
const
|
|
116
|
+
const ivskM = await this.keyStore.getMasterSecretKey(this.account.publicKeys.masterIncomingViewingPublicKey);
|
|
117
|
+
const addressSecret = computeAddressSecret(this.account.getPreaddress(), ivskM);
|
|
118
|
+
|
|
119
|
+
const ovskM = await this.keyStore.getMasterSecretKey(this.account.publicKeys.masterOutgoingViewingPublicKey);
|
|
119
120
|
|
|
120
121
|
// Iterate over both blocks and encrypted logs.
|
|
121
122
|
for (const block of blocks) {
|
|
122
123
|
this.stats.blocks++;
|
|
123
|
-
const { txLogs } = block.body.noteEncryptedLogs;
|
|
124
|
+
const { txLogs: encryptedTxLogs } = block.body.noteEncryptedLogs;
|
|
125
|
+
const { txLogs: unencryptedTxLogs } = block.body.unencryptedLogs;
|
|
126
|
+
|
|
124
127
|
const dataStartIndexForBlock =
|
|
125
128
|
block.header.state.partial.noteHashTree.nextAvailableLeafIndex -
|
|
126
129
|
block.body.numberOfTxsIncludingPadded * MAX_NOTE_HASHES_PER_TX;
|
|
@@ -131,65 +134,77 @@ export class NoteProcessor {
|
|
|
131
134
|
const outgoingNotes: OutgoingNoteDao[] = [];
|
|
132
135
|
|
|
133
136
|
// Iterate over all the encrypted logs and try decrypting them. If successful, store the note.
|
|
134
|
-
for (let indexOfTxInABlock = 0; indexOfTxInABlock <
|
|
137
|
+
for (let indexOfTxInABlock = 0; indexOfTxInABlock < encryptedTxLogs.length; ++indexOfTxInABlock) {
|
|
135
138
|
this.stats.txs++;
|
|
136
139
|
const dataStartIndexForTx = dataStartIndexForBlock + indexOfTxInABlock * MAX_NOTE_HASHES_PER_TX;
|
|
137
140
|
const noteHashes = block.body.txEffects[indexOfTxInABlock].noteHashes;
|
|
138
141
|
// Note: Each tx generates a `TxL2Logs` object and for this reason we can rely on its index corresponding
|
|
139
142
|
// to the index of a tx in a block.
|
|
140
|
-
const
|
|
143
|
+
const encryptedTxFunctionLogs = encryptedTxLogs[indexOfTxInABlock].functionLogs;
|
|
144
|
+
const unencryptedTxFunctionLogs = unencryptedTxLogs[indexOfTxInABlock].functionLogs;
|
|
141
145
|
const excludedIndices: Set<number> = new Set();
|
|
142
|
-
for (const functionLogs of txFunctionLogs) {
|
|
143
|
-
for (const log of functionLogs.logs) {
|
|
144
|
-
this.stats.seen++;
|
|
145
|
-
const incomingNotePayload = L1NotePayload.decryptAsIncoming(log, ivskM);
|
|
146
|
-
const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(log, ovskM);
|
|
147
|
-
|
|
148
|
-
if (incomingNotePayload || outgoingNotePayload) {
|
|
149
|
-
if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) {
|
|
150
|
-
throw new Error(
|
|
151
|
-
`Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify(
|
|
152
|
-
incomingNotePayload,
|
|
153
|
-
)}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
146
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
dataStartIndexForTx,
|
|
169
|
-
excludedIndices,
|
|
170
|
-
this.log,
|
|
171
|
-
txEffect.unencryptedLogs,
|
|
147
|
+
// We iterate over both encrypted and unencrypted logs to decrypt the notes since partial notes are passed
|
|
148
|
+
// via the unencrypted logs stream.
|
|
149
|
+
for (const txFunctionLogs of [encryptedTxFunctionLogs, unencryptedTxFunctionLogs]) {
|
|
150
|
+
const isFromPublic = txFunctionLogs === unencryptedTxFunctionLogs;
|
|
151
|
+
for (const functionLogs of txFunctionLogs) {
|
|
152
|
+
for (const unprocessedLog of functionLogs.logs) {
|
|
153
|
+
this.stats.seen++;
|
|
154
|
+
const incomingNotePayload = L1NotePayload.decryptAsIncoming(
|
|
155
|
+
unprocessedLog.data,
|
|
156
|
+
addressSecret,
|
|
157
|
+
isFromPublic,
|
|
172
158
|
);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
159
|
+
const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(unprocessedLog.data, ovskM, isFromPublic);
|
|
160
|
+
|
|
161
|
+
if (incomingNotePayload || outgoingNotePayload) {
|
|
162
|
+
if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Incoming and outgoing note payloads do not match. Incoming: ${JSON.stringify(
|
|
165
|
+
incomingNotePayload,
|
|
166
|
+
)}, Outgoing: ${JSON.stringify(outgoingNotePayload)}`,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const payload = incomingNotePayload || outgoingNotePayload;
|
|
171
|
+
|
|
172
|
+
const txEffect = block.body.txEffects[indexOfTxInABlock];
|
|
173
|
+
const { incomingNote, outgoingNote, incomingDeferredNote, outgoingDeferredNote } =
|
|
174
|
+
await produceNoteDaos(
|
|
175
|
+
this.simulator,
|
|
176
|
+
this.db,
|
|
177
|
+
incomingNotePayload ? computePoint(this.account.address) : undefined,
|
|
178
|
+
outgoingNotePayload ? this.account.publicKeys.masterOutgoingViewingPublicKey : undefined,
|
|
179
|
+
payload!,
|
|
180
|
+
txEffect.txHash,
|
|
181
|
+
noteHashes,
|
|
182
|
+
dataStartIndexForTx,
|
|
183
|
+
excludedIndices,
|
|
184
|
+
this.log,
|
|
185
|
+
txEffect.unencryptedLogs,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (incomingNote) {
|
|
189
|
+
incomingNotes.push(incomingNote);
|
|
190
|
+
this.stats.decryptedIncoming++;
|
|
191
|
+
}
|
|
192
|
+
if (outgoingNote) {
|
|
193
|
+
outgoingNotes.push(outgoingNote);
|
|
194
|
+
this.stats.decryptedOutgoing++;
|
|
195
|
+
}
|
|
196
|
+
if (incomingDeferredNote) {
|
|
197
|
+
deferredIncomingNotes.push(incomingDeferredNote);
|
|
198
|
+
this.stats.deferredIncoming++;
|
|
199
|
+
}
|
|
200
|
+
if (outgoingDeferredNote) {
|
|
201
|
+
deferredOutgoingNotes.push(outgoingDeferredNote);
|
|
202
|
+
this.stats.deferredOutgoing++;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (incomingNote == undefined && outgoingNote == undefined && incomingDeferredNote == undefined) {
|
|
206
|
+
this.stats.failed++;
|
|
207
|
+
}
|
|
193
208
|
}
|
|
194
209
|
}
|
|
195
210
|
}
|
|
@@ -207,7 +222,7 @@ export class NoteProcessor {
|
|
|
207
222
|
await this.processDeferredNotes(deferredIncomingNotes, deferredOutgoingNotes);
|
|
208
223
|
|
|
209
224
|
const syncedToBlock = blocks[blocks.length - 1].number;
|
|
210
|
-
await this.db.
|
|
225
|
+
await this.db.setSynchedBlockNumberForAccount(this.account.address, syncedToBlock);
|
|
211
226
|
|
|
212
227
|
this.log.debug(`Synched block ${syncedToBlock}`);
|
|
213
228
|
}
|
|
@@ -241,7 +256,7 @@ export class NoteProcessor {
|
|
|
241
256
|
const nullifiers: Fr[] = blocksAndNotes.flatMap(b =>
|
|
242
257
|
b.block.body.txEffects.flatMap(txEffect => txEffect.nullifiers),
|
|
243
258
|
);
|
|
244
|
-
const removedNotes = await this.db.removeNullifiedNotes(nullifiers, this.
|
|
259
|
+
const removedNotes = await this.db.removeNullifiedNotes(nullifiers, computePoint(this.account.address));
|
|
245
260
|
removedNotes.forEach(noteDao => {
|
|
246
261
|
this.log.verbose(
|
|
247
262
|
`Removed note for contract ${noteDao.contractAddress} at slot ${
|
|
@@ -265,15 +280,15 @@ export class NoteProcessor {
|
|
|
265
280
|
await this.db.addDeferredNotes([...deferredIncomingNotes, ...deferredOutgoingNotes]);
|
|
266
281
|
deferredIncomingNotes.forEach(noteDao => {
|
|
267
282
|
this.log.verbose(
|
|
268
|
-
`Deferred incoming note for contract ${noteDao.contractAddress} at slot ${
|
|
269
|
-
noteDao.storageSlot
|
|
283
|
+
`Deferred incoming note for contract ${noteDao.payload.contractAddress} at slot ${
|
|
284
|
+
noteDao.payload.storageSlot
|
|
270
285
|
} in tx ${noteDao.txHash.toString()}`,
|
|
271
286
|
);
|
|
272
287
|
});
|
|
273
288
|
deferredOutgoingNotes.forEach(noteDao => {
|
|
274
289
|
this.log.verbose(
|
|
275
|
-
`Deferred outgoing note for contract ${noteDao.contractAddress} at slot ${
|
|
276
|
-
noteDao.storageSlot
|
|
290
|
+
`Deferred outgoing note for contract ${noteDao.payload.contractAddress} at slot ${
|
|
291
|
+
noteDao.payload.storageSlot
|
|
277
292
|
} in tx ${noteDao.txHash.toString()}`,
|
|
278
293
|
);
|
|
279
294
|
});
|
|
@@ -298,21 +313,10 @@ export class NoteProcessor {
|
|
|
298
313
|
const outgoingNotes: OutgoingNoteDao[] = [];
|
|
299
314
|
|
|
300
315
|
for (const deferredNote of deferredNoteDaos) {
|
|
301
|
-
const {
|
|
302
|
-
publicKey,
|
|
303
|
-
note,
|
|
304
|
-
contractAddress,
|
|
305
|
-
storageSlot,
|
|
306
|
-
noteTypeId,
|
|
307
|
-
txHash,
|
|
308
|
-
noteHashes,
|
|
309
|
-
dataStartIndexForTx,
|
|
310
|
-
unencryptedLogs,
|
|
311
|
-
} = deferredNote;
|
|
312
|
-
const payload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId);
|
|
316
|
+
const { publicKey, payload, txHash, noteHashes, dataStartIndexForTx, unencryptedLogs } = deferredNote;
|
|
313
317
|
|
|
314
|
-
const isIncoming = publicKey.equals(this.
|
|
315
|
-
const isOutgoing = publicKey.equals(this.
|
|
318
|
+
const isIncoming = publicKey.equals(computePoint(this.account.address));
|
|
319
|
+
const isOutgoing = publicKey.equals(this.account.publicKeys.masterOutgoingViewingPublicKey);
|
|
316
320
|
|
|
317
321
|
if (!isIncoming && !isOutgoing) {
|
|
318
322
|
// The note does not belong to this note processor
|
|
@@ -322,8 +326,8 @@ export class NoteProcessor {
|
|
|
322
326
|
const { incomingNote, outgoingNote } = await produceNoteDaos(
|
|
323
327
|
this.simulator,
|
|
324
328
|
this.db,
|
|
325
|
-
isIncoming ? this.
|
|
326
|
-
isOutgoing ? this.
|
|
329
|
+
isIncoming ? computePoint(this.account.address) : undefined,
|
|
330
|
+
isOutgoing ? this.account.publicKeys.masterOutgoingViewingPublicKey : undefined,
|
|
327
331
|
payload,
|
|
328
332
|
txHash,
|
|
329
333
|
noteHashes,
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type L1NotePayload, Note } from '@aztec/circuit-types';
|
|
2
|
+
import { ContractNotFoundError } from '@aztec/simulator';
|
|
3
|
+
|
|
4
|
+
import { type PxeDatabase } from '../../database/pxe_database.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Merges privately and publicly delivered note values.
|
|
8
|
+
* @param db - PXE database used to fetch contract instance and artifact.
|
|
9
|
+
* @param payload - Payload corresponding to the note.
|
|
10
|
+
* @returns Note payload with public fields added.
|
|
11
|
+
*/
|
|
12
|
+
export async function getOrderedNoteItems(
|
|
13
|
+
db: PxeDatabase,
|
|
14
|
+
{ contractAddress, noteTypeId, privateNoteValues, publicNoteValues }: L1NotePayload,
|
|
15
|
+
): Promise<Note> {
|
|
16
|
+
if (publicNoteValues.length === 0) {
|
|
17
|
+
return new Note(privateNoteValues);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const instance = await db.getContractInstance(contractAddress);
|
|
21
|
+
if (!instance) {
|
|
22
|
+
throw new ContractNotFoundError(
|
|
23
|
+
`Could not find instance for ${contractAddress.toString()}. This should never happen here as the partial notes flow should be triggered only for non-deferred notes.`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const artifact = await db.getContractArtifact(instance.contractClassId);
|
|
28
|
+
if (!artifact) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`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.`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const noteFields = Object.values(artifact.notes).find(note => note.id.equals(noteTypeId))?.fields;
|
|
35
|
+
|
|
36
|
+
if (!noteFields) {
|
|
37
|
+
throw new Error(`Could not find note fields for note type ${noteTypeId.toString()}.`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// We sort note fields by index so that we can iterate over them in order.
|
|
41
|
+
noteFields.sort((a, b) => a.index - b.index);
|
|
42
|
+
|
|
43
|
+
// Now we insert the public fields into the note based on its indices defined in the ABI.
|
|
44
|
+
const modifiedNoteItems = privateNoteValues;
|
|
45
|
+
let indexInPublicValues = 0;
|
|
46
|
+
for (let i = 0; i < noteFields.length; i++) {
|
|
47
|
+
const noteField = noteFields[i];
|
|
48
|
+
if (noteField.nullable) {
|
|
49
|
+
if (i == noteFields.length - 1) {
|
|
50
|
+
// We are processing the last field so we simply insert the rest of the public fields at the end
|
|
51
|
+
modifiedNoteItems.push(...publicNoteValues.slice(indexInPublicValues));
|
|
52
|
+
} else {
|
|
53
|
+
const noteFieldLength = noteFields[i + 1].index - noteField.index;
|
|
54
|
+
const publicValuesToInsert = publicNoteValues.slice(indexInPublicValues, indexInPublicValues + noteFieldLength);
|
|
55
|
+
indexInPublicValues += noteFieldLength;
|
|
56
|
+
// Now we insert the public fields at the note field index
|
|
57
|
+
modifiedNoteItems.splice(noteField.index, 0, ...publicValuesToInsert);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new Note(modifiedNoteItems);
|
|
63
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Note, type TxHash } from '@aztec/circuit-types';
|
|
2
|
+
import { type AztecAddress } from '@aztec/circuits.js';
|
|
2
3
|
import { computeNoteHashNonce, siloNullifier } from '@aztec/circuits.js/hash';
|
|
4
|
+
import { type NoteSelector } from '@aztec/foundation/abi';
|
|
3
5
|
import { Fr } from '@aztec/foundation/fields';
|
|
4
6
|
import { type AcirSimulator } from '@aztec/simulator';
|
|
5
7
|
|
|
@@ -18,7 +20,10 @@ export interface NoteInfo {
|
|
|
18
20
|
* @remarks This method assists in identifying spent notes in the note hash tree.
|
|
19
21
|
* @param siloedNoteHashes - Note hashes in the tx. One of them should correspond to the note we are looking for
|
|
20
22
|
* @param txHash - Hash of a tx the note was emitted in.
|
|
21
|
-
* @param
|
|
23
|
+
* @param contractAddress - Address of the contract the note was emitted in.
|
|
24
|
+
* @param storageSlot - Storage slot of the note.
|
|
25
|
+
* @param noteTypeId - Type of the note.
|
|
26
|
+
* @param note - Note items.
|
|
22
27
|
* @param excludedIndices - Indices that have been assigned a note in the same tx. Notes in a tx can have the same
|
|
23
28
|
* l1NotePayload. We need to find a different index for each replicate.
|
|
24
29
|
* @param computeNullifier - A flag indicating whether to compute the nullifier or just return 0.
|
|
@@ -29,7 +34,10 @@ export async function bruteForceNoteInfo(
|
|
|
29
34
|
simulator: AcirSimulator,
|
|
30
35
|
siloedNoteHashes: Fr[],
|
|
31
36
|
txHash: TxHash,
|
|
32
|
-
|
|
37
|
+
contractAddress: AztecAddress,
|
|
38
|
+
storageSlot: Fr,
|
|
39
|
+
noteTypeId: NoteSelector,
|
|
40
|
+
note: Note,
|
|
33
41
|
excludedIndices: Set<number>,
|
|
34
42
|
computeNullifier: boolean,
|
|
35
43
|
): Promise<NoteInfo> {
|
|
@@ -17,7 +17,7 @@ import { produceNoteDaosForKey } from './produce_note_daos_for_key.js';
|
|
|
17
17
|
*
|
|
18
18
|
* @param simulator - An instance of AcirSimulator.
|
|
19
19
|
* @param db - An instance of PxeDatabase.
|
|
20
|
-
* @param
|
|
20
|
+
* @param addressPoint - The public counterpart to the address secret, which is used in the decryption of incoming note logs.
|
|
21
21
|
* @param ovpkM - The public counterpart to the secret key to be used in the decryption of outgoing note logs.
|
|
22
22
|
* @param payload - An instance of l1NotePayload.
|
|
23
23
|
* @param txHash - The hash of the transaction that created the note. Equivalent to the first nullifier of the transaction.
|
|
@@ -31,7 +31,7 @@ import { produceNoteDaosForKey } from './produce_note_daos_for_key.js';
|
|
|
31
31
|
export async function produceNoteDaos(
|
|
32
32
|
simulator: AcirSimulator,
|
|
33
33
|
db: PxeDatabase,
|
|
34
|
-
|
|
34
|
+
addressPoint: PublicKey | undefined,
|
|
35
35
|
ovpkM: PublicKey | undefined,
|
|
36
36
|
payload: L1NotePayload,
|
|
37
37
|
txHash: TxHash,
|
|
@@ -46,10 +46,8 @@ export async function produceNoteDaos(
|
|
|
46
46
|
incomingDeferredNote: DeferredNoteDao | undefined;
|
|
47
47
|
outgoingDeferredNote: DeferredNoteDao | undefined;
|
|
48
48
|
}> {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!ivpkM && !ovpkM) {
|
|
52
|
-
throw new Error('Both ivpkM and ovpkM are undefined. Cannot create note.');
|
|
49
|
+
if (!addressPoint && !ovpkM) {
|
|
50
|
+
throw new Error('Both addressPoint and ovpkM are undefined. Cannot create note.');
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
let incomingNote: IncomingNoteDao | undefined;
|
|
@@ -57,11 +55,11 @@ export async function produceNoteDaos(
|
|
|
57
55
|
let incomingDeferredNote: DeferredNoteDao | undefined;
|
|
58
56
|
let outgoingDeferredNote: DeferredNoteDao | undefined;
|
|
59
57
|
|
|
60
|
-
if (
|
|
58
|
+
if (addressPoint) {
|
|
61
59
|
[incomingNote, incomingDeferredNote] = await produceNoteDaosForKey(
|
|
62
60
|
simulator,
|
|
63
61
|
db,
|
|
64
|
-
|
|
62
|
+
addressPoint,
|
|
65
63
|
payload,
|
|
66
64
|
txHash,
|
|
67
65
|
noteHashes,
|
|
@@ -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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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 {
|
|
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: (
|
|
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
|
-
}
|