@aztec/key-store 0.0.1-commit.fffb133c → 0.0.1-dev

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.
@@ -30,6 +30,8 @@ export declare class KeyStore {
30
30
  * @returns A Promise that resolves to an array of account addresses.
31
31
  */
32
32
  getAccounts(): Promise<AztecAddress[]>;
33
+ /** Checks whether an account is registered in the key store. */
34
+ hasAccount(account: AztecAddress): Promise<boolean>;
33
35
  /**
34
36
  * Gets the key validation request for a given master public key hash and contract address.
35
37
  * @throws If the account corresponding to the master public key hash does not exist in the key store.
@@ -89,6 +91,13 @@ export declare class KeyStore {
89
91
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
90
92
  */
91
93
  getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar>;
94
+ /**
95
+ * Checks whether a given account has a key matching the provided master public key hash.
96
+ * @param account - The account address to check.
97
+ * @param pkMHash - The master public key hash to look for.
98
+ * @returns True if the account has a key with the given hash.
99
+ */
100
+ accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean>;
92
101
  /**
93
102
  * Gets the key prefix and account address for a given value.
94
103
  * @returns A tuple containing the key prefix and account address.
@@ -97,4 +106,4 @@ export declare class KeyStore {
97
106
  */
98
107
  getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
99
108
  }
100
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUlmLE1BQU0sb0JBQW9CLENBQUM7QUFFNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBRzFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUV0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FvQ3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVEOzs7Ozs7T0FNRztJQUNVLHVCQUF1QixDQUFDLE9BQU8sRUFBRSxFQUFFLEVBQUUsZUFBZSxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsb0JBQW9CLENBQUMsQ0FxQzlHO0lBRUQ7Ozs7O09BS0c7SUFDVSwyQkFBMkIsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FRbEY7SUFFRDs7Ozs7T0FLRztJQUNVLGlDQUFpQyxDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVF4RjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSx5QkFBeUIsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FRaEY7SUFFRDs7Ozs7T0FLRztJQUNVLGlDQUFpQyxDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQyxDQVE3RjtJQUVEOzs7Ozs7T0FNRztJQUNVLDhCQUE4QixDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLENBYWpHO0lBRUQ7Ozs7OztPQU1HO0lBQ1Usa0JBQWtCLENBQUMsR0FBRyxFQUFFLFNBQVMsR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBaUJ2RTtJQUVEOzs7OztPQUtHO0lBQ1Usc0JBQXNCLENBQUMsS0FBSyxFQUFFLFVBQVUsR0FBRyxPQUFPLENBQUMsQ0FBQyxTQUFTLEVBQUUsWUFBWSxDQUFDLENBQUMsQ0FjekY7Q0FDRiJ9
109
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUlmLE1BQU0sb0JBQW9CLENBQUM7QUFPNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0F3Q3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQXdDeEc7SUFFRDs7Ozs7T0FLRztJQUNVLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFsRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FReEY7SUFFRDs7Ozs7T0FLRztJQUNVLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFoRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBUTdGO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsOEJBQThCLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0Fhakc7SUFFRDs7Ozs7O09BTUc7SUFDSSxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsU0FBUyxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0F3QmpFO0lBRUQ7Ozs7O09BS0c7SUFDSSxhQUFhLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FXekU7SUFFRDs7Ozs7T0FLRztJQUNVLHNCQUFzQixDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBY3pGO0NBQ0YifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"key_store.d.ts","sourceRoot":"","sources":["../src/key_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,cAAc,EAAS,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,SAAS,EAIf,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;IAG1C,YAAY,QAAQ,EAAE,iBAAiB,EAEtC;IAED;;;OAGG;IACI,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAI/C;IAED;;;;;OAKG;IACU,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAoCxF;IAED;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKlD;IAED;;;;;;OAMG;IACU,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAqC9G;IAED;;;;;OAKG;IACU,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQlF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQhF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ7F;IAED;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAajG;IAED;;;;;;OAMG;IACU,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAiBvE;IAED;;;;;OAKG;IACU,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAczF;CACF"}
1
+ {"version":3,"file":"key_store.d.ts","sourceRoot":"","sources":["../src/key_store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,EAAE,EAAE,MAAM,gCAAgC,CAAC;AACpD,OAAO,EAAE,cAAc,EAAS,MAAM,mCAAmC,CAAC;AAE1E,OAAO,EAAE,KAAK,UAAU,EAAqB,MAAM,6BAA6B,CAAC;AACjF,OAAO,KAAK,EAAE,iBAAiB,EAAiB,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,SAAS,EAIf,MAAM,oBAAoB,CAAC;AAO5B;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;IAI1C,YAAY,QAAQ,EAAE,iBAAiB,EAGtC;IAED;;;OAGG;IACI,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAI/C;IAED;;;;;OAKG;IACU,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAwCxF;IAED;;;OAGG;IACU,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAKlD;IAED,gEAAgE;IACnD,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAE/D;IAED;;;;;;OAMG;IACI,uBAAuB,CAAC,OAAO,EAAE,EAAE,EAAE,eAAe,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAwCxG;IAED;;;;;OAKG;IACU,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQlF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQxF;IAED;;;;;OAKG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAQhF;IAED;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAQ7F;IAED;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAajG;IAED;;;;;;OAMG;IACI,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAwBjE;IAED;;;;;OAKG;IACI,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAWzE;IAED;;;;;OAKG;IACU,sBAAsB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAczF;CACF"}
package/dest/key_store.js CHANGED
@@ -1,4 +1,4 @@
1
- import { GeneratorIndex } from '@aztec/constants';
1
+ import { DomainSeparator } from '@aztec/constants';
2
2
  import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
@@ -8,12 +8,17 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
8
8
  import { CompleteAddress } from '@aztec/stdlib/contract';
9
9
  import { KeyValidationRequest } from '@aztec/stdlib/kernel';
10
10
  import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey } from '@aztec/stdlib/keys';
