Skip to content

Commit 7b7de8e

Browse files
committed
refactor: tagging related cleanup
1 parent 40f4f7a commit 7b7de8e

File tree

8 files changed

+1136
-1158
lines changed

8 files changed

+1136
-1158
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { Fr } from '@aztec/foundation/fields';
2+
3+
export type IndexedTaggingSecret = {
4+
directionalAppTaggingSecret: Fr;
5+
index: number;
6+
};

yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.test.ts

Lines changed: 978 additions & 978 deletions
Large diffs are not rendered by default.

yarn-project/pxe/src/contract_function_simulator/pxe_oracle_interface.ts

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import type { CompleteAddress, ContractInstance } from '@aztec/stdlib/contract';
1515
import { computeUniqueNoteHash, siloNoteHash, siloNullifier, siloPrivateLog } from '@aztec/stdlib/hash';
1616
import { type AztecNode, MAX_RPC_LEN } from '@aztec/stdlib/interfaces/client';
1717
import type { KeyValidationRequest } from '@aztec/stdlib/kernel';
18-
import { computeAddressSecret, computeAppTaggingSecret } from '@aztec/stdlib/keys';
18+
import { computeAddressSecret } from '@aztec/stdlib/keys';
1919
import {
20-
IndexedTaggingSecret,
2120
PendingTaggedLog,
2221
PrivateLogWithTxData,
2322
PublicLog,
@@ -47,7 +46,14 @@ import { LogRetrievalRequest } from './noir-structs/log_retrieval_request.js';
4746
import { LogRetrievalResponse } from './noir-structs/log_retrieval_response.js';
4847
import { NoteValidationRequest } from './noir-structs/note_validation_request.js';
4948
import type { ProxiedNode } from './proxied_node.js';
50-
import { WINDOW_HALF_SIZE, getIndexedTaggingSecretsForTheWindow, getInitialIndexesMap } from './tagging_utils.js';
49+
import {
50+
WINDOW_HALF_SIZE,
51+
computeDirectionalAppTaggingSecret,
52+
computeSiloedTag,
53+
computeTag,
54+
getIndexedTaggingSecretsForTheWindow,
55+
getInitialIndexesMap,
56+
} from './tagging_utils.js';
5157

5258
/**
5359
* A data layer that provides and stores information needed for simulating/proving a transaction.
@@ -276,33 +282,37 @@ export class PXEOracleInterface implements ExecutionDataProvider {
276282
): Promise<Fr> {
277283
await this.syncTaggedLogsAsSender(contractAddress, sender, recipient);
278284

279-
const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
280-
const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret], sender);
285+
const directionalAppTaggingSecret = await this.#calculateDirectionalAppTaggingSecret(
286+
contractAddress,
287+
sender,
288+
recipient,
289+
);
290+
const [index] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([directionalAppTaggingSecret]);
281291

282292
// Increment the index for next time
283293
const contractName = await this.contractDataProvider.getDebugContractName(contractAddress);
284294
this.log.debug(`Incrementing app tagging secret at ${contractName}(${contractAddress})`, {
285-
appTaggingSecret,
295+
directionalAppTaggingSecret,
286296
sender,
287297
recipient,
288298
contractName,
289299
contractAddress,
290300
});
291301

292-
await this.taggingDataProvider.setTaggingSecretsIndexesAsSender(
293-
[new IndexedTaggingSecret(appTaggingSecret, index + 1)],
294-
sender,
295-
);
302+
await this.taggingDataProvider.setTaggingSecretIndexAsSender(directionalAppTaggingSecret, index + 1);
296303

297304
// Compute and return the tag using the current index
298-
const indexedTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, index);
299-
return indexedTaggingSecret.computeTag(recipient);
305+
return computeTag(directionalAppTaggingSecret, index);
300306
}
301307

302-
async #calculateAppTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress) {
308+
async #calculateDirectionalAppTaggingSecret(
309+
contractAddress: AztecAddress,
310+
sender: AztecAddress,
311+
recipient: AztecAddress,
312+
) {
303313
const senderCompleteAddress = await this.getCompleteAddress(sender);
304314
const senderIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(sender);
305-
return computeAppTaggingSecret(senderCompleteAddress, senderIvsk, recipient, contractAddress);
315+
return computeDirectionalAppTaggingSecret(senderCompleteAddress, senderIvsk, recipient, contractAddress, recipient);
306316
}
307317

308318
/**
@@ -317,7 +327,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
317327
async #getIndexedTaggingSecretsForSenders(
318328
contractAddress: AztecAddress,
319329
recipient: AztecAddress,
320-
): Promise<IndexedTaggingSecret[]> {
330+
): Promise<{ directionalAppTaggingSecret: Fr; index: number }[]> {
321331
const recipientCompleteAddress = await this.getCompleteAddress(recipient);
322332
const recipientIvsk = await this.keyStore.getMasterIncomingViewingSecretKey(recipient);
323333

@@ -327,13 +337,26 @@ export class PXEOracleInterface implements ExecutionDataProvider {
327337
...(await this.taggingDataProvider.getSenderAddresses()),
328338
...(await this.keyStore.getAccounts()),
329339
].filter((address, index, self) => index === self.findIndex(otherAddress => otherAddress.equals(address)));
330-
const appTaggingSecrets = await Promise.all(
331-
senders.map(contact =>
332-
computeAppTaggingSecret(recipientCompleteAddress, recipientIvsk, contact, contractAddress),
333-
),
340+
const directionalAppTaggingSecrets = await Promise.all(
341+
senders.map(contact => {
342+
return computeDirectionalAppTaggingSecret(
343+
recipientCompleteAddress,
344+
recipientIvsk,
345+
contact,
346+
contractAddress,
347+
recipient,
348+
);
349+
}),
334350
);
335-
const indexes = await this.taggingDataProvider.getTaggingSecretsIndexesAsRecipient(appTaggingSecrets, recipient);
336-
return appTaggingSecrets.map((secret, i) => new IndexedTaggingSecret(secret, indexes[i]));
351+
const indexes = await this.taggingDataProvider.getTaggingSecretsIndexesAsRecipient(directionalAppTaggingSecrets);
352+
if (indexes.length !== directionalAppTaggingSecrets.length) {
353+
throw new Error('Indexes and directional app tagging secrets have different lengths');
354+
}
355+
356+
return directionalAppTaggingSecrets.map((secret, i) => ({
357+
directionalAppTaggingSecret: secret,
358+
index: indexes[i],
359+
}));
337360
}
338361

339362
/**
@@ -349,8 +372,13 @@ export class PXEOracleInterface implements ExecutionDataProvider {
349372
sender: AztecAddress,
350373
recipient: AztecAddress,
351374
): Promise<void> {
352-
const appTaggingSecret = await this.#calculateAppTaggingSecret(contractAddress, sender, recipient);
353-
const [oldIndex] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([appTaggingSecret], sender);
375+
const directionalAppTaggingSecret = await this.#calculateDirectionalAppTaggingSecret(
376+
contractAddress,
377+
sender,
378+
recipient,
379+
);
380+
381+
const [oldIndex] = await this.taggingDataProvider.getTaggingSecretsIndexesAsSender([directionalAppTaggingSecret]);
354382

355383
// This algorithm works such that:
356384
// 1. If we find minimum consecutive empty logs in a window of logs we set the index to the index of the last log
@@ -364,8 +392,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
364392
do {
365393
// We compute the tags for the current window of indexes
366394
const currentTags = await timesParallel(WINDOW_SIZE, i => {
367-
const indexedAppTaggingSecret = new IndexedTaggingSecret(appTaggingSecret, currentIndex + i);
368-
return indexedAppTaggingSecret.computeSiloedTag(recipient, contractAddress);
395+
return computeSiloedTag(directionalAppTaggingSecret, currentIndex + i, contractAddress);
369396
});
370397

371398
// We fetch the logs for the tags
@@ -388,14 +415,11 @@ export class PXEOracleInterface implements ExecutionDataProvider {
388415

389416
const contractName = await this.contractDataProvider.getDebugContractName(contractAddress);
390417
if (currentIndex !== oldIndex) {
391-
await this.taggingDataProvider.setTaggingSecretsIndexesAsSender(
392-
[new IndexedTaggingSecret(appTaggingSecret, currentIndex)],
393-
sender,
394-
);
418+
await this.taggingDataProvider.setTaggingSecretIndexAsSender(directionalAppTaggingSecret, currentIndex);
395419

396420
this.log.debug(`Syncing logs for sender ${sender} at contract ${contractName}(${contractAddress})`, {
397421
sender,
398-
secret: appTaggingSecret,
422+
secret: directionalAppTaggingSecret,
399423
index: currentIndex,
400424
contractName,
401425
contractAddress,
@@ -445,7 +469,7 @@ export class PXEOracleInterface implements ExecutionDataProvider {
445469
// some logs. For these reasons, we have to look both back and ahead of the stored index.
446470
let secretsAndWindows = secrets.map(secret => {
447471
return {
448-
appTaggingSecret: secret.appTaggingSecret,
472+
directionalAppTaggingSecret: secret.directionalAppTaggingSecret,
449473
leftMostIndex: Math.max(0, secret.index - WINDOW_HALF_SIZE),
450474
rightMostIndex: secret.index + WINDOW_HALF_SIZE,
451475
};
@@ -460,7 +484,9 @@ export class PXEOracleInterface implements ExecutionDataProvider {
460484
while (secretsAndWindows.length > 0) {
461485
const secretsForTheWholeWindow = getIndexedTaggingSecretsForTheWindow(secretsAndWindows);
462486
const tagsForTheWholeWindow = await Promise.all(
463-
secretsForTheWholeWindow.map(secret => secret.computeSiloedTag(recipient, contractAddress)),
487+
secretsForTheWholeWindow.map(secret =>
488+
computeSiloedTag(secret.directionalAppTaggingSecret, secret.index, contractAddress),
489+
),
464490
);
465491

466492
// We store the new largest indexes we find in the iteration in the following map to later on construct
@@ -492,17 +518,18 @@ export class PXEOracleInterface implements ExecutionDataProvider {
492518
// We retrieve the indexed tagging secret corresponding to the log as I need that to evaluate whether
493519
// a new largest index have been found.
494520
const secretCorrespondingToLog = secretsForTheWholeWindow[logIndex];
495-
const initialIndex = initialIndexesMap[secretCorrespondingToLog.appTaggingSecret.toString()];
521+
const initialIndex = initialIndexesMap[secretCorrespondingToLog.directionalAppTaggingSecret.toString()];
496522

497523
if (
498524
secretCorrespondingToLog.index >= initialIndex &&
499-
(newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] === undefined ||
525+
(newLargestIndexMapForIteration[secretCorrespondingToLog.directionalAppTaggingSecret.toString()] ===
526+
undefined ||
500527
secretCorrespondingToLog.index >=
501-
newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()])
528+
newLargestIndexMapForIteration[secretCorrespondingToLog.directionalAppTaggingSecret.toString()])
502529
) {
503530
// We have found a new largest index so we store it for later processing (storing it in the db + fetching
504531
// the difference of the window sets of current and the next iteration)
505-
newLargestIndexMapForIteration[secretCorrespondingToLog.appTaggingSecret.toString()] =
532+
newLargestIndexMapForIteration[secretCorrespondingToLog.directionalAppTaggingSecret.toString()] =
506533
secretCorrespondingToLog.index + 1;
507534

508535
this.log.debug(
@@ -518,21 +545,23 @@ export class PXEOracleInterface implements ExecutionDataProvider {
518545
// for. Note that it's very unlikely that a new log from the current window would appear between the iterations
519546
// so we fetch the logs only for the difference of the window sets.
520547
const newSecretsAndWindows = [];
521-
for (const [appTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) {
522-
const secret = secrets.find(secret => secret.appTaggingSecret.toString() === appTaggingSecret);
548+
for (const [directionalAppTaggingSecret, newIndex] of Object.entries(newLargestIndexMapForIteration)) {
549+
const secret = secrets.find(
550+
secret => secret.directionalAppTaggingSecret.toString() === directionalAppTaggingSecret,
551+
);
523552
if (secret) {
524553
newSecretsAndWindows.push({
525-
appTaggingSecret: secret.appTaggingSecret,
554+
directionalAppTaggingSecret: secret.directionalAppTaggingSecret,
526555
// We set the left most index to the new index to avoid fetching the same logs again
527556
leftMostIndex: newIndex,
528557
rightMostIndex: newIndex + WINDOW_HALF_SIZE,
529558
});
530559

531560
// We store the new largest index in the map to later store it in the db.
532-
newLargestIndexMapToStore[appTaggingSecret] = newIndex;
561+
newLargestIndexMapToStore[directionalAppTaggingSecret] = newIndex;
533562
} else {
534563
throw new Error(
535-
`Secret not found for appTaggingSecret ${appTaggingSecret}. This is a bug as it should never happen!`,
564+
`Secret not found for directionalAppTaggingSecret ${directionalAppTaggingSecret}. This is a bug as it should never happen!`,
536565
);
537566
}
538567
}
@@ -543,10 +572,10 @@ export class PXEOracleInterface implements ExecutionDataProvider {
543572

544573
// At this point we have processed all the logs for the recipient so we store the new largest indexes in the db.
545574
await this.taggingDataProvider.setTaggingSecretsIndexesAsRecipient(
546-
Object.entries(newLargestIndexMapToStore).map(
547-
([appTaggingSecret, index]) => new IndexedTaggingSecret(Fr.fromHexString(appTaggingSecret), index),
548-
),
549-
recipient,
575+
Object.entries(newLargestIndexMapToStore).map(([directionalAppTaggingSecret, index]) => ({
576+
directionalAppTaggingSecret: Fr.fromHexString(directionalAppTaggingSecret),
577+
index,
578+
})),
550579
);
551580
}
552581
}
Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,71 @@
1-
import type { Fr } from '@aztec/foundation/fields';
2-
import { IndexedTaggingSecret } from '@aztec/stdlib/logs';
1+
import { Grumpkin, poseidon2Hash } from '@aztec/foundation/crypto';
2+
import type { Fq, Fr } from '@aztec/foundation/fields';
3+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4+
import type { CompleteAddress } from '@aztec/stdlib/contract';
5+
import { computeAddressSecret, computePreaddress } from '@aztec/stdlib/keys';
36

47
// Half the size of the window we slide over the tagging secret indexes.
58
export const WINDOW_HALF_SIZE = 10;
69

710
export function getIndexedTaggingSecretsForTheWindow(
8-
secretsAndWindows: { appTaggingSecret: Fr; leftMostIndex: number; rightMostIndex: number }[],
9-
): IndexedTaggingSecret[] {
10-
const secrets: IndexedTaggingSecret[] = [];
11+
secretsAndWindows: { directionalAppTaggingSecret: Fr; leftMostIndex: number; rightMostIndex: number }[],
12+
): { directionalAppTaggingSecret: Fr; index: number }[] {
13+
const secrets: { directionalAppTaggingSecret: Fr; index: number }[] = [];
1114
for (const secretAndWindow of secretsAndWindows) {
1215
for (let i = secretAndWindow.leftMostIndex; i <= secretAndWindow.rightMostIndex; i++) {
13-
secrets.push(new IndexedTaggingSecret(secretAndWindow.appTaggingSecret, i));
16+
secrets.push({ directionalAppTaggingSecret: secretAndWindow.directionalAppTaggingSecret, index: i });
1417
}
1518
}
1619
return secrets;
1720
}
1821

1922
/**
20-
* Creates a map from app tagging secret to initial index.
23+
* Creates a map from directional app tagging secret to initial index.
2124
* @param indexedTaggingSecrets - The indexed tagging secrets to get the initial indexes from.
22-
* @returns The map from app tagging secret to initial index.
25+
* @returns The map from directional app tagging secret to initial index.
2326
*/
24-
export function getInitialIndexesMap(indexedTaggingSecrets: IndexedTaggingSecret[]): { [k: string]: number } {
27+
export function getInitialIndexesMap(indexedTaggingSecrets: { directionalAppTaggingSecret: Fr; index: number }[]): {
28+
[k: string]: number;
29+
} {
2530
const initialIndexes: { [k: string]: number } = {};
2631

2732
for (const indexedTaggingSecret of indexedTaggingSecrets) {
28-
initialIndexes[indexedTaggingSecret.appTaggingSecret.toString()] = indexedTaggingSecret.index;
33+
initialIndexes[indexedTaggingSecret.directionalAppTaggingSecret.toString()] = indexedTaggingSecret.index;
2934
}
3035

3136
return initialIndexes;
3237
}
38+
39+
// Returns shared tagging secret computed with Diffie-Hellman key exchange.
40+
async function computeTaggingSecretPoint(knownAddress: CompleteAddress, ivsk: Fq, externalAddress: AztecAddress) {
41+
const knownPreaddress = await computePreaddress(await knownAddress.publicKeys.hash(), knownAddress.partialAddress);
42+
// TODO: #8970 - Computation of address point from x coordinate might fail
43+
const externalAddressPoint = await externalAddress.toAddressPoint();
44+
const curve = new Grumpkin();
45+
// Given A (known complete address) -> B (external address) and h == preaddress
46+
// Compute shared secret as S = (h_A + ivsk_A) * Addr_Point_B
47+
48+
// Beware! h_a + ivsk_a (also known as the address secret) can lead to an address point with a negative y-coordinate, since there's two possible candidates
49+
// computeAddressSecret takes care of selecting the one that leads to a positive y-coordinate, which is the only valid address point
50+
return curve.mul(externalAddressPoint, await computeAddressSecret(knownPreaddress, ivsk));
51+
}
52+
53+
export async function computeDirectionalAppTaggingSecret(
54+
knownAddress: CompleteAddress,
55+
ivsk: Fq,
56+
externalAddress: AztecAddress,
57+
app: AztecAddress,
58+
recipient: AztecAddress,
59+
) {
60+
const taggingSecretPoint = await computeTaggingSecretPoint(knownAddress, ivsk, externalAddress);
61+
const appTaggingSecret = await poseidon2Hash([taggingSecretPoint.x, taggingSecretPoint.y, app]);
62+
return poseidon2Hash([appTaggingSecret, recipient]);
63+
}
64+
65+
export function computeTag(directionalAppTaggingSecret: Fr, index: number) {
66+
return poseidon2Hash([directionalAppTaggingSecret, index]);
67+
}
68+
69+
export async function computeSiloedTag(directionalAppTaggingSecret: Fr, index: number, app: AztecAddress) {
70+
return poseidon2Hash([app, await computeTag(directionalAppTaggingSecret, index)]);
71+
}

0 commit comments

Comments
 (0)