Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
benesjan committed Apr 16, 2024
1 parent 11a5003 commit 4591484
Show file tree
Hide file tree
Showing 18 changed files with 1,279 additions and 1,226 deletions.
12 changes: 6 additions & 6 deletions yarn-project/aztec.js/src/account_manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class AccountManager {

constructor(
private pxe: PXE,
private encryptionPrivateKey: GrumpkinPrivateKey,
private secretKey: GrumpkinPrivateKey,
private accountContract: AccountContract,
salt?: Salt,
) {
Expand All @@ -45,7 +45,7 @@ export class AccountManager {

protected getEncryptionPublicKey() {
if (!this.encryptionPublicKey) {
this.encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey);
this.encryptionPublicKey = generatePublicKey(this.secretKey);
}
return this.encryptionPublicKey;
}
Expand All @@ -67,7 +67,7 @@ export class AccountManager {
*/
public getCompleteAddress(): CompleteAddress {
if (!this.completeAddress) {
const encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey);
const encryptionPublicKey = generatePublicKey(this.secretKey);
const instance = this.getInstance();
this.completeAddress = CompleteAddress.fromPublicKeyAndInstance(encryptionPublicKey, instance);
}
Expand All @@ -81,7 +81,7 @@ export class AccountManager {
*/
public getInstance(): ContractInstanceWithAddress {
if (!this.instance) {
const encryptionPublicKey = generatePublicKey(this.encryptionPrivateKey);
const encryptionPublicKey = generatePublicKey(this.secretKey);
this.instance = getContractInstanceFromDeployParams(this.accountContract.getContractArtifact(), {
constructorArgs: this.accountContract.getDeploymentArgs(),
salt: this.salt,
Expand All @@ -98,7 +98,7 @@ export class AccountManager {
*/
public async getWallet(): Promise<AccountWalletWithPrivateKey> {
const entrypoint = await this.getAccount();
return new AccountWalletWithPrivateKey(this.pxe, entrypoint, this.encryptionPrivateKey, this.salt);
return new AccountWalletWithPrivateKey(this.pxe, entrypoint, this.secretKey, this.salt);
}

/**
Expand Down Expand Up @@ -199,6 +199,6 @@ export class AccountManager {

async #register(): Promise<void> {
const completeAddress = this.getCompleteAddress();
await this.pxe.registerAccount(this.encryptionPrivateKey, completeAddress.partialAddress);
await this.pxe.registerAccount(this.secretKey, completeAddress.partialAddress);
}
}
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export abstract class BaseWallet implements Wallet {
addCapsule(capsule: Fr[]): Promise<void> {
return this.pxe.addCapsule(capsule);
}
registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise<CompleteAddress> {
return this.pxe.registerAccount(privKey, partialAddress);
registerAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
return this.pxe.registerAccount(sk, partialAddress);
}
registerRecipient(account: CompleteAddress): Promise<void> {
return this.pxe.registerRecipient(account);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export interface PXE {
* @param partialAddress - The partial address of the account contract corresponding to the account being registered.
* @returns The complete address of the account.
*/
registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise<CompleteAddress>;
registerAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress>;

/**
* Registers a recipient in PXE. This is required when sending encrypted notes to
Expand Down
11 changes: 11 additions & 0 deletions yarn-project/circuit-types/src/keys/key_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,15 @@ export interface KeyStore {
* @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
*/
getMasterNullifierSecretKeyForPublicKey(masterNullifierPublicKey: PublicKey): Promise<GrumpkinPrivateKey>;

/**
* Retrieves the master incoming viewing secret key (ivsk_m) corresponding to the specified master incoming viewing
* public key (Ivpk_m).
* @throws If the provided public key is not associated with any of the registered accounts.
* @param masterIncomingViewingPublicKey - The master nullifier public key to get secret key for.
* @returns A Promise that resolves to the master nullifier secret key.
* @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
* TODO(benesjan): will need to be updated once we have app siloing of viewing keys
*/
getMasterIncomingViewingSecretKeyForPublicKey(masterIncomingViewingPublicKey: PublicKey): Promise<GrumpkinPrivateKey>;
}
1 change: 1 addition & 0 deletions yarn-project/key-store/src/key_pair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { type Grumpkin } from '@aztec/circuits.js/barretenberg';
* a constant public and private key pair. It provides methods for creating a random instance of the key pair,
* retrieving the public key, getting the private key. This class ensures the persistence and consistency of
* the generated keys, making it suitable for cryptographic operations where constant key pairs are required.
* TODO(benesjan): NUKE?
*/
export class ConstantKeyPair implements KeyPair {
/**
Expand Down
24 changes: 24 additions & 0 deletions yarn-project/key-store/src/test_key_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,28 @@ export class NewTestKeyStore implements NewKeyStore {

throw new Error(`Could not find master nullifier secret key for public key ${masterNullifierPublicKey.toString()}`);
}

/**
* Retrieves the master incoming viewing secret key (ivsk_m) corresponding to the specified master incoming viewing
* public key (Ivpk_m).
* @throws If the provided public key is not associated with any of the registered accounts.
* @param masterIncomingViewingPublicKey - The master nullifier public key to get secret key for.
* @returns A Promise that resolves to the master nullifier secret key.
* @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
* TODO(benesjan): will need to be updated once we have app siloing of viewing keys
*/
public getMasterIncomingViewingSecretKeyForPublicKey(
masterIncomingViewingPublicKey: PublicKey,
): Promise<GrumpkinPrivateKey> {
const allMapValues = Array.from(this.#keys.values());
const masterIncomingViewingSecretKeyBuffer = allMapValues.find(value =>
value.equals(masterIncomingViewingPublicKey.toBuffer()),
);
if (!masterIncomingViewingSecretKeyBuffer) {
throw new Error(
`Could not find master incoming viewing secret key for public key ${masterIncomingViewingPublicKey.toString()}`,
);
}
return Promise.resolve(GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer));
}
}
2 changes: 1 addition & 1 deletion yarn-project/pxe/src/note_processor/note_processor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ describe('Note Processor', () => {
aztecNode = mock<AztecNode>();
keyStore = mock<KeyStore>();
simulator = mock<AcirSimulator>();
keyStore.getAccountPrivateKey.mockResolvedValue(owner.getPrivateKey());
keyStore.getMasterIncomingViewingSecretKeyForPublicKey.mockResolvedValue(owner.getPrivateKey());
noteProcessor = new NoteProcessor(
owner.getPublicKey(),
keyStore,
Expand Down
18 changes: 9 additions & 9 deletions yarn-project/pxe/src/note_processor/note_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class NoteProcessor {
/**
* The public counterpart to the private key to be used in note decryption.
*/
public readonly publicKey: PublicKey,
public readonly masterIncomingViewingPublicKey: PublicKey,
private keyStore: KeyStore,
private db: PxeDatabase,
private node: AztecNode,
Expand Down Expand Up @@ -78,7 +78,7 @@ export class NoteProcessor {
}

private getSyncedToBlock(): number {
return this.db.getSynchedBlockNumberForPublicKey(this.publicKey) ?? this.startingBlock - 1;
return this.db.getSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey) ?? this.startingBlock - 1;
}

/**
Expand Down Expand Up @@ -116,7 +116,7 @@ export class NoteProcessor {
// We are using set for `userPertainingTxIndices` to avoid duplicates. This would happen in case there were
// multiple encrypted logs in a tx pertaining to a user.
const noteDaos: NoteDao[] = [];
const privateKey = await this.keyStore.getAccountPrivateKey(this.publicKey);
const secretKey = await this.keyStore.getMasterIncomingViewingSecretKeyForPublicKey(this.masterIncomingViewingPublicKey);

// Iterate over all the encrypted logs and try decrypting them. If successful, store the note.
for (let indexOfTxInABlock = 0; indexOfTxInABlock < txLogs.length; ++indexOfTxInABlock) {
Expand All @@ -130,15 +130,15 @@ export class NoteProcessor {
for (const functionLogs of txFunctionLogs) {
for (const log of functionLogs.logs) {
this.stats.seen++;
const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, privateKey, curve);
const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, secretKey, curve);
if (taggedNote?.notePayload) {
const { notePayload: payload } = taggedNote;
// We have successfully decrypted the data.
const txHash = block.body.txEffects[indexOfTxInABlock].txHash;
try {
const noteDao = await produceNoteDao(
this.simulator,
this.publicKey,
this.masterIncomingViewingPublicKey,
payload,
txHash,
newNoteHashes,
Expand All @@ -152,7 +152,7 @@ export class NoteProcessor {
this.stats.deferred++;
this.log.warn(e.message);
const deferredNoteDao = new DeferredNoteDao(
this.publicKey,
this.masterIncomingViewingPublicKey,
payload.note,
payload.contractAddress,
payload.storageSlot,
Expand Down Expand Up @@ -182,7 +182,7 @@ export class NoteProcessor {
await this.processDeferredNotes(deferredNoteDaos);

const syncedToBlock = l2Blocks[l2Blocks.length - 1].number;
await this.db.setSynchedBlockNumberForPublicKey(this.publicKey, syncedToBlock);
await this.db.setSynchedBlockNumberForPublicKey(this.masterIncomingViewingPublicKey, syncedToBlock);

this.log.debug(`Synched block ${syncedToBlock}`);
}
Expand Down Expand Up @@ -212,7 +212,7 @@ export class NoteProcessor {
const newNullifiers: Fr[] = blocksAndNotes.flatMap(b =>
b.block.body.txEffects.flatMap(txEffect => txEffect.nullifiers),
);
const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.publicKey);
const removedNotes = await this.db.removeNullifiedNotes(newNullifiers, this.masterIncomingViewingPublicKey);
removedNotes.forEach(noteDao => {
this.log.verbose(
`Removed note for contract ${noteDao.contractAddress} at slot ${
Expand Down Expand Up @@ -260,7 +260,7 @@ export class NoteProcessor {
try {
const noteDao = await produceNoteDao(
this.simulator,
this.publicKey,
this.masterIncomingViewingPublicKey,
payload,
txHash,
newNoteHashes,
Expand Down
70 changes: 37 additions & 33 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
@@ -1,59 +1,58 @@
import {
type AuthWitness,
type AztecNode,
EncryptedTxL2Logs,
ExtendedNote,
MerkleTreeId,
SimulatedTx,
SimulationError,
Tx,
UnencryptedTxL2Logs,
isNoirCallStackUnresolved,
type AuthWitness,
type AztecNode,
type FunctionCall,
type GetUnencryptedLogsResponse,
type KeyStore,
type L2Block,
type LogFilter,
MerkleTreeId,
type NoteFilter,
type PXE,
SimulatedTx,
SimulationError,
Tx,
type TxEffect,
type TxExecutionRequest,
type TxHash,
type TxReceipt,
UnencryptedTxL2Logs,
isNoirCallStackUnresolved,
} from '@aztec/circuit-types';
import { type TxPXEProcessingStats } from '@aztec/circuit-types/stats';
import {
AztecAddress,
CallRequest,
CompleteAddress,
FunctionData,
type GrumpkinPrivateKey,
MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX,
type PartialAddress,
type PrivateKernelTailCircuitPublicInputs,
type PublicCallRequest,
computeContractClassId,
getContractClassFromArtifact,
type PartialAddress,
type PrivateKernelTailCircuitPublicInputs,
type PublicCallRequest
} from '@aztec/circuits.js';
import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/hash';
import { type ContractArtifact, type DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
import { FunctionSelector, encodeArguments, type ContractArtifact, type DecodedReturn } from '@aztec/foundation/abi';
import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/fields';
import { SerialQueue } from '@aztec/foundation/fifo';
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { createDebugLogger, type DebugLogger } from '@aztec/foundation/log';
import { Timer } from '@aztec/foundation/timer';
import {
type AcirSimulator,
type ExecutionResult,
collectEncryptedLogs,
collectEnqueuedPublicFunctionCalls,
collectUnencryptedLogs,
resolveOpcodeLocations,
type AcirSimulator,
type ExecutionResult,
} from '@aztec/simulator';
import { type ContractClassWithId, type ContractInstanceWithAddress } from '@aztec/types/contracts';
import { type NodeInfo } from '@aztec/types/interfaces';

import { type PXEServiceConfig, getPackageInfo } from '../config/index.js';
import { getPackageInfo, type PXEServiceConfig } from '../config/index.js';
import { ContractDataOracle } from '../contract_data_oracle/index.js';
import { type PxeDatabase } from '../database/index.js';
import { NoteDao } from '../database/note_dao.js';
Expand Down Expand Up @@ -105,7 +104,8 @@ export class PXEService implements PXE {
}

private async restoreNoteProcessors() {
const publicKeys = await this.keyStore.getAccounts();
const accounts = await this.keyStore.getAccounts();
const publicKeys = accounts.map(async account => await this.keyStore.getMasterIncomingViewingPublicKey(account));
const publicKeysSet = new Set(publicKeys.map(k => k.toString()));

const registeredAddresses = await this.db.getCompleteAddresses();
Expand Down Expand Up @@ -165,27 +165,31 @@ export class PXEService implements PXE {
return artifact && getContractClassFromArtifact(artifact);
}

public async registerAccount(privKey: GrumpkinPrivateKey, partialAddress: PartialAddress): Promise<CompleteAddress> {
const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(privKey, partialAddress);
const wasAdded = await this.db.addCompleteAddress(completeAddress);
if (wasAdded) {
const pubKey = await this.keyStore.addAccount(privKey);
this.synchronizer.addAccount(pubKey, this.keyStore, this.config.l2StartingBlock);
public async registerAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
const accounts = await this.keyStore.getAccounts();
const account = await this.keyStore.addAccount(sk, partialAddress);
const completeAddress = new CompleteAddress(account, await this.keyStore.getMasterIncomingViewingPublicKey(account), partialAddress);
if (accounts.includes(account)) {
this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
return completeAddress;
} else {
// TODO(benesjan): we will have to handle app siloing here
const masterIncomingViewingPublicKey = await this.keyStore.getMasterIncomingViewingPublicKey(account);
this.synchronizer.addAccount(masterIncomingViewingPublicKey, this.keyStore, this.config.l2StartingBlock);
this.log.info(`Registered account ${completeAddress.address.toString()}`);
this.log.debug(`Registered account\n ${completeAddress.toReadableString()}`);
} else {
this.log.info(`Account:\n "${completeAddress.address.toString()}"\n already registered.`);
}

await this.db.addCompleteAddress(completeAddress);
return completeAddress;
}

public async getRegisteredAccounts(): Promise<CompleteAddress[]> {
// Get complete addresses of both the recipients and the accounts
const addresses = await this.db.getCompleteAddresses();
const completeAddresses = await this.db.getCompleteAddresses();
// Filter out the addresses not corresponding to accounts
const accountPubKeys = await this.keyStore.getAccounts();
const accounts = addresses.filter(address => accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
return accounts;
const accounts = await this.keyStore.getAccounts();
return completeAddresses.filter(completeAddress => accounts.find(address => address.equals(completeAddress.address)));
}

public async getRegisteredAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
Expand All @@ -205,10 +209,10 @@ export class PXEService implements PXE {

public async getRecipients(): Promise<CompleteAddress[]> {
// Get complete addresses of both the recipients and the accounts
const addresses = await this.db.getCompleteAddresses();
const completeAddresses = await this.db.getCompleteAddresses();
// Filter out the addresses corresponding to accounts
const accountPubKeys = await this.keyStore.getAccounts();
const recipients = addresses.filter(address => !accountPubKeys.find(pubKey => pubKey.equals(address.publicKey)));
const accounts = await this.keyStore.getAccounts();
const recipients = completeAddresses.filter(completeAddress => !accounts.find(account => account.equals(completeAddress.address)));
return recipients;
}

Expand Down
15 changes: 7 additions & 8 deletions yarn-project/pxe/src/pxe_service/test/pxe_test_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
}, 120_000);

it('registers an account and returns it as an account only and not as a recipient', async () => {
const keyPair = ConstantKeyPair.random(new Grumpkin());
const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random());

await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
const randomSecretKey = Fr.random();
const randomPartialAddress = Fr.random();
const completeAddress = await pxe.registerAccount(randomSecretKey, randomPartialAddress);

// Check that the account is correctly registered using the getAccounts and getRecipients methods
const accounts = await pxe.getRegisteredAccounts();
Expand Down Expand Up @@ -64,11 +63,11 @@ export const pxeTestSuite = (testName: string, pxeSetup: () => Promise<PXE>) =>
});

it('does not throw when registering the same account twice (just ignores the second attempt)', async () => {
const keyPair = ConstantKeyPair.random(new Grumpkin());
const completeAddress = CompleteAddress.fromPrivateKeyAndPartialAddress(keyPair.getPrivateKey(), Fr.random());
const randomSecretKey = Fr.random();
const randomPartialAddress = Fr.random();

await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
await pxe.registerAccount(keyPair.getPrivateKey(), completeAddress.partialAddress);
await pxe.registerAccount(randomSecretKey, randomPartialAddress);
await pxe.registerAccount(randomSecretKey, randomPartialAddress);
});

it('cannot register a recipient with the same aztec address but different pub key or partial address', async () => {
Expand Down
Loading

0 comments on commit 4591484

Please sign in to comment.