diff --git a/yarn-project/circuit-types/src/keys/new_key_store.ts b/yarn-project/circuit-types/src/keys/new_key_store.ts index 6ae9337cc18..10eddd49561 100644 --- a/yarn-project/circuit-types/src/keys/new_key_store.ts +++ b/yarn-project/circuit-types/src/keys/new_key_store.ts @@ -49,4 +49,31 @@ export interface NewKeyStore { * @returns A Promise that resolves to the master tagging key. */ getMasterTaggingPublicKey(account: AztecAddress): Promise; + + /** + * Retrieves application nullifier secret key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the application nullifier secret key for. + * @param app - The application address to retrieve the nullifier secret key for. + * @returns A Promise that resolves to the application nullifier secret key. + */ + getAppNullifierSecretKey(account: AztecAddress, app: AztecAddress): Promise; + + /** + * Retrieves application 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. + */ + getAppIncomingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise; + + /** + * Retrieves application outgoing viewing secret key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the application outgoing viewing secret key for. + * @param app - The application address to retrieve the outgoing viewing secret key for. + * @returns A Promise that resolves to the application outgoing viewing secret key. + */ + getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise; } diff --git a/yarn-project/key-store/src/new_test_key_store.test.ts b/yarn-project/key-store/src/new_test_key_store.test.ts index 45a1ca06f1d..7e56a873ac8 100644 --- a/yarn-project/key-store/src/new_test_key_store.test.ts +++ b/yarn-project/key-store/src/new_test_key_store.test.ts @@ -1,4 +1,4 @@ -import { Fr } from '@aztec/circuits.js'; +import { AztecAddress, Fr } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { openTmpStore } from '@aztec/kv-store/utils'; @@ -37,5 +37,23 @@ describe('NewTestKeyStore', () => { expect(masterTaggingPublicKey.toString()).toMatchInlineSnapshot( `"0x076429010fdebfa522b053267f654a4c5daf18589915d96f7e5001d63ea2033f27f915f254560c84450aa38e93c3162be52492d05b316e75f542e3b302117360"`, ); + + // Arbitrary app contract address + const appAddress = AztecAddress.fromBigInt(624n); + + const appNullifierSecretKey = await keyStore.getAppNullifierSecretKey(accountAddress, appAddress); + expect(appNullifierSecretKey.toString()).toMatchInlineSnapshot( + `"0x230a44dfe7cfec7a735c89f7289c5cb5d2c3dc0bf5d3505917fd2476f67873a8"`, + ); + + const appIncomingViewingSecretKey = await keyStore.getAppIncomingViewingSecretKey(accountAddress, appAddress); + expect(appIncomingViewingSecretKey.toString()).toMatchInlineSnapshot( + `"0x0084c92262407236c992dcea10cf3406a642074cad6c6034d2990ffb073207a7"`, + ); + + const appOutgoingViewingSecretKey = await keyStore.getAppOutgoingViewingSecretKey(accountAddress, appAddress); + expect(appOutgoingViewingSecretKey.toString()).toMatchInlineSnapshot( + `"0x2639b26510f9d30b7e173d301b263b246b7a576186be1f44cd7c86bc06773f8a"`, + ); }); }); diff --git a/yarn-project/key-store/src/new_test_key_store.ts b/yarn-project/key-store/src/new_test_key_store.ts index 7ba118d58d7..c5ff22030e7 100644 --- a/yarn-project/key-store/src/new_test_key_store.ts +++ b/yarn-project/key-store/src/new_test_key_store.ts @@ -1,5 +1,5 @@ import { type NewKeyStore, type PublicKey } from '@aztec/circuit-types'; -import { AztecAddress, Fr, GeneratorIndex, type PartialAddress, Point } from '@aztec/circuits.js'; +import { AztecAddress, Fr, GeneratorIndex, GrumpkinScalar, type PartialAddress, Point } from '@aztec/circuits.js'; import { type Grumpkin } from '@aztec/circuits.js/barretenberg'; import { poseidon2Hash, sha512ToGrumpkinScalar } from '@aztec/foundation/crypto'; import { type AztecKVStore, type AztecMap } from '@aztec/kv-store'; @@ -60,7 +60,12 @@ export class NewTestKeyStore implements NewKeyStore { const accountAddressFr = poseidon2Hash([partialAddress, publicKeysHash, GeneratorIndex.CONTRACT_ADDRESS_V1]); const accountAddress = AztecAddress.fromField(accountAddressFr); - // We store the keys in the database + // We store all the public and secret keys in the database + await this.#keys.set(`${accountAddress.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer()); + await this.#keys.set(`${accountAddress.toString()}-npk_m`, masterNullifierPublicKey.toBuffer()); await this.#keys.set(`${accountAddress.toString()}-ivpk_m`, masterIncomingViewingPublicKey.toBuffer()); await this.#keys.set(`${accountAddress.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer()); @@ -125,4 +130,71 @@ export class NewTestKeyStore implements NewKeyStore { } return Promise.resolve(Point.fromBuffer(masterTaggingPublicKeyBuffer)); } + + /** + * Retrieves application nullifier secret key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the application nullifier secret key for. + * @param app - The application address to retrieve the nullifier secret key for. + * @returns A Promise that resolves to the application nullifier secret key. + */ + public getAppNullifierSecretKey(account: AztecAddress, app: AztecAddress): Promise { + const masterNullifierSecretKeyBuffer = this.#keys.get(`${account.toString()}-nsk_m`); + if (!masterNullifierSecretKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + const masterNullifierSecretKey = GrumpkinScalar.fromBuffer(masterNullifierSecretKeyBuffer); + + return Promise.resolve( + poseidon2Hash([masterNullifierSecretKey.high, masterNullifierSecretKey.low, app, GeneratorIndex.NSK_M]), + ); + } + + /** + * Retrieves application 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. + */ + public getAppIncomingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise { + const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ivsk_m`); + if (!masterIncomingViewingSecretKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + const masterIncomingViewingSecretKey = GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer); + + return Promise.resolve( + poseidon2Hash([ + masterIncomingViewingSecretKey.high, + masterIncomingViewingSecretKey.low, + app, + GeneratorIndex.IVSK_M, + ]), + ); + } + + /** + * Retrieves application outgoing viewing secret key. + * @throws If the account does not exist in the key store. + * @param account - The account to retrieve the application outgoing viewing secret key for. + * @param app - The application address to retrieve the outgoing viewing secret key for. + * @returns A Promise that resolves to the application outgoing viewing secret key. + */ + public getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise { + const masterOutgoingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ovsk_m`); + if (!masterOutgoingViewingSecretKeyBuffer) { + throw new Error(`Account ${account.toString()} does not exist.`); + } + const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer); + + return Promise.resolve( + poseidon2Hash([ + masterOutgoingViewingSecretKey.high, + masterOutgoingViewingSecretKey.low, + app, + GeneratorIndex.OVSK_M, + ]), + ); + } }