feat: improve cred managment (#95)

This commit is contained in:
Sasha 2024-02-13 22:19:35 +01:00 committed by GitHub
parent 77ba0a6d5d
commit ac12f6800a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 72 additions and 21 deletions

View File

@ -19,7 +19,7 @@ import type {
DecryptedCredentials, DecryptedCredentials,
EncryptedCredentials, EncryptedCredentials,
} from "./keystore/index.js"; } from "./keystore/index.js";
import { Password } from "./keystore/types.js"; import { KeystoreEntity, Password } from "./keystore/types.js";
import { extractMetaMaskSigner } from "./metamask.js"; import { extractMetaMaskSigner } from "./metamask.js";
import verificationKey from "./resources/verification_key.js"; import verificationKey from "./resources/verification_key.js";
import { RLNContract } from "./rln_contract.js"; import { RLNContract } from "./rln_contract.js";
@ -196,6 +196,10 @@ type RegisterMembershipOptions =
| { signature: string } | { signature: string }
| { identity: IdentityCredential }; | { identity: IdentityCredential };
type WakuRLNEncoderOptions = WakuEncoderOptions & {
credentials: EncryptedCredentials | DecryptedCredentials;
};
export class RLNInstance { export class RLNInstance {
private started = false; private started = false;
private starting = false; private starting = false;
@ -227,10 +231,18 @@ export class RLNInstance {
this.starting = true; this.starting = true;
try { try {
const { credentials, keystore } =
await RLNInstance.decryptCredentialsIfNeeded(options.credentials);
const { signer, registryAddress } = await this.determineStartOptions( const { signer, registryAddress } = await this.determineStartOptions(
options options,
credentials
); );
if (keystore) {
this.keystore = keystore;
}
this._credentials = credentials;
this._signer = signer!; this._signer = signer!;
this._contract = await RLNContract.init(this, { this._contract = await RLNContract.init(this, {
registryAddress: registryAddress!, registryAddress: registryAddress!,
@ -243,12 +255,9 @@ export class RLNInstance {
} }
private async determineStartOptions( private async determineStartOptions(
options: StartRLNOptions options: StartRLNOptions,
credentials: KeystoreEntity | undefined
): Promise<StartRLNOptions> { ): Promise<StartRLNOptions> {
const credentials = await this.decryptCredentialsIfNeeded(
options.credentials
);
let chainId = credentials?.membership.chainId; let chainId = credentials?.membership.chainId;
const registryAddress = const registryAddress =
credentials?.membership.address || credentials?.membership.address ||
@ -271,35 +280,35 @@ export class RLNInstance {
return { return {
signer, signer,
registryAddress, registryAddress,
credentials: options.credentials,
}; };
} }
private async decryptCredentialsIfNeeded( private static async decryptCredentialsIfNeeded(
credentials?: EncryptedCredentials | DecryptedCredentials credentials?: EncryptedCredentials | DecryptedCredentials
): Promise<undefined | DecryptedCredentials> { ): Promise<{ credentials?: DecryptedCredentials; keystore?: Keystore }> {
if (!credentials) { if (!credentials) {
return; return {};
} }
if ("identity" in credentials) { if ("identity" in credentials) {
this._credentials = credentials; return { credentials };
return credentials;
} }
const keystore = Keystore.fromString(credentials.keystore); const keystore = Keystore.fromString(credentials.keystore);
if (!keystore) { if (!keystore) {
throw Error("Failed to start RLN: cannot read Keystore provided."); return {};
} }
this.keystore = keystore; const decryptedCredentials = await keystore.readCredential(
this._credentials = await keystore.readCredential(
credentials.id, credentials.id,
credentials.password credentials.password
); );
return this._credentials; return {
keystore,
credentials: decryptedCredentials,
};
} }
public async registerMembership( public async registerMembership(
@ -331,21 +340,56 @@ export class RLNInstance {
this._credentials = await this.keystore?.readCredential(id, password); this._credentials = await this.keystore?.readCredential(id, password);
} }
public createEncoder(options: WakuEncoderOptions): RLNEncoder { public async createEncoder(
if (!this._credentials) { options: WakuRLNEncoderOptions
): Promise<RLNEncoder> {
const { credentials: decryptedCredentials } =
await RLNInstance.decryptCredentialsIfNeeded(options.credentials);
const credentials = decryptedCredentials || this._credentials;
if (!credentials) {
throw Error( throw Error(
"Failed to create Encoder: missing RLN credentials. Use createRLNEncoder directly." "Failed to create Encoder: missing RLN credentials. Use createRLNEncoder directly."
); );
} }
await this.verifyCredentialsAgainstContract(credentials);
return createRLNEncoder({ return createRLNEncoder({
encoder: createEncoder(options), encoder: createEncoder(options),
rlnInstance: this, rlnInstance: this,
index: this._credentials.membership.treeIndex, index: credentials.membership.treeIndex,
credential: this._credentials.identity, credential: credentials.identity,
}); });
} }
private async verifyCredentialsAgainstContract(
credentials: KeystoreEntity
): Promise<void> {
if (!this._contract) {
throw Error(
"Failed to verify chain coordinates: no contract initialized."
);
}
const registryAddress = credentials.membership.address;
const currentRegistryAddress = this._contract.registry.address;
if (registryAddress !== currentRegistryAddress) {
throw Error(
`Failed to verify chain coordinates: credentials contract address=${registryAddress} is not equal to registryContract address=${currentRegistryAddress}`
);
}
const chainId = credentials.membership.chainId;
const network = await this._contract.registry.getNetwork();
const currentChainId = network.chainId;
if (chainId !== currentChainId) {
throw Error(
`Failed to verify chain coordinates: credentials chainID=${chainId} is not equal to registryContract chainID=${currentChainId}`
);
}
}
public createDecoder( public createDecoder(
contentTopic: ContentTopic contentTopic: ContentTopic
): RLNDecoder<IDecodedMessage> { ): RLNDecoder<IDecodedMessage> {

View File

@ -93,6 +93,13 @@ export class RLNContract {
this.deployBlock = await this.storageContract.deployedBlockNumber(); this.deployBlock = await this.storageContract.deployedBlockNumber();
} }
public get registry(): ethers.Contract {
if (!this.registryContract) {
throw Error("Registry contract was not initialized");
}
return this.registryContract as ethers.Contract;
}
public get contract(): ethers.Contract { public get contract(): ethers.Contract {
if (!this.storageContract) { if (!this.storageContract) {
throw Error("Storage contract was not initialized"); throw Error("Storage contract was not initialized");