@aztec/key-store 5.0.0-rc.2 → 5.0.0

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.
@@ -0,0 +1,23 @@
1
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
2
+ import type { PublicKey } from '@aztec/stdlib/keys';
3
+ /**
4
+ * The four master privacy secret keys the key store holds for an account: the nullifier-hiding, incoming-viewing,
5
+ * outgoing-viewing, and tagging keys.
6
+ */
7
+ export type AccountPrivacySecretKeys = {
8
+ masterNullifierHidingSecretKey: GrumpkinScalar;
9
+ masterIncomingViewingSecretKey: GrumpkinScalar;
10
+ masterOutgoingViewingSecretKey: GrumpkinScalar;
11
+ masterTaggingSecretKey: GrumpkinScalar;
12
+ };
13
+ /**
14
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
15
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
16
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
17
+ * reconstruct the account's address).
18
+ */
19
+ export type AccountPrivacyKeys = AccountPrivacySecretKeys & {
20
+ masterMessageSigningPublicKey: PublicKey;
21
+ masterFallbackPublicKey: PublicKey;
22
+ };
23
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjb3VudF9wcml2YWN5X2tleXMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9hY2NvdW50X3ByaXZhY3lfa2V5cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxjQUFjLEVBQUUsTUFBTSxtQ0FBbUMsQ0FBQztBQUN4RSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUVwRDs7O0dBR0c7QUFDSCxNQUFNLE1BQU0sd0JBQXdCLEdBQUc7SUFDckMsOEJBQThCLEVBQUUsY0FBYyxDQUFDO0lBQy9DLDhCQUE4QixFQUFFLGNBQWMsQ0FBQztJQUMvQyw4QkFBOEIsRUFBRSxjQUFjLENBQUM7SUFDL0Msc0JBQXNCLEVBQUUsY0FBYyxDQUFDO0NBQ3hDLENBQUM7QUFFRjs7Ozs7R0FLRztBQUNILE1BQU0sTUFBTSxrQkFBa0IsR0FBRyx3QkFBd0IsR0FBRztJQUMxRCw2QkFBNkIsRUFBRSxTQUFTLENBQUM7SUFDekMsdUJBQXVCLEVBQUUsU0FBUyxDQUFDO0NBQ3BDLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account_privacy_keys.d.ts","sourceRoot":"","sources":["../src/account_privacy_keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,8BAA8B,EAAE,cAAc,CAAC;IAC/C,8BAA8B,EAAE,cAAc,CAAC;IAC/C,8BAA8B,EAAE,cAAc,CAAC;IAC/C,sBAAsB,EAAE,cAAc,CAAC;CACxC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,wBAAwB,GAAG;IAC1D,6BAA6B,EAAE,SAAS,CAAC;IACzC,uBAAuB,EAAE,SAAS,CAAC;CACpC,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
3
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
4
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
5
+ * reconstruct the account's address).
6
+ */ export { };
package/dest/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGdCQUFnQixDQUFDIn0=
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsZ0JBQWdCLENBQUMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC"}
package/dest/index.js CHANGED
@@ -1 +1,2 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
@@ -6,6 +6,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
6
6
  import { CompleteAddress, type PartialAddress } from '@aztec/stdlib/contract';
7
7
  import { KeyValidationRequest } from '@aztec/stdlib/kernel';
8
8
  import { type KeyPrefix, type PublicKey } from '@aztec/stdlib/keys';
9
+ import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
9
10
  /**
10
11
  * Used for managing keys. Can hold keys of multiple accounts.
11
12
  */
@@ -14,17 +15,19 @@ export declare class KeyStore {
14
15
  static readonly SCHEMA_VERSION = 1;
15
16
  constructor(database: AztecAsyncKVStore);
16
17
  /**
17
- * Creates a new account from a randomly generated secret key.
18
- * @returns A promise that resolves to the newly created account's CompleteAddress.
19
- */
20
- createAccount(): Promise<CompleteAddress>;
21
- /**
22
- * Adds an account to the key store from the provided secret key.
23
- * @param sk - The secret key of the account.
18
+ * Adds an account to the key store.
19
+ *
20
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
21
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
22
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
23
+ * address, which commits to all six master public keys.
24
+ *
25
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
24
26
  * @param partialAddress - The partial address of the account.
25
27
  * @returns The account's complete address.
28
+ * @throws If any of the account's six master public keys would be the point at infinity.
26
29
  */
27
- addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress>;
30
+ addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress>;
28
31
  /**
29
32
  * Retrieves addresses of accounts stored in the key store.
30
33
  * @returns A Promise that resolves to an array of account addresses.
@@ -43,38 +46,36 @@ export declare class KeyStore {
43
46
  /**
44
47
  * Gets the master nullifier public key for a given account.
45
48
  * @throws If the account does not exist in the key store.
46
- * @param account - The account address for which to retrieve the master nullifier public key.
47
- * @returns The master nullifier public key for the account.
48
49
  */
49
- getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey>;
50
+ getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey>;
50
51
  /**
51
52
  * Gets the master incoming viewing public key for a given account.
52
53
  * @throws If the account does not exist in the key store.
53
- * @param account - The account address for which to retrieve the master incoming viewing public key.
54
- * @returns The master incoming viewing public key for the account.
55
54
  */
56
55
  getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
57
56
  /**
58
57
  * Retrieves the master outgoing viewing public key.
59
58
  * @throws If the account does not exist in the key store.
60
- * @param account - The account to retrieve the master outgoing viewing key for.
61
- * @returns A Promise that resolves to the master outgoing viewing key.
62
59
  */
63
60
  getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey>;
64
61
  /**
65
62
  * Retrieves the master tagging public key.
66
63
  * @throws If the account does not exist in the key store.
67
- * @param account - The account to retrieve the master tagging key for.
68
- * @returns A Promise that resolves to the master tagging key.
69
64
  */
70
65
  getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey>;
71
66
  /**
72
67
  * Retrieves master incoming viewing secret key.
73
68
  * @throws If the account does not exist in the key store.
74
- * @param account - The account to retrieve the master incoming viewing secret key for.
75
- * @returns A Promise that resolves to the master incoming viewing secret key.
76
69
  */
77
70
  getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar>;
71
+ /**
72
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
73
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
74
+ * fallback secret keys are not held by the key store and so are not returned.
75
+ *
76
+ * @throws If the account does not exist in the key store.
77
+ */
78
+ getAccountSecretKeys(account: AztecAddress): Promise<AccountPrivacySecretKeys>;
78
79
  /**
79
80
  * Retrieves application outgoing viewing secret key.
80
81
  * @throws If the account does not exist in the key store.
@@ -106,4 +107,4 @@ export declare class KeyStore {
106
107
  */
107
108
  getKeyPrefixAndAccount(value: Bufferable): Promise<[KeyPrefix, AztecAddress]>;
108
109
  }