11
+ /** Maps a key prefix to the storage suffix for the corresponding master secret key. */ function secretKeyStorageSuffix(prefix) {
12
+ return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
13
+ }
11
14
  /**
12
15
  * Used for managing keys. Can hold keys of multiple accounts.
13
16
  */ export class KeyStore {
14
17
  static SCHEMA_VERSION = 1;
18
+ #db;
15
19
  #keys;
16
20
  constructor(database){
21
+ this.#db = database;
17
22
  this.#keys = database.openMap('key_store');
18
23
  }
19
24
  /**
@@ -30,28 +35,31 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
30
35
  * @param partialAddress - The partial address of the account.
31
36
  * @returns The account's complete address.
32
37
  */ async addAccount(sk, partialAddress) {
33
- const { masterNullifierSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys } = await deriveKeys(sk);
38
+ const { masterNullifierHidingKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys } = await deriveKeys(sk);
34
39
  const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
35
40
  const { address: account } = completeAddress;
36
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
37
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
38
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
39
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
40
- await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
41
- await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
42
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
43
- await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
44
- await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
45
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
46
- // using the #getKeyPrefixAndAccount function later on
41
+ // Compute hashes before transaction
47
42
  const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
48
- await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
49
43
  const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
50
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
51
44
  const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
52
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
53
45
  const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
54
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
46
+ await this.#db.transactionAsync(async ()=>{
47
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
48
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
49
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
50
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
51
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
52
+ await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
53
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
54
+ await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
55
+ await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
56
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
57
+ // using the #getKeyPrefixAndAccount function later on
58
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
59
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
60
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
61
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
62
+ });
55
63
  // At last, we return the newly derived account address
56
64
  return completeAddress;
57
65
  }
@@ -64,38 +72,43 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
64
72
  const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
65
73
  return accounts.map((account)=>AztecAddress.fromString(account));
66
74
  }
75
+ /** Checks whether an account is registered in the key store. */ async hasAccount(account) {
76
+ return !!await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
77
+ }
67
78
  /**
68
79
  * Gets the key validation request for a given master public key hash and contract address.
69
80
  * @throws If the account corresponding to the master public key hash does not exist in the key store.
70
81
  * @param pkMHash - The master public key hash.
71
82
  * @param contractAddress - The contract address to silo the secret key in the key validation request with.
72
83
  * @returns The key validation request.
73
- */ async getKeyValidationRequest(pkMHash, contractAddress) {
74
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
75
- // Now we find the master public key for the account
76
- const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
77
- if (!pkMBuffer) {
78
- throw new Error(`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
79
- }
80
- const pkM = Point.fromBuffer(pkMBuffer);
81
- const computedPkMHash = await pkM.hash();
82
- if (!computedPkMHash.equals(pkMHash)) {
83
- throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
84
- }
85
- // Now we find the secret key for the public key
86
- const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
87
- if (!skMBuffer) {
88
- throw new Error(`Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
89
- }
90
- const skM = GrumpkinScalar.fromBuffer(skMBuffer);
91
- // We sanity check that it's possible to derive the public key from the secret key
92
- const derivedPkM = await derivePublicKeyFromSecretKey(skM);
93
- if (!derivedPkM.equals(pkM)) {
94
- throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
95
- }
96
- // At last we silo the secret key and return the key validation request
97
- const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
98
- return new KeyValidationRequest(pkM, skApp);
84
+ */ getKeyValidationRequest(pkMHash, contractAddress) {
85
+ return this.#db.transactionAsync(async ()=>{
86
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
87
+ // Now we find the master public key for the account
88
+ const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
89
+ if (!pkMBuffer) {
90
+ throw new Error(`Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
91
+ }
92
+ const pkM = Point.fromBuffer(pkMBuffer);
93
+ // Now we find the secret key for the public key
94
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
95
+ const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
96
+ if (!skMBuffer) {
97
+ throw new Error(`Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
98
+ }
99
+ const skM = GrumpkinScalar.fromBuffer(skMBuffer);
100
+ // The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
101
+ const computedPkMHash = await pkM.hash();
102
+ if (!computedPkMHash.equals(pkMHash)) {
103
+ throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
104
+ }
105
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
106
+ if (!derivedPkM.equals(pkM)) {
107
+ throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
108
+ }
109
+ const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix);
110
+ return new KeyValidationRequest(pkM, skApp);
111
+ });
99
112
  }
