Skip to content

Commit

Permalink
feat!: add new operations and improve start (#93)
Browse files Browse the repository at this point in the history
* featimprove start, types, import/export, add checks for netwrok, shortcuts for decoder encoder

* fix type issue

* update tests

* provide ability to use Keystore as a seed for credentials

* fix types

* up test

* initialize keystore by default

* add keys operation to Keystore
  • Loading branch information
weboko authored Jan 30, 2024
1 parent bafbe01 commit 77ba0a6
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 67 deletions.
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from "./constants.js";
import { createRLN } from "./create.js";
import { Keystore } from "./keystore/index.js";
import { extractMetaMaskSigner } from "./metamask.js";
import {
IdentityCredential,
Proof,
Expand All @@ -29,4 +30,5 @@ export {
RLN_STORAGE_ABI,
RLN_REGISTRY_ABI,
SEPOLIA_CONTRACT,
extractMetaMaskSigner,
};
2 changes: 2 additions & 0 deletions src/keystore/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Keystore } from "./keystore.js";
import type { DecryptedCredentials, EncryptedCredentials } from "./types.js";

export { Keystore };
export type { EncryptedCredentials, DecryptedCredentials };
6 changes: 3 additions & 3 deletions src/keystore/keystore.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ describe("Keystore", () => {
});

it("should fail to create store from invalid string", () => {
expect(Keystore.fromString("/asdq}")).to.eq(null);
expect(Keystore.fromString('{ "name": "it" }')).to.eq(null);
expect(Keystore.fromString("/asdq}")).to.eq(undefined);
expect(Keystore.fromString('{ "name": "it" }')).to.eq(undefined);
});

it("shoud create store from valid string", async () => {
Expand Down Expand Up @@ -308,6 +308,6 @@ describe("Keystore", () => {
const store = Keystore.fromObject(NWAKU_KEYSTORE as any);

Check warning on line 308 in src/keystore/keystore.spec.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

const result = await store.readCredential("wrong-hash", "wrong-password");
expect(result).to.eq(null);
expect(result).to.eq(undefined);
});
});
33 changes: 19 additions & 14 deletions src/keystore/keystore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import _ from "lodash";
import { v4 as uuidV4 } from "uuid";

import { buildBigIntFromUint8Array } from "../byte_utils.js";
import type { IdentityCredential } from "../rln.js";

import { decryptEipKeystore, keccak256Checksum } from "./cipher.js";
import { isCredentialValid, isKeystoreValid } from "./schema_validator.js";
import type {
Keccak256Hash,
KeystoreEntity,
MembershipHash,
MembershipInfo,
Password,
Expand Down Expand Up @@ -57,11 +57,6 @@ type KeystoreCreateOptions = {
appIdentifier?: string;
};

type IdentityOptions = {
identity: IdentityCredential;
membership: MembershipInfo;
};

export class Keystore {
private data: NwakuKeystore;

Expand All @@ -81,7 +76,9 @@ export class Keystore {
return new Keystore(options);
}

public static fromString(str: string): Keystore | null {
// should be valid JSON string that contains Keystore file
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/keyfile.nim#L376
public static fromString(str: string): undefined | Keystore {
try {
const obj = JSON.parse(str);

Expand All @@ -92,7 +89,7 @@ export class Keystore {
return new Keystore(obj);
} catch (err) {
console.error("Cannot create Keystore from string:", err);
return null;
return;
}
}

Expand All @@ -105,7 +102,7 @@ export class Keystore {
}

public async addCredential(
options: IdentityOptions,
options: KeystoreEntity,
password: Password
): Promise<MembershipHash> {
const membershipHash: MembershipHash = Keystore.computeMembershipHash(
Expand Down Expand Up @@ -138,11 +135,11 @@ export class Keystore {
public async readCredential(
membershipHash: MembershipHash,
password: Password
): Promise<null | IdentityOptions> {
): Promise<undefined | KeystoreEntity> {
const nwakuCredential = this.data.credentials[membershipHash];

if (!nwakuCredential) {
return null;
return;
}

const eipKeystore = Keystore.fromCredentialToEip(nwakuCredential);
Expand All @@ -167,6 +164,14 @@ export class Keystore {
return this.data;
}

/**
* Read array of hashes of current credentials
* @returns array of keys of credentials in current Keystore
*/
public keys(): string[] {
return Object.keys(this.toObject().credentials || {});
}

private static isValidNwakuStore(obj: unknown): boolean {
if (!isKeystoreValid(obj)) {
return false;
Expand Down Expand Up @@ -237,7 +242,7 @@ export class Keystore {

private static fromBytesToIdentity(
bytes: Uint8Array
): null | IdentityOptions {
): undefined | KeystoreEntity {
try {
const str = bytesToUtf8(bytes);
const obj = JSON.parse(str);
Expand Down Expand Up @@ -271,7 +276,7 @@ export class Keystore {
};
} catch (err) {
console.error("Cannot parse bytes to Nwaku Credentials:", err);
return null;
return;
}
}

Expand Down Expand Up @@ -302,7 +307,7 @@ export class Keystore {

// follows nwaku implementation
// https://github.com/waku-org/nwaku/blob/f05528d4be3d3c876a8b07f9bb7dfaae8aa8ec6e/waku/waku_keystore/protocol_types.nim#L98
private static fromIdentityToBytes(options: IdentityOptions): Uint8Array {
private static fromIdentityToBytes(options: KeystoreEntity): Uint8Array {
return utf8ToBytes(
JSON.stringify({
treeIndex: options.membership.treeIndex,
Expand Down
24 changes: 24 additions & 0 deletions src/keystore/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { IdentityCredential } from "../rln.js";

export type MembershipHash = string;
export type Sha256Hash = string;
export type Keccak256Hash = string;
Expand All @@ -10,3 +12,25 @@ export type MembershipInfo = {
address: string;
treeIndex: number;
};

export type KeystoreEntity = {
identity: IdentityCredential;
membership: MembershipInfo;
};

export type DecryptedCredentials = KeystoreEntity;

export type EncryptedCredentials = {
/**
* Valid JSON string that contains Keystore
*/
keystore: string;
/**
* ID of credentials from provided Keystore to use
*/
id: string;
/**
* Password to decrypt credentials provided
*/
password: Password;
};
27 changes: 14 additions & 13 deletions src/metamask.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { ethers } from "ethers";

export const extractMetaMaskAccount =
async (): Promise<ethers.providers.Web3Provider> => {
const ethereum = (window as any).ethereum;

if (!ethereum) {
throw Error(
"Missing or invalid Ethereum provider. Please install a Web3 wallet such as MetaMask."
);
}

await ethereum.request({ method: "eth_requestAccounts" });
return new ethers.providers.Web3Provider(ethereum, "any");
};
export const extractMetaMaskSigner = async (): Promise<ethers.Signer> => {
const ethereum = (window as any).ethereum;

Check warning on line 4 in src/metamask.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

if (!ethereum) {
throw Error(
"Missing or invalid Ethereum provider. Please install a Web3 wallet such as MetaMask."
);
}

await ethereum.request({ method: "eth_requestAccounts" });
const provider = new ethers.providers.Web3Provider(ethereum, "any");

return provider.getSigner();
};
Loading

0 comments on commit 77ba0a6

Please sign in to comment.