109
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUtmLE1BQU0sb0JBQW9CLENBQUM7QUFPNUI7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7T0FHRztJQUNJLGFBQWEsSUFBSSxPQUFPLENBQUMsZUFBZSxDQUFDLENBSS9DO0lBRUQ7Ozs7O09BS0c7SUFDVSxVQUFVLENBQUMsRUFBRSxFQUFFLEVBQUUsRUFBRSxjQUFjLEVBQUUsY0FBYyxHQUFHLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0ErQ3hGO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQTBDeEc7SUFFRDs7Ozs7T0FLRztJQUNVLDJCQUEyQixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFsRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBUXhGO0lBRUQ7Ozs7O09BS0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FReEY7SUFFRDs7Ozs7T0FLRztJQUNVLHlCQUF5QixDQUFDLE9BQU8sRUFBRSxZQUFZLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQVFoRjtJQUVEOzs7OztPQUtHO0lBQ1UsaUNBQWlDLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBUTdGO0lBRUQ7Ozs7OztPQU1HO0lBQ1UsOEJBQThCLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0Fhakc7SUFFRDs7Ozs7O09BTUc7SUFDSSxrQkFBa0IsQ0FBQyxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0E2QjlEO0lBRUQ7Ozs7O09BS0c7SUFDSSxhQUFhLENBQUMsT0FBTyxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FXekU7SUFFRDs7Ozs7T0FLRztJQUNVLHNCQUFzQixDQUFDLEtBQUssRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksQ0FBQyxDQUFDLENBY3pGO0NBQ0YifQ==
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2V5X3N0b3JlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMva2V5X3N0b3JlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRCxPQUFPLEVBQUUsY0FBYyxFQUFTLE1BQU0sbUNBQW1DLENBQUM7QUFFMUUsT0FBTyxFQUFFLEtBQUssVUFBVSxFQUFxQixNQUFNLDZCQUE2QixDQUFDO0FBQ2pGLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFpQixNQUFNLGlCQUFpQixDQUFDO0FBQ3hFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsZUFBZSxFQUFFLEtBQUssY0FBYyxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUUsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDNUQsT0FBTyxFQUVMLEtBQUssU0FBUyxFQUNkLEtBQUssU0FBUyxFQUtmLE1BQU0sb0JBQW9CLENBQUM7QUFFNUIsT0FBTyxLQUFLLEVBQUUsa0JBQWtCLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQStEOUY7O0dBRUc7QUFDSCxxQkFBYSxRQUFROztJQUNuQixnQkFBdUIsY0FBYyxLQUFLO0lBSTFDLFlBQVksUUFBUSxFQUFFLGlCQUFpQixFQUd0QztJQUVEOzs7Ozs7Ozs7Ozs7T0FZRztJQUNVLFVBQVUsQ0FBQyxJQUFJLEVBQUUsa0JBQWtCLEVBQUUsY0FBYyxFQUFFLGNBQWMsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLENBRzFHO0lBRUQ7OztPQUdHO0lBQ1UsV0FBVyxJQUFJLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUtsRDtJQUVELGdFQUFnRTtJQUNuRCxVQUFVLENBQUMsT0FBTyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRS9EO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksdUJBQXVCLENBQUMsT0FBTyxFQUFFLEVBQUUsRUFBRSxlQUFlLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxDQTBDeEc7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFeEY7SUFFRDs7O09BR0c7SUFDVSx5QkFBeUIsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FFaEY7SUFFRDs7O09BR0c7SUFDVSxpQ0FBaUMsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsQ0FFN0Y7SUFFRDs7Ozs7O09BTUc7SUFDVSxvQkFBb0IsQ0FBQyxPQUFPLEVBQUUsWUFBWSxHQUFHLE9BQU8sQ0FBQyx3QkFBd0IsQ0FBQyxDQVExRjtJQUVEOzs7Ozs7T0FNRztJQUNVLDhCQUE4QixDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFLFlBQVksR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLENBT2pHO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksa0JBQWtCLENBQUMsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsY0FBYyxDQUFDLENBNkI5RDtJQUVEOzs7OztPQUtHO0lBQ0ksYUFBYSxDQUFDLE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBV3pFO0lBRUQ7Ozs7O09BS0c7SUFDVSxzQkFBc0IsQ0FBQyxLQUFLLEVBQUUsVUFBVSxHQUFHLE9BQU8sQ0FBQyxDQUFDLFNBQVMsRUFBRSxZQUFZLENBQUMsQ0FBQyxDQWN6RjtDQWdGRiJ9
@@ -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,EAKf,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,CA+CxF;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,CA0CxG;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,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B9D;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"}
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,EAKf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AA+D9F;;GAEG;AACH,qBAAa,QAAQ;;IACnB,gBAAuB,cAAc,KAAK;IAI1C,YAAY,QAAQ,EAAE,iBAAiB,EAGtC;IAED;;;;;;;;;;;;OAYG;IACU,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAG1G;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,CA0CxG;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAExF;IAED;;;OAGG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAEhF;IAED;;;OAGG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAE7F;IAED;;;;;;OAMG;IACU,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAQ1F;IAED;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,CAOjG;IAED;;;;;;OAMG;IACI,kBAAkB,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CA6B9D;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;CAgFF"}
package/dest/key_store.js CHANGED
@@ -1,16 +1,51 @@
1
1
  import { DomainSeparator } from '@aztec/constants';
