Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions noir-projects/aztec-nr/aztec/src/oracle/notes.nr
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,15 @@ pub unconstrained fn check_nullifier_exists(inner_nullifier: Field) -> bool {

#[oracle(checkNullifierExists)]
unconstrained fn check_nullifier_exists_oracle(_inner_nullifier: Field) -> Field {}

/// Returns the tagging secret for a given sender and recipient pair. For this to work, PXE must know the ivpsk_m of the sender.
/// For the recipient's side, only the address is needed.
pub unconstrained fn get_tagging_secret(sender: AztecAddress, recipient: AztecAddress) -> Field {
Comment thread
Thunkar marked this conversation as resolved.
Outdated
get_tagging_secret_oracle(sender, recipient)
}
Comment thread
Thunkar marked this conversation as resolved.
Outdated

#[oracle(getTaggingSecret)]
unconstrained fn get_tagging_secret_oracle(
_sender: AztecAddress,
_recipient: AztecAddress,
) -> Field {}
16 changes: 5 additions & 11 deletions yarn-project/key-store/src/key_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,12 @@ export class KeyStore {
}

/**
* Retrieves application incoming viewing secret key.
* Retrieves master incoming viewing secret key.
* @throws If the account does not exist in the key store.
* @param account - The account to retrieve the application incoming viewing secret key for.
* @param app - The application address to retrieve the incoming viewing secret key for.
* @returns A Promise that resolves to the application incoming viewing secret key.
* @param account - The account to retrieve the master incoming viewing secret key for.
* @returns A Promise that resolves to the master incoming viewing secret key.
*/
public async getAppIncomingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
public async getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar> {
const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ivsk_m`);
if (!masterIncomingViewingSecretKeyBuffer) {
throw new Error(
Expand All @@ -220,12 +219,7 @@ export class KeyStore {
}
const masterIncomingViewingSecretKey = GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);

return Promise.resolve(
poseidon2HashWithSeparator(
[masterIncomingViewingSecretKey.hi, masterIncomingViewingSecretKey.lo, app],
GeneratorIndex.IVSK_M,
),
);
return Promise.resolve(masterIncomingViewingSecretKey);
}

/**
Expand Down
33 changes: 33 additions & 0 deletions yarn-project/pxe/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ import {
type AztecAddress,
type CompleteAddress,
type ContractInstance,
Fq,
type Fr,
type FunctionSelector,
type Header,
type KeyValidationRequest,
type L1_TO_L2_MSG_TREE_HEIGHT,
computePoint,
computePreaddress,
} from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi';
import { poseidon2Hash } from '@aztec/foundation/crypto';
import { createDebugLogger } from '@aztec/foundation/log';
import { type KeyStore } from '@aztec/key-store';
import { type DBOracle, MessageLoadOracleInputs } from '@aztec/simulator';
Expand Down Expand Up @@ -226,4 +231,32 @@ export class SimulatorOracle implements DBOracle {
public getDebugFunctionName(contractAddress: AztecAddress, selector: FunctionSelector): Promise<string> {
return this.contractDataOracle.getDebugFunctionName(contractAddress, selector);
}

/**
* Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
* @param contractAddress - The contract address to silo the secret for
* @param sender - The address sending the note
* @param recipient - The address receiving the note
* @returns A tagging secret that can be used to tag notes.
*/
public async getTaggingSecret(
contractAddress: AztecAddress,
sender: AztecAddress,
recipient: AztecAddress,
): Promise<Fr> {
const senderCompleteAddress = await this.getCompleteAddress(sender);
const senderPreaddress = computePreaddress(
senderCompleteAddress.publicKeys.hash(),
senderCompleteAddress.partialAddress,
);
const ivskSender = await this.keyStore.getMasterIncomingViewingSecretKey(senderPreaddress);
// TODO: #8970 - Computation of address point from x coordinate might fail
const recipientAddressPoint = computePoint(recipient);
const curve = new Grumpkin();
// Given A (sender) -> B (recipient) and h == preaddress
// Compute shared secret as S = (h_A + ivsk_A) * Addr_Point_B
const sharedSecret = curve.mul(recipientAddressPoint, ivskSender.add(new Fq(senderPreaddress.toBigInt())));
// Silo the secret to the app so it can't be used to track other app's notes
return poseidon2Hash([sharedSecret.x, sharedSecret.y, contractAddress]);
Comment thread
nventuro marked this conversation as resolved.
Outdated
}
}
8 changes: 8 additions & 0 deletions yarn-project/simulator/src/acvm/oracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,4 +408,12 @@ export class Oracle {
notifySetMinRevertibleSideEffectCounter([minRevertibleSideEffectCounter]: ACVMField[]) {
this.typedOracle.notifySetMinRevertibleSideEffectCounter(frToNumber(fromACVMField(minRevertibleSideEffectCounter)));
}

async getTaggingSecret([sender]: ACVMField[], [recipient]: ACVMField[]): Promise<ACVMField> {
const taggingSecret = await this.typedOracle.getTaggingSecret(
AztecAddress.fromString(sender),
AztecAddress.fromString(recipient),
);
return toACVMField(taggingSecret);
}
}
4 changes: 4 additions & 0 deletions yarn-project/simulator/src/acvm/oracle/typed_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,8 @@ export abstract class TypedOracle {
debugLog(_message: string, _fields: Fr[]): void {
throw new OracleMethodNotAvailableError('debugLog');
}

getTaggingSecret(_sender: AztecAddress, _recipient: AztecAddress): Promise<Fr> {
throw new OracleMethodNotAvailableError('getTaggingSecret');
}
}
27 changes: 0 additions & 27 deletions yarn-project/simulator/src/client/client_execution_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,33 +602,6 @@ export class ClientExecutionContext extends ViewDataOracle {
);
}

/**
Comment thread
nventuro marked this conversation as resolved.
* Read the public storage data.
* @param contractAddress - The address to read storage from.
* @param startStorageSlot - The starting storage slot.
* @param blockNumber - The block number to read storage at.
* @param numberOfElements - Number of elements to read from the starting storage slot.
*/
public override async storageRead(
contractAddress: Fr,
startStorageSlot: Fr,
blockNumber: number,
numberOfElements: number,
): Promise<Fr[]> {
const values = [];
for (let i = 0n; i < numberOfElements; i++) {
const storageSlot = new Fr(startStorageSlot.value + i);

const value = await this.aztecNode.getPublicStorageAt(contractAddress, storageSlot, blockNumber);
this.log.debug(
`Oracle storage read: slot=${storageSlot.toString()} address-${contractAddress.toString()} value=${value}`,
);

values.push(value);
}
return values;
}

public override debugLog(message: string, fields: Fr[]) {
this.log.verbose(`debug_log ${applyStringFormatting(message, fields)}`);
}
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/simulator/src/client/db_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,13 @@ export interface DBOracle extends CommitmentsDB {
* @returns The block number.
*/
getBlockNumber(): Promise<number>;

/**
* Returns the tagging secret for a given sender and recipient pair. For this to work, the ivpsk_m of the sender must be known.
* @param contractAddress - The contract address to silo the secret for
* @param sender - The address sending the note
* @param recipient - The address receiving the note
* @returns A tagging secret that can be used to tag notes.
*/
getTaggingSecret(contractAddress: AztecAddress, sender: AztecAddress, recipient: AztecAddress): Promise<Fr>;
}
11 changes: 11 additions & 0 deletions yarn-project/simulator/src/client/view_data_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,15 @@ export class ViewDataOracle extends TypedOracle {
const formattedStr = applyStringFormatting(message, fields);
this.log.verbose(`debug_log ${formattedStr}`);
}

/**
* Returns the tagging secret for a given sender and recipient pair, siloed to the current contract address.
* For this to work, the ivpsk_m of the sender must be known.
* @param sender - The address sending the note
* @param recipient - The address receiving the note
* @returns A tagging secret that can be used to tag notes.
*/
public override async getTaggingSecret(sender: AztecAddress, recipient: AztecAddress): Promise<Fr> {
return await this.db.getTaggingSecret(this.contractAddress, sender, recipient);
}
}
24 changes: 22 additions & 2 deletions yarn-project/txe/src/oracle/txe_oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ import {
type PublicDataTreeLeafPreimage,
TxContext,
computeContractClassId,
computePoint,
computePreaddress,
deriveKeys,
getContractClassFromArtifact,
} from '@aztec/circuits.js';
import { Schnorr } from '@aztec/circuits.js/barretenberg';
import { Grumpkin, Schnorr } from '@aztec/circuits.js/barretenberg';
import { computePublicDataTreeLeafSlot, siloNoteHash, siloNullifier } from '@aztec/circuits.js/hash';
import {
type ContractArtifact,
Expand All @@ -43,7 +45,8 @@ import {
countArgumentsSize,
} from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { poseidon2Hash } from '@aztec/foundation/crypto';
import { Fq, Fr } from '@aztec/foundation/fields';
import { type Logger, applyStringFormatting } from '@aztec/foundation/log';
import { Timer } from '@aztec/foundation/timer';
import { type KeyStore } from '@aztec/key-store';
Expand Down Expand Up @@ -747,6 +750,23 @@ export class TXE implements TypedOracle {
return;
}

async getTaggingSecret(sender: AztecAddress, recipient: AztecAddress): Promise<Fr> {
const senderCompleteAddress = await this.getCompleteAddress(sender);
const senderPreaddress = computePreaddress(
senderCompleteAddress.publicKeys.hash(),
senderCompleteAddress.partialAddress,
);
const ivskSender = await this.keyStore.getMasterIncomingViewingSecretKey(senderPreaddress);
// TODO: #8970 - Computation of address point from x coordinate might fail
const recipientAddressPoint = computePoint(recipient);
const curve = new Grumpkin();
// Given A (sender) -> B (recipient) and h == preaddress
// Compute shared secret as S = (h_A + ivsk_A) * Addr_Point_B
const sharedSecret = curve.mul(recipientAddressPoint, ivskSender.add(new Fq(senderPreaddress.toBigInt())));
// Silo the secret to the app so it can't be used to track other app's notes
return poseidon2Hash([sharedSecret.x, sharedSecret.y, this.contractAddress]);
}
Comment thread
nventuro marked this conversation as resolved.
Outdated

// AVM oracles

async avmOpcodeCall(
Expand Down
8 changes: 8 additions & 0 deletions yarn-project/txe/src/txe_service/txe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,14 @@ export class TXEService {
return toForeignCallResult([]);
}

async getTaggingSecret(sender: ForeignCallSingle, recipient: ForeignCallSingle) {
const secret = await this.typedOracle.getTaggingSecret(
AztecAddress.fromField(fromSingle(sender)),
AztecAddress.fromField(fromSingle(recipient)),
);
return toForeignCallResult([toSingle(secret)]);
}

// AVM opcodes

avmOpcodeEmitUnencryptedLog(_message: ForeignCallArray) {
Expand Down