@aztec/key-store 0.40.0 → 0.41.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,403 @@
1
+ import { type PublicKey } from '@aztec/circuit-types';
2
+ import {
3
+ AztecAddress,
4
+ CompleteAddress,
5
+ Fq,
6
+ Fr,
7
+ GeneratorIndex,
8
+ type GrumpkinPrivateKey,
9
+ GrumpkinScalar,
10
+ KEY_PREFIXES,
11
+ type KeyGenerator,
12
+ type KeyPrefix,
13
+ KeyValidationRequest,
14
+ type PartialAddress,
15
+ Point,
16
+ computeAddress,
17
+ computeAppSecretKey,
18
+ deriveKeys,
19
+ derivePublicKeyFromSecretKey,
20
+ getKeyGenerator,
21
+ } from '@aztec/circuits.js';
22
+ import { poseidon2Hash } from '@aztec/foundation/crypto';
23
+ import { type Bufferable, serializeToBuffer } from '@aztec/foundation/serialize';
24
+ import { type AztecKVStore, type AztecMap } from '@aztec/kv-store';
25
+
26
+ /**
27
+ * Used for managing keys. Can hold keys of multiple accounts and allows for key rotation.
28
+ */
29
+ export class KeyStore {
30
+ #keys: AztecMap<string, Buffer>;
31
+
32
+ constructor(database: AztecKVStore) {
33
+ this.#keys = database.openMap('key_store');
34
+ }
35
+
36
+ /**
37
+ * Creates a new account from a randomly generated secret key.
38
+ * @returns A promise that resolves to the newly created account's CompleteAddress.
39
+ */
40
+ public createAccount(): Promise<CompleteAddress> {
41
+ const sk = Fr.random();
42
+ const partialAddress = Fr.random();
43
+ return this.addAccount(sk, partialAddress);
44
+ }
45
+
46
+ /**
47
+ * Adds an account to the key store from the provided secret key.
48
+ * @param sk - The secret key of the account.
49
+ * @param partialAddress - The partial address of the account.
50
+ * @returns The account's complete address.
51
+ */
52
+ public async addAccount(sk: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
53
+ const {
54
+ masterNullifierSecretKey,
55
+ masterIncomingViewingSecretKey,
56
+ masterOutgoingViewingSecretKey,
57
+ masterTaggingSecretKey,
58
+ publicKeys,
59
+ } = deriveKeys(sk);
60
+
61
+ const publicKeysHash = publicKeys.hash();
62
+ const account = computeAddress(publicKeysHash, partialAddress);
63
+
64
+ // Naming of keys is as follows ${account}-${n/iv/ov/t}${sk/pk}_m
65
+ await this.#keys.set(`${account.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
66
+ await this.#keys.set(`${account.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
67
+ await this.#keys.set(`${account.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
68
+ await this.#keys.set(`${account.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
69
+
70
+ await this.#keys.set(`${account.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
71
+ await this.#keys.set(`${account.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
72
+ await this.#keys.set(`${account.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
73
+ await this.#keys.set(`${account.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
74
+
75
+ // We store pk_m_hash under `account-{n/iv/ov/t}pk_m_hash` key to be able to obtain address and key prefix
76
+ // using the #getKeyPrefixAndAccount function later on
77
+ await this.#keys.set(`${account.toString()}-npk_m_hash`, publicKeys.masterNullifierPublicKey.hash().toBuffer());
78
+ await this.#keys.set(
79
+ `${account.toString()}-ivpk_m_hash`,
80
+ publicKeys.masterIncomingViewingPublicKey.hash().toBuffer(),
81
+ );
82
+ await this.#keys.set(
83
+ `${account.toString()}-ovpk_m_hash`,
84
+ publicKeys.masterOutgoingViewingPublicKey.hash().toBuffer(),
85
+ );
86
+ await this.#keys.set(`${account.toString()}-tpk_m_hash`, publicKeys.masterTaggingPublicKey.hash().toBuffer());
87
+
88
+ // At last, we return the newly derived account address
89
+ return Promise.resolve(new CompleteAddress(account, publicKeys, partialAddress));
90
+ }
91
+
92
+ /**
93
+ * Retrieves addresses of accounts stored in the key store.
94
+ * @returns A Promise that resolves to an array of account addresses.
95
+ */
96
+ public getAccounts(): Promise<AztecAddress[]> {
97
+ const allMapKeys = Array.from(this.#keys.keys());
98
+ // We return account addresses based on the map keys that end with '-ivsk_m'
99
+ const accounts = allMapKeys.filter(key => key.endsWith('-ivsk_m')).map(key => key.split('-')[0]);
100
+ return Promise.resolve(accounts.map(account => AztecAddress.fromString(account)));
101
+ }
102
+
103
+ /**
104
+ * Gets the key validation request for a given master public key hash and contract address.
105
+ * @throws If the account corresponding to the master public key hash does not exist in the key store.
106
+ * @param pkMHash - The master public key hash.
107
+ * @param contractAddress - The contract address to silo the secret key in the the key validation request with.
108
+ * @returns The key validation request.
109
+ */
110
+ public getKeyValidationRequest(pkMHash: Fr, contractAddress: AztecAddress): Promise<KeyValidationRequest> {
111
+ const [keyPrefix, account] = this.#getKeyPrefixAndAccount(pkMHash);
112
+
113
+ // Now we find the master public key for the account
114
+ // Since each public keys buffer contains multiple public keys, we need to find the one that matches the hash.
115
+ // Then we store the index of the key in the buffer to be able to quickly obtain the corresponding secret key.
116
+ let pkM: PublicKey | undefined;
117
+ let keyIndexInBuffer = 0;
118
+ {
119
+ const pkMsBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}pk_m`);
120
+ if (!pkMsBuffer) {
121
+ throw new Error(
122
+ `Could not find ${keyPrefix}pk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
123
+ );
124
+ }
125
+
126
+ // Now we iterate over the public keys in the buffer to find the one that matches the hash
127
+ const numKeys = this.#calculateNumKeys(pkMsBuffer, Point);
128
+ for (; keyIndexInBuffer < numKeys; keyIndexInBuffer++) {
129
+ const foundPkM = Point.fromBuffer(
130
+ pkMsBuffer.subarray(keyIndexInBuffer * Point.SIZE_IN_BYTES, (keyIndexInBuffer + 1) * Point.SIZE_IN_BYTES),
131
+ );
132
+ if (foundPkM.hash().equals(pkMHash)) {
133
+ pkM = foundPkM;
134
+ break;
135
+ }
136
+ }
137
+
138
+ if (!pkM) {
139
+ throw new Error(`Could not find ${keyPrefix}pkM for ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`);
140
+ }
141
+ }
142
+
143
+ // Now we find the secret key for the public key
144
+ let skM: GrumpkinPrivateKey | undefined;
145
+ {
146
+ const skMsBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}sk_m`);
147
+ if (!skMsBuffer) {
148
+ throw new Error(
149
+ `Could not find ${keyPrefix}sk_m for account ${account.toString()} whose address was successfully obtained with ${keyPrefix}pk_m_hash ${pkMHash.toString()}.`,
150
+ );
151
+ }
152
+
153
+ skM = GrumpkinScalar.fromBuffer(
154
+ skMsBuffer.subarray(
155
+ keyIndexInBuffer * GrumpkinScalar.SIZE_IN_BYTES,
156
+ (keyIndexInBuffer + 1) * GrumpkinScalar.SIZE_IN_BYTES,
157
+ ),
158
+ );
159
+ }
160
+
161
+ // We sanity check that it's possible to derive the public key from the secret key
162
+ if (!derivePublicKeyFromSecretKey(skM).equals(pkM)) {
163
+ throw new Error(`Could not derive ${keyPrefix}pkM from ${keyPrefix}skM.`);
164
+ }
165
+
166
+ // At last we silo the secret key and return the key validation request
167
+ const skApp = computeAppSecretKey(skM, contractAddress, keyPrefix!);
168
+
169
+ return Promise.resolve(new KeyValidationRequest(pkM, skApp));
170
+ }
171
+
172
+ /**
173
+ * Gets the master incoming viewing public key for a given account.
174
+ * @throws If the account does not exist in the key store.
175
+ * @param account - The account address for which to retrieve the master incoming viewing public key.
176
+ * @returns The master incoming viewing public key for the account.
177
+ */
178
+ public async getMasterIncomingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
179
+ const masterIncomingViewingPublicKeyBuffer = this.#keys.get(`${account.toString()}-ivpk_m`);
180
+ if (!masterIncomingViewingPublicKeyBuffer) {
181
+ throw new Error(
182
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
183
+ );
184
+ }
185
+ return Promise.resolve(Point.fromBuffer(masterIncomingViewingPublicKeyBuffer));
186
+ }
187
+
188
+ /**
189
+ * Retrieves the master outgoing viewing public key.
190
+ * @throws If the account does not exist in the key store.
191
+ * @param account - The account to retrieve the master outgoing viewing key for.
192
+ * @returns A Promise that resolves to the master outgoing viewing key.
193
+ */
194
+ public async getMasterOutgoingViewingPublicKey(account: AztecAddress): Promise<PublicKey> {
195
+ const masterOutgoingViewingPublicKeyBuffer = this.#keys.get(`${account.toString()}-ovpk_m`);
196
+ if (!masterOutgoingViewingPublicKeyBuffer) {
197
+ throw new Error(
198
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
199
+ );
200
+ }
201
+ return Promise.resolve(Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer));
202
+ }
203
+
204
+ /**
205
+ * Retrieves the master tagging public key.
206
+ * @throws If the account does not exist in the key store.
207
+ * @param account - The account to retrieve the master tagging key for.
208
+ * @returns A Promise that resolves to the master tagging key.
209
+ */
210
+ public async getMasterTaggingPublicKey(account: AztecAddress): Promise<PublicKey> {
211
+ const masterTaggingPublicKeyBuffer = this.#keys.get(`${account.toString()}-tpk_m`);
212
+ if (!masterTaggingPublicKeyBuffer) {
213
+ throw new Error(
214
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
215
+ );
216
+ }
217
+ return Promise.resolve(Point.fromBuffer(masterTaggingPublicKeyBuffer));
218
+ }
219
+
220
+ /**
221
+ * Retrieves application incoming viewing secret key.
222
+ * @throws If the account does not exist in the key store.
223
+ * @param account - The account to retrieve the application incoming viewing secret key for.
224
+ * @param app - The application address to retrieve the incoming viewing secret key for.
225
+ * @returns A Promise that resolves to the application incoming viewing secret key.
226
+ */
227
+ public async getAppIncomingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
228
+ const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ivsk_m`);
229
+ if (!masterIncomingViewingSecretKeyBuffer) {
230
+ throw new Error(
231
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
232
+ );
233
+ }
234
+ const masterIncomingViewingSecretKey = GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
235
+
236
+ return Promise.resolve(
237
+ poseidon2Hash([
238
+ masterIncomingViewingSecretKey.high,
239
+ masterIncomingViewingSecretKey.low,
240
+ app,
241
+ GeneratorIndex.IVSK_M,
242
+ ]),
243
+ );
244
+ }
245
+
246
+ /**
247
+ * Retrieves application outgoing viewing secret key.
248
+ * @throws If the account does not exist in the key store.
249
+ * @param account - The account to retrieve the application outgoing viewing secret key for.
250
+ * @param app - The application address to retrieve the outgoing viewing secret key for.
251
+ * @returns A Promise that resolves to the application outgoing viewing secret key.
252
+ */
253
+ public async getAppOutgoingViewingSecretKey(account: AztecAddress, app: AztecAddress): Promise<Fr> {
254
+ const masterOutgoingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ovsk_m`);
255
+ if (!masterOutgoingViewingSecretKeyBuffer) {
256
+ throw new Error(
257
+ `Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`,
258
+ );
259
+ }
260
+ const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
261
+
262
+ return Promise.resolve(
263
+ poseidon2Hash([
264
+ masterOutgoingViewingSecretKey.high,
265
+ masterOutgoingViewingSecretKey.low,
266
+ app,
267
+ GeneratorIndex.OVSK_M,
268
+ ]),
269
+ );
270
+ }
271
+
272
+ /**
273
+ * Retrieves the sk_m for the pk_m and a generator index of the key type.
274
+ * @throws If the provided public key is not associated with any of the registered accounts.
275
+ * @param pkM - The master public key to get secret key for.
276
+ * @returns A Promise that resolves to sk_m.
277
+ * @dev Used when feeding the sk_m to the kernel circuit for keys verification.
278
+ */
279
+ public getMasterSecretKeyAndAppKeyGenerator(pkM: PublicKey): Promise<[GrumpkinPrivateKey, KeyGenerator]> {
280
+ const [keyPrefix, account] = this.#getKeyPrefixAndAccount(pkM);
281
+
282
+ // We get the secret keys buffer and iterate over the values in the buffer to find the one that matches pkM
283
+ let sk: GrumpkinScalar | undefined;
284
+ {
285
+ const secretKeysBuffer = this.#keys.get(`${account.toString()}-${keyPrefix}sk_m`);
286
+ if (!secretKeysBuffer) {
287
+ throw new Error(
288
+ `Could not find ${keyPrefix}sk_m for ${keyPrefix}pk_m ${pkM.toString()}. This should not happen.`,
289
+ );
290
+ }
291
+
292
+ const numKeys = this.#calculateNumKeys(secretKeysBuffer, GrumpkinScalar);
293
+ for (let i = 0; i < numKeys; i++) {
294
+ const foundSk = GrumpkinScalar.fromBuffer(
295
+ secretKeysBuffer.subarray(i * GrumpkinScalar.SIZE_IN_BYTES, (i + 1) * GrumpkinScalar.SIZE_IN_BYTES),
296
+ );
297
+ if (derivePublicKeyFromSecretKey(foundSk).equals(pkM)) {
298
+ sk = foundSk;
299
+ break;
300
+ }
301
+ }
302
+
303
+ if (!sk) {
304
+ throw new Error(`Could not find ${keyPrefix}skM for ${keyPrefix}pkM ${pkM.toString()} in secret keys buffer.`);
305
+ }
306
+ }
307
+
308
+ // Now we determine the key type and return generator accordingly
309
+ const generator = getKeyGenerator(keyPrefix);
310
+ return Promise.resolve([sk, generator]);
311
+ }
312
+
313
+ /**
314
+ * Retrieves the master incoming viewing secret key (ivsk_m) corresponding to the specified master incoming viewing
315
+ * public key (Ivpk_m).
316
+ * @throws If the provided public key is not associated with any of the registered accounts.
317
+ * @param masterIncomingViewingPublicKey - The master nullifier public key to get secret key for.
318
+ * @returns A Promise that resolves to the master nullifier secret key.
319
+ * @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
320
+ */
321
+ public getMasterIncomingViewingSecretKeyForPublicKey(
322
+ masterIncomingViewingPublicKey: PublicKey,
323
+ ): Promise<GrumpkinPrivateKey> {
324
+ // We iterate over the map keys to find the account address that corresponds to the provided public key
325
+ for (const [key, value] of this.#keys.entries()) {
326
+ if (value.equals(masterIncomingViewingPublicKey.toBuffer())) {
327
+ // We extract the account address from the map key
328
+ const account = key.split('-')[0];
329
+ // We fetch the secret key and return it
330
+ const masterIncomingViewingSecretKeyBuffer = this.#keys.get(`${account.toString()}-ivsk_m`);
331
+ if (!masterIncomingViewingSecretKeyBuffer) {
332
+ throw new Error(`Could not find master incoming viewing secret key for account ${account.toString()}`);
333
+ }
334
+ return Promise.resolve(GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer));
335
+ }
336
+ }
337
+
338
+ throw new Error(
339
+ `Could not find master incoming viewing secret key for public key ${masterIncomingViewingPublicKey.toString()}`,
340
+ );
341
+ }
342
+
343
+ /**
344
+ * Rotates the master nullifier key for the specified account.
345
+ *
346
+ * @dev This function updates the secret and public keys associated with the account.
347
+ * It appends a new secret key to the existing secret keys, derives the
348
+ * corresponding public key, and updates the stored keys accordingly.
349
+ *
350
+ * @param account - The account address for which the master nullifier key is being rotated.
351
+ * @param newSecretKey - (Optional) A new secret key of type Fq. If not provided, a random key is generated.
352
+ * @throws If the account does not have existing nullifier secret keys or public keys.
353
+ * @returns A Promise that resolves when the key rotation is complete.
354
+ */
355
+ public async rotateMasterNullifierKey(account: AztecAddress, newSecretKey: Fq = Fq.random()) {
356
+ // We append the secret key to the array of secret keys
357
+ await this.#appendValue(`${account.toString()}-nsk_m`, newSecretKey);
358
+
359
+ // Now we derive the public key from the new secret key and append it to the buffer of original public keys
360
+ const newPublicKey = derivePublicKeyFromSecretKey(newSecretKey);
361
+ await this.#appendValue(`${account.toString()}-npk_m`, newPublicKey);
362
+
363
+ // At last we store npk_m_hash under `account-npk_m_hash` key to be able to obtain address and key prefix
364
+ // using the #getKeyPrefixAndAccount function later on
365
+ await this.#appendValue(`${account.toString()}-npk_m_hash`, newPublicKey.hash());
366
+ }
367
+
368
+ /**
369
+ * Gets the key prefix and account address for a given value.
370
+ * @returns A tuple containing the key prefix and account address.
371
+ * @dev Note that this is quite inefficient but it should not matter because there should never be too many keys
372
+ * in the key store.
373
+ */
374
+ #getKeyPrefixAndAccount(value: Bufferable): [KeyPrefix, AztecAddress] {
375
+ const valueBuffer = serializeToBuffer(value);
376
+ for (const [key, val] of this.#keys.entries()) {
377
+ // `val` can contain multiple values due to key rotation so we check if the value is in the buffer instead
378
+ // of just calling `.equals(...)`
379
+ if (val.includes(valueBuffer)) {
380
+ for (const prefix of KEY_PREFIXES) {
381
+ if (key.includes(`-${prefix}`)) {
382
+ const account = AztecAddress.fromString(key.split('-')[0]);
383
+ return [prefix, account];
384
+ }
385
+ }
386
+ }
387
+ }
388
+ throw new Error(`Could not find key prefix.`);
389
+ }
390
+
391
+ async #appendValue(key: string, value: Bufferable) {
392
+ const currentValue = this.#keys.get(key);
393
+ if (!currentValue) {
394
+ throw new Error(`Could not find current value for key ${key}`);
395
+ }
396
+
397
+ await this.#keys.set(key, serializeToBuffer([currentValue, value]));
398
+ }
399
+
400
+ #calculateNumKeys(buf: Buffer, T: typeof Point | typeof Fq) {
401
+ return buf.byteLength / T.SIZE_IN_BYTES;
402
+ }
403
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"test_key_store.d.ts","sourceRoot":"","sources":["../src/test_key_store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,eAAe,EACf,EAAE,EAEF,KAAK,kBAAkB,EAEvB,KAAK,cAAc,EAKpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,KAAK,YAAY,EAAiB,MAAM,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,qBAAa,YAAa,YAAW,QAAQ;;gBAG/B,QAAQ,EAAE,YAAY;IAIlC;;;OAGG;IACI,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC;IAMhD;;;;;OAKG;IACU,UAAU,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IA6BzF;;;OAGG;IACI,WAAW,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAO7C;;;;;OAKG;IACU,2BAA2B,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUnF;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzF;;;;;OAKG;IACU,iCAAiC,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzF;;;;;OAKG;IACU,yBAAyB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC;IAUjF;;;;;;OAMG;IACU,wBAAwB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC;IAY5F;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC;IAmBlG;;;;;;OAMG;IACU,8BAA8B,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC;IAmBlG;;;;;;;OAOG;IACI,uCAAuC,CAAC,wBAAwB,EAAE,SAAS,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAkBhH;;;;;;;OAOG;IACI,6CAA6C,CAClD,8BAA8B,EAAE,SAAS,GACxC,OAAO,CAAC,kBAAkB,CAAC;IAoB9B;;;;;OAKG;IACU,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC;CASnE"}
@@ -1,227 +0,0 @@
1
- var _TestKeyStore_keys;
2
- import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
3
- import { AztecAddress, CompleteAddress, Fr, GeneratorIndex, GrumpkinScalar, Point, computeAddress, computeAppNullifierSecretKey, deriveKeys, } from '@aztec/circuits.js';
4
- import { poseidon2Hash } from '@aztec/foundation/crypto';
5
- /**
6
- * TestKeyStore is an implementation of the KeyStore interface, used for managing key pairs in a testing environment.
7
- * It should be utilized in testing scenarios where secure key management is not required, and ease-of-use is prioritized.
8
- */
9
- export class TestKeyStore {
10
- constructor(database) {
11
- _TestKeyStore_keys.set(this, void 0);
12
- __classPrivateFieldSet(this, _TestKeyStore_keys, database.openMap('key_store'), "f");
13
- }
14
- /**
15
- * Creates a new account from a randomly generated secret key.
16
- * @returns A promise that resolves to the newly created account's CompleteAddress.
17
- */
18
- createAccount() {
19
- const sk = Fr.random();
20
- const partialAddress = Fr.random();
21
- return this.addAccount(sk, partialAddress);
22
- }
23
- /**
24
- * Adds an account to the key store from the provided secret key.
25
- * @param sk - The secret key of the account.
26
- * @param partialAddress - The partial address of the account.
27
- * @returns The account's complete address.
28
- */
29
- async addAccount(sk, partialAddress) {
30
- const { masterNullifierSecretKey, masterIncomingViewingSecretKey, masterOutgoingViewingSecretKey, masterTaggingSecretKey, publicKeys, } = deriveKeys(sk);
31
- const publicKeysHash = publicKeys.hash();
32
- const accountAddress = computeAddress(publicKeysHash, partialAddress);
33
- // We save the keys to db
34
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-public_keys_hash`, publicKeysHash.toBuffer());
35
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-nsk_m`, masterNullifierSecretKey.toBuffer());
36
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-ivsk_m`, masterIncomingViewingSecretKey.toBuffer());
37
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-ovsk_m`, masterOutgoingViewingSecretKey.toBuffer());
38
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-tsk_m`, masterTaggingSecretKey.toBuffer());
39
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-npk_m`, publicKeys.masterNullifierPublicKey.toBuffer());
40
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-ivpk_m`, publicKeys.masterIncomingViewingPublicKey.toBuffer());
41
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-ovpk_m`, publicKeys.masterOutgoingViewingPublicKey.toBuffer());
42
- await __classPrivateFieldGet(this, _TestKeyStore_keys, "f").set(`${accountAddress.toString()}-tpk_m`, publicKeys.masterTaggingPublicKey.toBuffer());
43
- // At last, we return the newly derived account address
44
- return Promise.resolve(new CompleteAddress(accountAddress, publicKeys, partialAddress));
45
- }
46
- /**
47
- * Retrieves addresses of accounts stored in the key store.
48
- * @returns A Promise that resolves to an array of account addresses.
49
- */
50
- getAccounts() {
51
- const allMapKeys = Array.from(__classPrivateFieldGet(this, _TestKeyStore_keys, "f").keys());
52
- // We return account addresses based on the map keys that end with '-nsk_m'
53
- const accounts = allMapKeys.filter(key => key.endsWith('-nsk_m')).map(key => key.split('-')[0]);
54
- return Promise.resolve(accounts.map(account => AztecAddress.fromString(account)));
55
- }
56
- /**
57
- * Gets the master nullifier public key for a given account.
58
- * @throws If the account does not exist in the key store.
59
- * @param account - The account address for which to retrieve the master nullifier public key.
60
- * @returns The master nullifier public key for the account.
61
- */
62
- async getMasterNullifierPublicKey(account) {
63
- const masterNullifierPublicKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-npk_m`);
64
- if (!masterNullifierPublicKeyBuffer) {
65
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
66
- }
67
- return Promise.resolve(Point.fromBuffer(masterNullifierPublicKeyBuffer));
68
- }
69
- /**
70
- * Gets the master incoming viewing public key for a given account.
71
- * @throws If the account does not exist in the key store.
72
- * @param account - The account address for which to retrieve the master incoming viewing public key.
73
- * @returns The master incoming viewing public key for the account.
74
- */
75
- async getMasterIncomingViewingPublicKey(account) {
76
- const masterIncomingViewingPublicKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-ivpk_m`);
77
- if (!masterIncomingViewingPublicKeyBuffer) {
78
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
79
- }
80
- return Promise.resolve(Point.fromBuffer(masterIncomingViewingPublicKeyBuffer));
81
- }
82
- /**
83
- * Retrieves the master outgoing viewing public key.
84
- * @throws If the account does not exist in the key store.
85
- * @param account - The account to retrieve the master outgoing viewing key for.
86
- * @returns A Promise that resolves to the master outgoing viewing key.
87
- */
88
- async getMasterOutgoingViewingPublicKey(account) {
89
- const masterOutgoingViewingPublicKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-ovpk_m`);
90
- if (!masterOutgoingViewingPublicKeyBuffer) {
91
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
92
- }
93
- return Promise.resolve(Point.fromBuffer(masterOutgoingViewingPublicKeyBuffer));
94
- }
95
- /**
96
- * Retrieves the master tagging public key.
97
- * @throws If the account does not exist in the key store.
98
- * @param account - The account to retrieve the master tagging key for.
99
- * @returns A Promise that resolves to the master tagging key.
100
- */
101
- async getMasterTaggingPublicKey(account) {
102
- const masterTaggingPublicKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-tpk_m`);
103
- if (!masterTaggingPublicKeyBuffer) {
104
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
105
- }
106
- return Promise.resolve(Point.fromBuffer(masterTaggingPublicKeyBuffer));
107
- }
108
- /**
109
- * Retrieves application nullifier secret key.
110
- * @throws If the account does not exist in the key store.
111
- * @param account - The account to retrieve the application nullifier secret key for.
112
- * @param app - The application address to retrieve the nullifier secret key for.
113
- * @returns A Promise that resolves to the application nullifier secret key.
114
- */
115
- async getAppNullifierSecretKey(account, app) {
116
- const masterNullifierSecretKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-nsk_m`);
117
- if (!masterNullifierSecretKeyBuffer) {
118
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
119
- }
120
- const masterNullifierSecretKey = GrumpkinScalar.fromBuffer(masterNullifierSecretKeyBuffer);
121
- const appNullifierSecretKey = computeAppNullifierSecretKey(masterNullifierSecretKey, app);
122
- return Promise.resolve(appNullifierSecretKey);
123
- }
124
- /**
125
- * Retrieves application incoming viewing secret key.
126
- * @throws If the account does not exist in the key store.
127
- * @param account - The account to retrieve the application incoming viewing secret key for.
128
- * @param app - The application address to retrieve the incoming viewing secret key for.
129
- * @returns A Promise that resolves to the application incoming viewing secret key.
130
- */
131
- async getAppIncomingViewingSecretKey(account, app) {
132
- const masterIncomingViewingSecretKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-ivsk_m`);
133
- if (!masterIncomingViewingSecretKeyBuffer) {
134
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
135
- }
136
- const masterIncomingViewingSecretKey = GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer);
137
- return Promise.resolve(poseidon2Hash([
138
- masterIncomingViewingSecretKey.high,
139
- masterIncomingViewingSecretKey.low,
140
- app,
141
- GeneratorIndex.IVSK_M,
142
- ]));
143
- }
144
- /**
145
- * Retrieves application outgoing viewing secret key.
146
- * @throws If the account does not exist in the key store.
147
- * @param account - The account to retrieve the application outgoing viewing secret key for.
148
- * @param app - The application address to retrieve the outgoing viewing secret key for.
149
- * @returns A Promise that resolves to the application outgoing viewing secret key.
150
- */
151
- async getAppOutgoingViewingSecretKey(account, app) {
152
- const masterOutgoingViewingSecretKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-ovsk_m`);
153
- if (!masterOutgoingViewingSecretKeyBuffer) {
154
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
155
- }
156
- const masterOutgoingViewingSecretKey = GrumpkinScalar.fromBuffer(masterOutgoingViewingSecretKeyBuffer);
157
- return Promise.resolve(poseidon2Hash([
158
- masterOutgoingViewingSecretKey.high,
159
- masterOutgoingViewingSecretKey.low,
160
- app,
161
- GeneratorIndex.OVSK_M,
162
- ]));
163
- }
164
- /**
165
- * Retrieves the master nullifier secret key (nsk_m) corresponding to the specified master nullifier public key
166
- * (Npk_m).
167
- * @throws If the provided public key is not associated with any of the registered accounts.
168
- * @param masterNullifierPublicKey - The master nullifier public key to get secret key for.
169
- * @returns A Promise that resolves to the master nullifier secret key.
170
- * @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
171
- */
172
- getMasterNullifierSecretKeyForPublicKey(masterNullifierPublicKey) {
173
- // We iterate over the map keys to find the account address that corresponds to the provided public key
174
- for (const [key, value] of __classPrivateFieldGet(this, _TestKeyStore_keys, "f").entries()) {
175
- if (value.equals(masterNullifierPublicKey.toBuffer())) {
176
- // We extract the account address from the map key
177
- const accountAddress = key.split('-')[0];
178
- // We fetch the secret key and return it
179
- const masterNullifierSecretKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${accountAddress.toString()}-nsk_m`);
180
- if (!masterNullifierSecretKeyBuffer) {
181
- throw new Error(`Could not find master nullifier secret key for account ${accountAddress.toString()}`);
182
- }
183
- return Promise.resolve(GrumpkinScalar.fromBuffer(masterNullifierSecretKeyBuffer));
184
- }
185
- }
186
- throw new Error(`Could not find master nullifier secret key for public key ${masterNullifierPublicKey.toString()}`);
187
- }
188
- /**
189
- * Retrieves the master incoming viewing secret key (ivsk_m) corresponding to the specified master incoming viewing
190
- * public key (Ivpk_m).
191
- * @throws If the provided public key is not associated with any of the registered accounts.
192
- * @param masterIncomingViewingPublicKey - The master nullifier public key to get secret key for.
193
- * @returns A Promise that resolves to the master nullifier secret key.
194
- * @dev Used when feeding the master nullifier secret key to the kernel circuit for nullifier keys verification.
195
- */
196
- getMasterIncomingViewingSecretKeyForPublicKey(masterIncomingViewingPublicKey) {
197
- // We iterate over the map keys to find the account address that corresponds to the provided public key
198
- for (const [key, value] of __classPrivateFieldGet(this, _TestKeyStore_keys, "f").entries()) {
199
- if (value.equals(masterIncomingViewingPublicKey.toBuffer())) {
200
- // We extract the account address from the map key
201
- const accountAddress = key.split('-')[0];
202
- // We fetch the secret key and return it
203
- const masterIncomingViewingSecretKeyBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${accountAddress.toString()}-ivsk_m`);
204
- if (!masterIncomingViewingSecretKeyBuffer) {
205
- throw new Error(`Could not find master incoming viewing secret key for account ${accountAddress.toString()}`);
206
- }
207
- return Promise.resolve(GrumpkinScalar.fromBuffer(masterIncomingViewingSecretKeyBuffer));
208
- }
209
- }
210
- throw new Error(`Could not find master incoming viewing secret key for public key ${masterIncomingViewingPublicKey.toString()}`);
211
- }
212
- /**
213
- * Retrieves public keys hash of the account
214
- * @throws If the provided account address is not associated with any of the registered accounts.
215
- * @param account - The account address to get public keys hash for.
216
- * @returns A Promise that resolves to the public keys hash.
217
- */
218
- async getPublicKeysHash(account) {
219
- const publicKeysHashBuffer = __classPrivateFieldGet(this, _TestKeyStore_keys, "f").get(`${account.toString()}-public_keys_hash`);
220
- if (!publicKeysHashBuffer) {
221
- throw new Error(`Account ${account.toString()} does not exist. Registered accounts: ${await this.getAccounts()}.`);
222
- }
223
- return Promise.resolve(Fr.fromBuffer(publicKeysHashBuffer));
224
- }
225
- }
226
- _TestKeyStore_keys = new WeakMap();
227
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGVzdF9rZXlfc3RvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvdGVzdF9rZXlfc3RvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFDQSxPQUFPLEVBQ0wsWUFBWSxFQUNaLGVBQWUsRUFDZixFQUFFLEVBQ0YsY0FBYyxFQUVkLGNBQWMsRUFFZCxLQUFLLEVBQ0wsY0FBYyxFQUNkLDRCQUE0QixFQUM1QixVQUFVLEdBQ1gsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFHekQ7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFlBQVk7SUFHdkIsWUFBWSxRQUFzQjtRQUZsQyxxQ0FBZ0M7UUFHOUIsdUJBQUEsSUFBSSxzQkFBUyxRQUFRLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxNQUFBLENBQUM7SUFDN0MsQ0FBQztJQUVEOzs7T0FHRztJQUNJLGFBQWE7UUFDbEIsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sY0FBYyxHQUFHLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNuQyxPQUFPLElBQUksQ0FBQyxVQUFVLENBQUMsRUFBRSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFBTSxFQUFFLGNBQThCO1FBQzVELE1BQU0sRUFDSix3QkFBd0IsRUFDeEIsOEJBQThCLEVBQzlCLDhCQUE4QixFQUM5QixzQkFBc0IsRUFDdEIsVUFBVSxHQUNYLEdBQUcsVUFBVSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBRW5CLE1BQU0sY0FBYyxHQUFHLFVBQVUsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN6QyxNQUFNLGNBQWMsR0FBRyxjQUFjLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxDQUFDO1FBRXRFLHlCQUF5QjtRQUN6QixNQUFNLHVCQUFB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