2
2
  import { poseidon2HashWithSeparator } from '@aztec/foundation/crypto/poseidon';
3
- import { Fr } from '@aztec/foundation/curves/bn254';
4
3
  import { GrumpkinScalar, Point } from '@aztec/foundation/curves/grumpkin';
5
4
  import { toArray } from '@aztec/foundation/iterable';
6
5
  import { serializeToBuffer } from '@aztec/foundation/serialize';
7
6
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
8
7
  import { CompleteAddress } from '@aztec/stdlib/contract';
9
8
  import { KeyValidationRequest } from '@aztec/stdlib/kernel';
10
- import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecretKey, hashPublicKey } from '@aztec/stdlib/keys';
9
+ import { KEY_PREFIXES, PublicKeys, computeAppSecretKey, derivePublicKeyFromSecretKey, hashPublicKey } from '@aztec/stdlib/keys';
11
10
  /** Maps a key prefix to the storage suffix for the corresponding master secret key. */ function secretKeyStorageSuffix(prefix) {
12
11
  return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
13
12
  }
13
+ /**
14
+ * Computes the public counterparts of an account's four privacy secret keys and assembles its {@link PublicKeys} struct
15
+ * (used to derive the address), from the {@link AccountPrivacyKeys} passed to {@link KeyStore.addAccount}.
16
+ *
17
+ * The message-signing and fallback keys are already supplied as public keys, since the key store never holds their
18
+ * secrets.
19
+ */ async function completeAccountKeys(keys) {
20
+ const { masterNullifierHidingSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterMessageSigningPublicKey, masterFallbackPublicKey } = keys;
21
+ const masterNullifierHidingPublicKey = await derivePublicKeyFromSecretKey(masterNullifierHidingSecretKey);
22
+ const masterIncomingViewingPublicKey = await derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey);
23
+ const masterOutgoingViewingPublicKey = await derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey);
24
+ const masterTaggingPublicKey = await derivePublicKeyFromSecretKey(masterTaggingSecretKey);
25
+ for (const [name, publicKey] of Object.entries({
26
+ masterNullifierHidingPublicKey,
27
+ masterIncomingViewingPublicKey,
28
+ masterOutgoingViewingPublicKey,
29
+ masterTaggingPublicKey,
30
+ masterMessageSigningPublicKey,
31
+ masterFallbackPublicKey
32
+ })){
33
+ if (publicKey.isInfinite) {
34
+ throw new Error(`Cannot register an account with an infinity ${name}.`);
35
+ }
36
+ }
37
+ const publicKeys = new PublicKeys(await hashPublicKey(masterNullifierHidingPublicKey), masterIncomingViewingPublicKey, await hashPublicKey(masterOutgoingViewingPublicKey), await hashPublicKey(masterTaggingPublicKey), await hashPublicKey(masterMessageSigningPublicKey), await hashPublicKey(masterFallbackPublicKey));
38
+ return {
39
+ masterNullifierHidingSecretKey,
40
+ masterIncomingViewingSecretKey,
41
+ masterOutgoingViewingSecretKey,
42
+ masterTaggingSecretKey,
43
+ masterNullifierHidingPublicKey,
44
+ masterOutgoingViewingPublicKey,
45
+ masterTaggingPublicKey,
46
+ publicKeys
47
+ };
48
+ }
14
49
  /**
15
50
  * Used for managing keys. Can hold keys of multiple accounts.
16
51
  */ export class KeyStore {
@@ -22,60 +57,32 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
22
57
  this.#keys = database.openMap('key_store');
23
58
  }
24
59
  /**
25
- * Creates a new account from a randomly generated secret key.
26
- * @returns A promise that resolves to the newly created account's CompleteAddress.
27
- */ createAccount() {
28
- const sk = Fr.random();
29
- const partialAddress = Fr.random();
30
- return this.addAccount(sk, partialAddress);
31
- }
32
- /**
33
- * Adds an account to the key store from the provided secret key.
34
- * @param sk - The secret key of the account.
60
+ * Adds an account to the key store.
61
+ *
62
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
63
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
64
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
65
+ * address, which commits to all six master public keys.
66
+ *
67
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
35
68
  * @param partialAddress - The partial address of the account.
36
69
  * @returns The account's complete address.
37
- */ async addAccount(sk, partialAddress) {
38
- const { masterNullifierHidingKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterNullifierPublicKey, masterOutgoingViewingPublicKey, masterTaggingPublicKey, publicKeys } = await deriveKeys(sk);
39
- const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
40
- const { address: account } = completeAddress;
41
- // The kernel cannot check that nhpk/ovpk/tpk are on-curve or non-infinity, so the PXE/key-store
42
- // must guarantee it before persistence. By design, the above derivation produces points that are on
43
- // the curve and not at infinity.
44
- // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
45
- const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
46
- // The Message Signing and Fallback Keys don't have a derivation path yet, so we just use the default values for their hashes.
47
- // So we avoid storing them persistently. The default hash is still required for address derivation
48
- await this.#db.transactionAsync(async ()=>{
49
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
50
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
51
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
52
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
53
- await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
54
- await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierPublicKey.toBuffer());
55
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
56
- await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
57
- await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
58
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
59
- // using the #getKeyPrefixAndAccount function later on
60
- await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
61
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
62
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
63
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
64
- });
65
- // At last, we return the newly derived account address
66
- return completeAddress;
70
+ * @throws If any of the account's six master public keys would be the point at infinity.
71
+ */ async addAccount(keys, partialAddress) {
72
+ const accountKeys = await completeAccountKeys(keys);
73
+ return this.#storeAccountKeys(accountKeys, partialAddress);
67
74
  }