100
113
  /**
101
114
  * Gets the master nullifier public key for a given account.
@@ -173,7 +186,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
173
186
  masterOutgoingViewingSecretKey.hi,
174
187
  masterOutgoingViewingSecretKey.lo,
175
188
  app
176
- ], GeneratorIndex.OVSK_M);
189
+ ], DomainSeparator.OVSK_M);
177
190
  }
178
191
  /**
179
192
  * Retrieves the sk_m corresponding to the pk_m.
@@ -181,18 +194,39 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
181
194
  * @param pkM - The master public key to get secret key for.
182
195
  * @returns A Promise that resolves to sk_m.
183
196
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
184
- */ async getMasterSecretKey(pkM) {
185
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
186
- const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
187
- if (!secretKeyBuffer) {
188
- throw new Error(`Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`);
189
- }
190
- const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
191
- const derivedpkM = await derivePublicKeyFromSecretKey(skM);
192
- if (!derivedpkM.equals(pkM)) {
193
- throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
194
- }
195
- return Promise.resolve(skM);
197
+ */ getMasterSecretKey(pkM) {
198
+ return this.#db.transactionAsync(async ()=>{
199
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
200
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
201
+ const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
202
+ if (!secretKeyBuffer) {
203
+ throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`);
204
+ }
205
+ const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
206
+ // Non-DB computation safe because no further IDB operations follow.
207
+ const derivedpkM = await derivePublicKeyFromSecretKey(skM);
208
+ if (!derivedpkM.equals(pkM)) {
209
+ throw new Error(`Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
210
+ }
211
+ return skM;
212
+ });
213
+ }
214
+ /**
215
+ * Checks whether a given account has a key matching the provided master public key hash.
216
+ * @param account - The account address to check.
217
+ * @param pkMHash - The master public key hash to look for.
218
+ * @returns True if the account has a key with the given hash.
219
+ */ accountHasKey(account, pkMHash) {
220
+ return this.#db.transactionAsync(async ()=>{
221
+ const pkMHashBuffer = serializeToBuffer(pkMHash);
222
+ for (const prefix of KEY_PREFIXES){
223
+ const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
224
+ if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
225
+ return true;
226
+ }
227
+ }
228
+ return false;
229
+ });
196
230
  }
197
231
  /**
198
232
  * Gets the key prefix and account address for a given value.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/key-store",
3
- "version": "0.0.1-commit.fffb133c",
3
+ "version": "0.0.1-dev",
4
4
  "type": "module",
5
5
  "exports": "./dest/index.js",
6
6
  "typedocOptions": {
@@ -57,10 +57,10 @@
57
57
  ]
58
58
  },
59
59
  "dependencies": {
60
- "@aztec/constants": "0.0.1-commit.fffb133c",
61
- "@aztec/foundation": "0.0.1-commit.fffb133c",
62
- "@aztec/kv-store": "0.0.1-commit.fffb133c",
63
- "@aztec/stdlib": "0.0.1-commit.fffb133c",
60
+ "@aztec/constants": "0.0.1-dev",
61
+ "@aztec/foundation": "0.0.1-dev",
62
+ "@aztec/kv-store": "0.0.1-dev",
63
+ "@aztec/stdlib": "0.0.1-dev",
64
64
  "tslib": "^2.4.0"
65
65
  },
66
66
  "devDependencies": {
package/src/key_store.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GeneratorIndex } from '@aztec/constants';
1
+ import { DomainSeparator } from '@aztec/constants';
2
2
  import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
@@ -17,14 +17,21 @@ import {
17
17
  derivePublicKeyFromSecretKey,
18
18
  } from '@aztec/stdlib/keys';
19
19
 
20
+ /** Maps a key prefix to the storage suffix for the corresponding master secret key. */
21
+ function secretKeyStorageSuffix(prefix: KeyPrefix): string {
22
+ return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
23
+ }
24
+
20
25
  /**
21
26
  * Used for managing keys. Can hold keys of multiple accounts.
22
27
  */