68
75
  /**
69
76
  * Retrieves addresses of accounts stored in the key store.
70
77
  * @returns A Promise that resolves to an array of account addresses.
71
78
  */ async getAccounts() {
72
- const allMapKeys = await toArray(this.#keys.keysAsync());
79
+ const allMapKeys = await this.#db.transactionAsync(()=>toArray(this.#keys.keysAsync()));
73
80
  // We return account addresses based on the map keys that end with '-ivsk_m'
74
81
  const accounts = allMapKeys.filter((key)=>key.endsWith('-ivsk_m')).map((key)=>key.split('-')[0]);
75
82
  return accounts.map((account)=>AztecAddress.fromStringUnsafe(account));
76
83
  }
77
84
  /** Checks whether an account is registered in the key store. */ async hasAccount(account) {
78
- return !!await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
85
+ return !!await this.#db.transactionAsync(()=>this.#keys.getAsync(`${account.toString()}-ivsk_m`));
79
86
  }
80
87
  /**
81
88
  * Gets the key validation request for a given master public key hash and contract address.
@@ -117,62 +124,52 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
117
124
  /**
118
125
  * Gets the master nullifier public key for a given account.
119
126
  * @throws If the account does not exist in the key store.
120
- * @param account - The account address for which to retrieve the master nullifier public key.
121
- * @returns The master nullifier public key for the account.
122
- */ async getMasterNullifierPublicKey(account) {
123
- const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
124
- if (!masterNullifierPublicKeyBuffer) {
125
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
126
- }
127
- return Point.fromBuffer(masterNullifierPublicKeyBuffer);
127
+ */ async getMasterNullifierHidingPublicKey(account) {
128
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
128
129
  }
129
130
  /**
130
131
  * Gets the master incoming viewing public key for a given account.
131
132
  * @throws If the account does not exist in the key store.
132
- * @param account - The account address for which to retrieve the master incoming viewing public key.
133
- * @returns The master incoming viewing public key for the account.
134
133
  */ async getMasterIncomingViewingPublicKey(account) {
135
- const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
136
- if (!masterIncomingViewingPublicKeyBuffer) {
137
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
138
- }
139
- return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
134
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
140
135
  }
141
136
  /**
142
137
  * Retrieves the master outgoing viewing public key.
143
138
  * @throws If the account does not exist in the key store.
144
- * @param account - The account to retrieve the master outgoing viewing key for.
145
- * @returns A Promise that resolves to the master outgoing viewing key.
146
139
  */ async getMasterOutgoingViewingPublicKey(account) {
147
- const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
148
- if (!masterOutgoingViewingPublicKeyBuffer) {
149
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
150
- }
151
- return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
140
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
152
141
  }
153
142
  /**
154
143
  * Retrieves the master tagging public key.
155
144
  * @throws If the account does not exist in the key store.
156
- * @param account - The account to retrieve the master tagging key for.
157
- * @returns A Promise that resolves to the master tagging key.
158
145
  */ async getMasterTaggingPublicKey(account) {
159
- const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
160
- if (!masterTaggingPublicKeyBuffer) {
161
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
162
- }
163
- return Point.fromBuffer(masterTaggingPublicKeyBuffer);
146
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
164
147
  }
165
148
  /**
166
149
  * Retrieves master incoming viewing secret key.
167
150
  * @throws If the account does not exist in the key store.
168
- * @param account - The account to retrieve the master incoming viewing secret key for.
169
- * @returns A Promise that resolves to the master incoming viewing secret key.
170
151
  */ async getMasterIncomingViewingSecretKey(account) {
171
- const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
172
- if (!masterIncomingViewingSecretKeyBuffer) {
173
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
174
- }
175
- return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
152
+ return GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivsk_m'));
153
+ }
154
+ /**
155
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
156
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
157
+ * fallback secret keys are not held by the key store and so are not returned.
158
+ *
159
+ * @throws If the account does not exist in the key store.
160
+ */ async getAccountSecretKeys(account) {
161
+ const [nhkM, ivskM, ovskM, tskM] = await this.#getMasterKeyBuffers(account, [
162
+ 'nhk_m',
163
+ 'ivsk_m',
164
+ 'ovsk_m',
165
+ 'tsk_m'
166
+ ]);
167
+ return {
168
+ masterNullifierHidingSecretKey: GrumpkinScalar.fromBuffer(nhkM),
169
+ masterIncomingViewingSecretKey: GrumpkinScalar.fromBuffer(ivskM),
170
+ masterOutgoingViewingSecretKey: GrumpkinScalar.fromBuffer(ovskM),
171
+ masterTaggingSecretKey: GrumpkinScalar.fromBuffer(tskM)
172
+ };
176
173
  }
177
174
  /**
178
175
  * Retrieves application outgoing viewing secret key.
@@ -181,11 +178,7 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
181
178
  * @param app - The application address to retrieve the outgoing viewing secret key for.
182
179
  * @returns A Promise that resolves to the application outgoing viewing secret key.
183
180
  */ async getAppOutgoingViewingSecretKey(account, app) {
184
- const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
185
- if (!masterOutgoingViewingSecretKeyBuffer) {
186
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
187
- }
188
- const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
181
+ const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
189
182
  return poseidon2HashWithSeparator([
190
183
  masterOutgoingViewingSecretKey.hi,
191
184
  masterOutgoingViewingSecretKey.lo,
@@ -260,4 +253,56 @@ import { KEY_PREFIXES, computeAppSecretKey, deriveKeys, derivePublicKeyFromSecre
260
253
  }
261
254
  throw new Error(`Could not find key prefix.`);
262
255
  }
256
+ /**
257
+ * Persists a completed set of account keys and returns the resulting complete address.
258
+ */ async #storeAccountKeys(accountKeys, partialAddress) {
259
+ const { masterNullifierHidingSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, masterNullifierHidingPublicKey, masterOutgoingViewingPublicKey, masterTaggingPublicKey, publicKeys } = accountKeys;
260
+ const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
261
+ const { address: account } = completeAddress;
262
+ // completeAccountKeys has already guaranteed these master public keys are non-infinity, which the kernel cannot
263
+ // check but the address relies on.
264
+ // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
265
+ const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
266
+ await this.#db.transactionAsync(async ()=>{
267
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m.
268
+ //
269
+ // The message-signing and fallback keys are not stored: their secret keys are withheld from the key store, and
270
+ // their public keys are only needed transiently to compute the address (they live in the AddressStore).
271
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
272
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
273
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
274
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingSecretKey.toBuffer());
275
+ await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierHidingPublicKey.toBuffer());
276
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
277
+ await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
278
+ await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
279
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
280
+ // using the #getKeyPrefixAndAccount function later on
281
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
282
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
283
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
284
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
285
+ });
286
+ return completeAddress;
287
+ }
288
+ /**
289
+ * Fetches a stored master key buffer for an account by its storage suffix (e.g. `npk_m`, `ivsk_m`).
290
+ * @throws If the account does not exist in the key store.
291
+ */ async #getMasterKeyBuffer(account, suffix) {
292
+ const [buffer] = await this.#getMasterKeyBuffers(account, [
293
+ suffix
294
+ ]);
295
+ return buffer;
296
+ }
297
+ /**
298
+ * Fetches multiple stored master key buffers for an account in a single transaction, returning them in the order of
299
+ * the requested storage suffixes.
300
+ * @throws If any of the keys is missing (i.e. the account does not exist in the key store).
301
+ */ async #getMasterKeyBuffers(account, suffixes) {
302
+ const buffers = await this.#db.transactionAsync(()=>Promise.all(suffixes.map((suffix)=>this.#keys.getAsync(`${account.toString()}-${suffix}`))));
303
+ if (!buffers.every((buffer)=>buffer !== undefined)) {
304
+ throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
305
+ }
306
+ return buffers;
307
+ }
263
308
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/key-store",
3
- "version": "5.0.0-rc.2",
3
+ "version": "5.0.0",
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": "5.0.0-rc.2",
61
- "@aztec/foundation": "5.0.0-rc.2",
62
- "@aztec/kv-store": "5.0.0-rc.2",
63
- "@aztec/stdlib": "5.0.0-rc.2",
60
+ "@aztec/constants": "5.0.0",
61
+ "@aztec/foundation": "5.0.0",
62
+ "@aztec/kv-store": "5.0.0",
63
+ "@aztec/stdlib": "5.0.0",
64
64
  "tslib": "^2.4.0"
65
65
  },