23
28
  export class KeyStore {
24
29
  public static readonly SCHEMA_VERSION = 1;
30
+ #db: AztecAsyncKVStore;
25
31
  #keys: AztecAsyncMap<string, Buffer>;
26
32
 
27
33
  constructor(database: AztecAsyncKVStore) {
34
+ this.#db = database;
28
35
  this.#keys = database.openMap('key_store');
29
36
  }
30
37
 
@@ -46,7 +53,7 @@ export class KeyStore {
46
53
  */
47
54
  public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
48
55
  const {
49
- masterNullifierSecretKey,
56
+ masterNullifierHidingKey,
50
57
  masterIncomingViewingSecretKey,
51
58
  masterOutgoingViewingSecretKey,
52
59
  masterTaggingSecretKey,
@@ -56,27 +63,31 @@ export class KeyStore {
56
63
  const completeAddress = await CompleteAddress.fromSecretKeyAndPartialAddress(sk, partialAddress);
57
64
  const { address: account } = completeAddress;
58
65
 
59
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
60
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
61
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
62
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
63
- await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
64
-
65
- await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
66
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
67
- await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
68
- await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
69
-
70
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
71
- // using the #getKeyPrefixAndAccount function later on
66
+ // Compute hashes before transaction
72
67
  const masterNullifierPublicKeyHash = await publicKeys.masterNullifierPublicKey.hash();
73
- await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
74
68
  const masterIncomingViewingPublicKeyHash = await publicKeys.masterIncomingViewingPublicKey.hash();
75
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
76
69
  const masterOutgoingViewingPublicKeyHash = await publicKeys.masterOutgoingViewingPublicKey.hash();
77
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
78
70
  const masterTaggingPublicKeyHash = await publicKeys.masterTaggingPublicKey.hash();
79
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
71
+
72
+ await this.#db.transactionAsync(async () => {
73
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
74
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
75
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
76
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
77
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
78
+
79
+ await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
80
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
81
+ await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
82
+ await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
83
+
84
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
85
+ // using the #getKeyPrefixAndAccount function later on
86
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, masterNullifierPublicKeyHash.toBuffer());
87
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
88
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, masterOutgoingViewingPublicKeyHash.toBuffer());
89
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, masterTaggingPublicKeyHash.toBuffer());
90
+ });
80
91
 
81
92
  // At last, we return the newly derived account address
82
93
  return completeAddress;
@@ -93,6 +104,11 @@ export class KeyStore {
93
104
  return accounts.map(account => AztecAddress.fromString(account));
94
105
  }
95
106
 
107
+ /** Checks whether an account is registered in the key store. */
108
+ public async hasAccount(account: AztecAddress): Promise<boolean> {
109
+ return !!(await this.#keys.getAsync(`${account.toString()}-ivsk_m`));
110
+ }
111
+
96
112
  /**
97
113
  * Gets the key validation request for a given master public key hash and contract address.
98
114
  * @throws If the account corresponding to the master public key hash does not exist in the key store.
@@ -100,43 +116,46 @@ export class KeyStore {
100
116
  * @param contractAddress - The contract address to silo the secret key in the key validation request with.
101
117
  * @returns The key validation request.
102
118
  */
103
- public async getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
104
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
119
+ public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
120
+ return this.#db.transactionAsync(async () => {
121
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkMHash);
105
122
 