66
66
  "devDependencies": {
@@ -0,0 +1,24 @@
1
+ import type { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
2
+ import type { PublicKey } from '@aztec/stdlib/keys';
3
+
4
+ /**
5
+ * The four master privacy secret keys the key store holds for an account: the nullifier-hiding, incoming-viewing,
6
+ * outgoing-viewing, and tagging keys.
7
+ */
8
+ export type AccountPrivacySecretKeys = {
9
+ masterNullifierHidingSecretKey: GrumpkinScalar;
10
+ masterIncomingViewingSecretKey: GrumpkinScalar;
11
+ masterOutgoingViewingSecretKey: GrumpkinScalar;
12
+ masterTaggingSecretKey: GrumpkinScalar;
13
+ };
14
+
15
+ /**
16
+ * The keys needed to register an account: the four privacy secret keys the key store holds, plus the *public*
17
+ * message-signing and fallback keys. The message-signing and fallback secret keys are withheld from the key store (and
18
+ * hence from PXE, which embeds it), since it is not trusted to hold them: only their public keys are needed (to
19
+ * reconstruct the account's address).
20
+ */
21
+ export type AccountPrivacyKeys = AccountPrivacySecretKeys & {
22
+ masterMessageSigningPublicKey: PublicKey;
23
+ masterFallbackPublicKey: PublicKey;
24
+ };
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
+ export * from './account_privacy_keys.js';
1
2
  export * from './key_store.js';
package/src/key_store.ts CHANGED
@@ -12,17 +12,75 @@ import {
12
12
  KEY_PREFIXES,
13
13
  type KeyPrefix,
14
14
  type PublicKey,
15
+ PublicKeys,
15
16
  computeAppSecretKey,
16
- deriveKeys,
17
17
  derivePublicKeyFromSecretKey,
18
18
  hashPublicKey,
19
19
  } from '@aztec/stdlib/keys';
20
20
 
21
+ import type { AccountPrivacyKeys, AccountPrivacySecretKeys } from './account_privacy_keys.js';
22
+
21
23
  /** Maps a key prefix to the storage suffix for the corresponding master secret key. */
22
24
  function secretKeyStorageSuffix(prefix: KeyPrefix): string {
23
25
  return prefix === 'n' ? 'nhk_m' : `${prefix}sk_m`;
24
26
  }
25
27
 
28
+ /**
29
+ * Computes the public counterparts of an account's four privacy secret keys and assembles its {@link PublicKeys} struct
30
+ * (used to derive the address), from the {@link AccountPrivacyKeys} passed to {@link KeyStore.addAccount}.
31
+ *
32
+ * The message-signing and fallback keys are already supplied as public keys, since the key store never holds their
33
+ * secrets.
34
+ */
35
+ async function completeAccountKeys(keys: AccountPrivacyKeys) {
36
+ const {
37
+ masterNullifierHidingSecretKey,
38
+ masterIncomingViewingSecretKey,
39
+ masterOutgoingViewingSecretKey,
40
+ masterTaggingSecretKey,
41
+ masterMessageSigningPublicKey,
42
+ masterFallbackPublicKey,
43
+ } = keys;
44
+
45
+ const masterNullifierHidingPublicKey = await derivePublicKeyFromSecretKey(masterNullifierHidingSecretKey);
46
+ const masterIncomingViewingPublicKey = await derivePublicKeyFromSecretKey(masterIncomingViewingSecretKey);
47
+ const masterOutgoingViewingPublicKey = await derivePublicKeyFromSecretKey(masterOutgoingViewingSecretKey);
48
+ const masterTaggingPublicKey = await derivePublicKeyFromSecretKey(masterTaggingSecretKey);
49
+
50
+ for (const [name, publicKey] of Object.entries({
51
+ masterNullifierHidingPublicKey,
52
+ masterIncomingViewingPublicKey,
53
+ masterOutgoingViewingPublicKey,
54
+ masterTaggingPublicKey,
55
+ masterMessageSigningPublicKey,
56
+ masterFallbackPublicKey,
57
+ })) {
58
+ if (publicKey.isInfinite) {
59
+ throw new Error(`Cannot register an account with an infinity ${name}.`);
60
+ }
61
+ }
62
+
63
+ const publicKeys = new PublicKeys(
64
+ await hashPublicKey(masterNullifierHidingPublicKey),
65
+ masterIncomingViewingPublicKey,
66
+ await hashPublicKey(masterOutgoingViewingPublicKey),
67
+ await hashPublicKey(masterTaggingPublicKey),
68
+ await hashPublicKey(masterMessageSigningPublicKey),
69
+ await hashPublicKey(masterFallbackPublicKey),
70
+ );
71
+
72
+ return {
73
+ masterNullifierHidingSecretKey,
74
+ masterIncomingViewingSecretKey,
75
+ masterOutgoingViewingSecretKey,
76
+ masterTaggingSecretKey,
77
+ masterNullifierHidingPublicKey,
78
+ masterOutgoingViewingPublicKey,
79
+ masterTaggingPublicKey,
80
+ publicKeys,
81
+ };
82
+ }
83
+
26
84
  /**
27
85
  * Used for managing keys. Can hold keys of multiple accounts.
28
86
  */
@@ -37,68 +95,21 @@ export class KeyStore {
37
95
  }
38
96
 
39
97
  /**
40
- * Creates a new account from a randomly generated secret key.
41
- * @returns A promise that resolves to the newly created account's CompleteAddress.
42
- */
43
- public createAccount(): Promise<CompleteAddress> {
44
- const sk = Fr.random();
45
- const partialAddress = Fr.random();
46
- return this.addAccount(sk, partialAddress);
47
- }
48
-
49
- /**
50
- * Adds an account to the key store from the provided secret key.
51
- * @param sk - The secret key of the account.
98
+ * Adds an account to the key store.
99
+ *
100
+ * The key store holds the four privacy secret keys (nullifier-hiding, incoming-viewing, outgoing-viewing, tagging),
101
+ * but only the *public* message-signing and fallback keys: their secret keys are withheld, since the key store (and
102
+ * PXE, which embeds it) is not trusted to hold them. The public keys are still needed to reconstruct the account's
103
+ * address, which commits to all six master public keys.
104
+ *
105
+ * @param keys - The account's privacy keys: four secret keys plus the message-signing and fallback public keys.
52
106
  * @param partialAddress - The partial address of the account.
53
107
  * @returns The account's complete address.
108
+ * @throws If any of the account's six master public keys would be the point at infinity.
54
109
  */
55
- public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
56
- const {
57
- masterNullifierHidingKey,
58
- masterIncomingViewingSecretKey,
59
- masterOutgoingViewingSecretKey,
60
- masterTaggingSecretKey,
61
- masterNullifierPublicKey,
62
- masterOutgoingViewingPublicKey,
63
- masterTaggingPublicKey,
64
- publicKeys,
65
- } = await deriveKeys(sk);
66
-
67
- const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
68
- const { address: account } = completeAddress;
69
-
70
- // The kernel cannot check that nhpk/ovpk/tpk are on-curve or non-infinity, so the PXE/key-store
71
- // must guarantee it before persistence. By design, the above derivation produces points that are on
72
- // the curve and not at infinity.
73
-
74
- // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
75
- const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
76
-
77
- // The Message Signing and Fallback Keys don't have a derivation path yet, so we just use the default values for their hashes.
78
- // So we avoid storing them persistently. The default hash is still required for address derivation
79
-
80
- await this.#db.transactionAsync(async () => {
81
- // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
82
- await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
83
- await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
84
- await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
85
- await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingKey.toBuffer());
86
-
87
- await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierPublicKey.toBuffer());
88
- await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
89
- await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
90
- await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
91
-
92
- // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
93
- // using the #getKeyPrefixAndAccount function later on
94
- await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
95
- await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
96
- await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
97
- await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
98
- });
99
-
100
- // At last, we return the newly derived account address
101
- return completeAddress;
110
+ public async addAccount(keys: AccountPrivacyKeys, partialAddress: PartialAddress): Promise<CompleteAddress> {
111
+ const accountKeys = await completeAccountKeys(keys);
112
+ return this.#storeAccountKeys(accountKeys, partialAddress);
102
113
  }
103
114
 
104
115
  /**
@@ -106,7 +117,7 @@ export class KeyStore {
106
117
  * @returns A Promise that resolves to an array of account addresses.
107
118
  */
108
119
  public async getAccounts(): Promise<AztecAddress[]> {
109
- const allMapKeys = await toArray(this.#keys.keysAsync());
120
+ const allMapKeys = await this.#db.transactionAsync(() => toArray(this.#keys.keysAsync()));
110
121
  // We return account addresses based on the map keys that end with '-ivsk_m'
111
122
  const accounts = allMapKeys.filter(key => key.endsWith('-ivsk_m')).map(key => key.split('-')[0]);
112
123
  return accounts.map(account => AztecAddress.fromStringUnsafe(account));
@@ -114,7 +125,7 @@ export class KeyStore {
114
125
 
115
126
  /** Checks whether an account is registered in the key store. */
116
127
  public async hasAccount(account: AztecAddress): Promise<boolean> {
117
- return !!(await this.#keys.getAsync(`${account.toString()}-ivsk_m`));
128
+ return !!(await this.#db.transactionAsync(() => this.#keys.getAsync(`${account.toString()}-ivsk_m`)));
118
129
  }
119
130
 
120
131
  /**
@@ -171,81 +182,58 @@ export class KeyStore {
171
182
  /**
172
183
  * Gets the master nullifier public key for a given account.
173
184
  * @throws If the account does not exist in the key store.
174
- * @param account - The account address for which to retrieve the master nullifier public key.
175
- * @returns The master nullifier public key for the account.
176
185
  */
177
- public async getMasterNullifierPublicKey(account: AztecAddress): Promise<PublicKey> {
178
- const masterNullifierPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-npk_m`);
179
- if (!masterNullifierPublicKeyBuffer) {
180
- throw new Error(
181
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
182
- );
183
- }
184
- return Point.fromBuffer(masterNullifierPublicKeyBuffer);
186
+ public async getMasterNullifierHidingPublicKey(account: AztecAddress): Promise<PublicKey> {
187
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'npk_m'));
185
188
  }
186
189
 
187
190
  /**
188
191
  * Gets the master incoming viewing public key for a given account.
189
192
  * @throws If the account does not exist in the key store.
190
- * @param account - The account address for which to retrieve the master incoming viewing public key.
191
- * @returns The master incoming viewing public key for the account.
192
193
  */
193
194
  public async getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
194
- const masterIncomingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivpk_m`);
195
- if (!masterIncomingViewingPublicKeyBuffer) {
196
- throw new Error(
197
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
198
- );
199
- }
200
- return Point.fromBuffer(masterIncomingViewingPublicKeyBuffer);
195
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivpk_m'));
201
196
  }
202
197
 
203
198
  /**
204
199
  * Retrieves the master outgoing viewing public key.
205
200
  * @throws If the account does not exist in the key store.
206
- * @param account - The account to retrieve the master outgoing viewing key for.
207
- * @returns A Promise that resolves to the master outgoing viewing key.
208
201
  */
209
202
  public async getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
210
- const masterOutgoingViewingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovpk_m`);
211
- if (!masterOutgoingViewingPublicKeyBuffer) {
212
- throw new Error(
213
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
214
- );
215
- }
216
- return Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer);
203
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovpk_m'));
217
204
  }
218
205
 
219
206
  /**
220
207
  * Retrieves the master tagging public key.
221
208
  * @throws If the account does not exist in the key store.
222
- * @param account - The account to retrieve the master tagging key for.
223
- * @returns A Promise that resolves to the master tagging key.
224
209
  */
225
210
  public async getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey> {
226
- const masterTaggingPublicKeyBuffer = await this.#keys.getAsync(`${account.toString()}-tpk_m`);
227
- if (!masterTaggingPublicKeyBuffer) {
228
- throw new Error(
229
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
230
- );
231
- }
232
- return Point.fromBuffer(masterTaggingPublicKeyBuffer);
211
+ return Point.fromBuffer(await this.#getMasterKeyBuffer(account, 'tpk_m'));
233
212
  }
234
213
 
235
214
  /**
236
215
  * Retrieves master incoming viewing secret key.
237
216
  * @throws If the account does not exist in the key store.
238
- * @param account - The account to retrieve the master incoming viewing secret key for.
239
- * @returns A Promise that resolves to the master incoming viewing secret key.
240
217
  */
241
218
  public async getMasterIncomingViewingSecretKey(account: AztecAddress): Promise<GrumpkinScalar> {
242
- const masterIncomingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ivsk_m`);
243
- if (!masterIncomingViewingSecretKeyBuffer) {
244
- throw new Error(
245
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
246
- );
247
- }
248
- return GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
219
+ return GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ivsk_m'));
220
+ }
221
+
222
+ /**
223
+ * Retrieves the four privacy secret keys the key store holds for an account. Paired with {@link addAccount}, this
224
+ * allows exporting an account's privacy secret keys, e.g. to re-register it on another PXE. The message-signing and
225
+ * fallback secret keys are not held by the key store and so are not returned.
226
+ *
227
+ * @throws If the account does not exist in the key store.
228
+ */
229
+ public async getAccountSecretKeys(account: AztecAddress): Promise<AccountPrivacySecretKeys> {
230
+ const [nhkM, ivskM, ovskM, tskM] = await this.#getMasterKeyBuffers(account, ['nhk_m', 'ivsk_m', 'ovsk_m', 'tsk_m']);
231
+ return {
232
+ masterNullifierHidingSecretKey: GrumpkinScalar.fromBuffer(nhkM),
233
+ masterIncomingViewingSecretKey: GrumpkinScalar.fromBuffer(ivskM),
234
+ masterOutgoingViewingSecretKey: GrumpkinScalar.fromBuffer(ovskM),
235
+ masterTaggingSecretKey: GrumpkinScalar.fromBuffer(tskM),
236
+ };
249
237
  }
250
238
 
251
239
  /**
@@ -256,13 +244,7 @@ export class KeyStore {
256
244
  * @returns A Promise that resolves to the application outgoing viewing secret key.
257
245
  */
258
246
  public async getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
259
- const masterOutgoingViewingSecretKeyBuffer = await this.#keys.getAsync(`${account.toString()}-ovsk_m`);
260
- if (!masterOutgoingViewingSecretKeyBuffer) {
261
- throw new Error(
262
- `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
263
- );
264
- }
265
- const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
247
+ const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(await this.#getMasterKeyBuffer(account, 'ovsk_m'));
266
248
 
267
249
  return poseidon2HashWithSeparator(
268
250
  [masterOutgoingViewingSecretKey.hi, masterOutgoingViewingSecretKey.lo, app],
@@ -348,4 +330,83 @@ export class KeyStore {
348
330
  }
349
331
  throw new Error(`Could not find key prefix.`);
350
332
  }
333
+
334
+ /**
335
+ * Persists a completed set of account keys and returns the resulting complete address.
336
+ */
337
+ async #storeAccountKeys(
338
+ accountKeys: Awaited<ReturnType<typeof completeAccountKeys>>,
339
+ partialAddress: PartialAddress,
340
+ ): Promise<CompleteAddress> {
341
+ const {
342
+ masterNullifierHidingSecretKey,
343
+ masterIncomingViewingSecretKey,
344
+ masterOutgoingViewingSecretKey,
345
+ masterTaggingSecretKey,
346
+ masterNullifierHidingPublicKey,
347
+ masterOutgoingViewingPublicKey,
348
+ masterTaggingPublicKey,
349
+ publicKeys,
350
+ } = accountKeys;
351
+
352
+ const completeAddress = await CompleteAddress.fromPublicKeysAndPartialAddress(publicKeys, partialAddress);
353
+ const { address: account } = completeAddress;
354
+
355
+ // completeAccountKeys has already guaranteed these master public keys are non-infinity, which the kernel cannot
356
+ // check but the address relies on.
357
+
358
+ // The npk/ovpk/tpk hashes are already in publicKeys; ivpk_m_hash is computed for indexing.
359
+ const masterIncomingViewingPublicKeyHash = await hashPublicKey(publicKeys.ivpkM);
360
+
361
+ await this.#db.transactionAsync(async () => {
362
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m.
363
+ //
364
+ // The message-signing and fallback keys are not stored: their secret keys are withheld from the key store, and
365
+ // their public keys are only needed transiently to compute the address (they live in the AddressStore).
366
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
367
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
368
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
369
+ await this.#keys.set(`${account.toString()}-nhk_m`, masterNullifierHidingSecretKey.toBuffer());
370
+
371
+ await this.#keys.set(`${account.toString()}-npk_m`, masterNullifierHidingPublicKey.toBuffer());
372
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.ivpkM.toBuffer());
373
+ await this.#keys.set(`${account.toString()}-ovpk_m`, masterOutgoingViewingPublicKey.toBuffer());
374
+ await this.#keys.set(`${account.toString()}-tpk_m`, masterTaggingPublicKey.toBuffer());
375
+
376
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
377
+ // using the #getKeyPrefixAndAccount function later on
378
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.npkMHash.toBuffer());
379
+ await this.#keys.set(`${account.toString()}-ivpk_m_hash`, masterIncomingViewingPublicKeyHash.toBuffer());
380
+ await this.#keys.set(`${account.toString()}-ovpk_m_hash`, publicKeys.ovpkMHash.toBuffer());
381
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.tpkMHash.toBuffer());
382
+ });
383
+
384
+ return completeAddress;
385
+ }
386
+
387
+ /**
388
+ * Fetches a stored master key buffer for an account by its storage suffix (e.g. `npk_m`, `ivsk_m`).
389
+ * @throws If the account does not exist in the key store.
390
+ */
391
+ async #getMasterKeyBuffer(account: AztecAddress, suffix: string): Promise<Buffer> {
392
+ const [buffer] = await this.#getMasterKeyBuffers(account, [suffix]);
393
+ return buffer;
394
+ }
395
+
396
+ /**
397
+ * Fetches multiple stored master key buffers for an account in a single transaction, returning them in the order of
398
+ * the requested storage suffixes.
399
+ * @throws If any of the keys is missing (i.e. the account does not exist in the key store).
400
+ */
401
+ async #getMasterKeyBuffers(account: AztecAddress, suffixes: string[]): Promise<Buffer[]> {
402
+ const buffers = await this.#db.transactionAsync(() =>
403
+ Promise.all(suffixes.map(suffix => this.#keys.getAsync(`${account.toString()}-${suffix}`))),
404
+ );
405
+ if (!buffers.every((buffer): buffer is Buffer => buffer !== undefined)) {
406
+ throw new Error(
407
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
408
+ );
409
+ }
410
+ return buffers;
411
+ }
351
412
  }