106
- // Now we find the master public key for the account
107
- const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
108
- if (!pkMBuffer) {
109
- throw new Error(
110
- `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
111
- );
112
- }
123
+ // Now we find the master public key for the account
124
+ const pkMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}pk_m`);
125
+ if (!pkMBuffer) {
126
+ throw new Error(
127
+ `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
128
+ );
129
+ }
113
130
 
114
- const pkM = Point.fromBuffer(pkMBuffer);
115
- const computedPkMHash = await pkM.hash();
116
- if (!computedPkMHash.equals(pkMHash)) {
117
- throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
118
- }
131
+ const pkM = Point.fromBuffer(pkMBuffer);
119
132
 
120
- // Now we find the secret key for the public key
121
- const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
122
- if (!skMBuffer) {
123
- throw new Error(
124
- `Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
125
- );
126
- }
133
+ // Now we find the secret key for the public key
134
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
135
+ const skMBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
136
+ if (!skMBuffer) {
137
+ throw new Error(
138
+ `Could not find ${skStorageSuffix} for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
139
+ );
140
+ }
127
141
 
128
- const skM = GrumpkinScalar.fromBuffer(skMBuffer);
142
+ const skM = GrumpkinScalar.fromBuffer(skMBuffer);
129
143
 
130
- // We sanity check that it's possible to derive the public key from the secret key
131
- const derivedPkM = await derivePublicKeyFromSecretKey(skM);
132
- if (!derivedPkM.equals(pkM)) {
133
- throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
134
- }
144
+ // The remaining awaits are non-DB computations. They are safe because no further IDB operations follow them.
145
+ const computedPkMHash = await pkM.hash();
146
+ if (!computedPkMHash.equals(pkMHash)) {
147
+ throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
148
+ }
135
149
 
136
- // At last we silo the secret key and return the key validation request
137
- const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
150
+ const derivedPkM = await derivePublicKeyFromSecretKey(skM);
151
+ if (!derivedPkM.equals(pkM)) {
152
+ throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
153
+ }
138
154
 
139
- return new KeyValidationRequest(pkM, skApp);
155
+ const skApp = await computeAppSecretKey(skM, contractAddress, keyPrefix!);
156
+
157
+ return new KeyValidationRequest(pkM, skApp);
158
+ });
140
159
  }
141
160
 
142
161
  /**
@@ -237,7 +256,7 @@ export class KeyStore {
237
256
 
238
257
  return poseidon2HashWithSeparator(
239
258
  [masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
240
- GeneratorIndex.OVSK_M,
259
+ DomainSeparator.OVSK_M,
241
260
  );
242
261
  }
243
262
 
@@ -248,23 +267,49 @@ export class KeyStore {
248
267
  * @returns A Promise that resolves to sk_m.
249
268
  * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
250
269
  */
251
- public async getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar> {
252
- const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
270
+ public getMasterSecretKey(pkM: PublicKey): Promise<GrumpkinScalar> {
271
+ return this.#db.transactionAsync(async () => {
272
+ const [keyPrefix, account] = await this.getKeyPrefixAndAccount(pkM);
253
273
 
254
- const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${keyPrefix}sk_m`);
255
- if (!secretKeyBuffer) {
256
- throw new Error(
257
- `Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
258
- );
259
- }
274
+ const skStorageSuffix = secretKeyStorageSuffix(keyPrefix);
275
+ const secretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-${skStorageSuffix}`);
276
+ if (!secretKeyBuffer) {
277
+ throw new Error(
278
+ `Could not find ${skStorageSuffix} for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
279
+ );
280
+ }
260
281
 
261
- const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
262
- const derivedpkM = await derivePublicKeyFromSecretKey(skM);
263
- if (!derivedpkM.equals(pkM)) {
264
- throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
265
- }
282
+ const skM = GrumpkinScalar.fromBuffer(secretKeyBuffer);
266
283
 
267
- return Promise.resolve(skM);
284
+ // Non-DB computation — safe because no further IDB operations follow.
285
+ const derivedpkM = await derivePublicKeyFromSecretKey(skM);
286
+ if (!derivedpkM.equals(pkM)) {
287
+ throw new Error(
288
+ `Could not find ${skStorageSuffix} for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`,
289
+ );
290
+ }
291
+
292
+ return skM;
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Checks whether a given account has a key matching the provided master public key hash.
298
+ * @param account - The account address to check.
299
+ * @param pkMHash - The master public key hash to look for.
300
+ * @returns True if the account has a key with the given hash.
301
+ */
302
+ public accountHasKey(account: AztecAddress, pkMHash: Fr): Promise<boolean> {
303
+ return this.#db.transactionAsync(async () => {
304
+ const pkMHashBuffer = serializeToBuffer(pkMHash);
305
+ for (const prefix of KEY_PREFIXES) {
306
+ const stored = await this.#keys.getAsync(`${account.toString()}-${prefix}pk_m_hash`);
307
+ if (stored && Buffer.from(stored).equals(pkMHashBuffer)) {
308
+ return true;
309
+ }
310
+ }
311
+ return false;
312
+ });
268
313
  }
269
314
 
270
315
  /**