diff --git a/src/Reclaim.ts b/src/Reclaim.ts index 984f8d0..f324d52 100644 --- a/src/Reclaim.ts +++ b/src/Reclaim.ts @@ -44,7 +44,7 @@ const sdkVersion = require('../package.json').version; export async function verifyProof(proof: Proof): Promise { if (!proof.signatures.length) { - throw new SignatureNotFoundError('No signatures') + throw new SignatureNotFoundError('No signatures, proof object is undefined') } try { @@ -70,7 +70,7 @@ export async function verifyProof(proof: Proof): Promise { proof.identifier = replaceAll(proof.identifier, '"', '') // check if the identifier matches the one in the proof if (calculatedIdentifier !== proof.identifier) { - throw new ProofNotVerifiedError('Identifier Mismatch') + throw new ProofNotVerifiedError(`Identifier Mismatch, ${calculatedIdentifier} is not as ${proof.identifier}`) } const signedClaim: SignedClaim = { @@ -84,7 +84,7 @@ export async function verifyProof(proof: Proof): Promise { assertValidSignedClaim(signedClaim, witnesses) } catch (e: Error | unknown) { - logger.info(`Error verifying proof: ${e instanceof Error ? e.message : String(e)}`) + logger.error(`Error verifying proof: ${e instanceof Error ? e.message : String(e)}`) return false } @@ -111,6 +111,7 @@ export function transformForOnchain(proof: Proof): { claimInfo: any, signedClaim return { claimInfo, signedClaim }; } + export class ReclaimProofRequest { // Private class properties private applicationId: string; @@ -132,10 +133,10 @@ export class ReclaimProofRequest { this.timeStamp = Date.now().toString(); this.applicationId = applicationId; this.sessionId = ""; - if (options?.log) { - loggerModule.setLogLevel('info'); + if (options?.log && options?.logLevel) { + loggerModule.setLogLevel(options?.logLevel!); } else { - loggerModule.setLogLevel('silent'); + loggerModule.setLogLevel('all') } this.options = options; // Fetch sdk version from package.json @@ -161,8 +162,13 @@ export class ReclaimProofRequest { } if (options.log) { validateFunctionParams([ - { paramName: 'log', input: options.log } + { paramName: 'log', input: options.log }, ], 'the constructor') + + if(options.logLevel){ + validateFunctionParams([{ paramName: 'logLevel', input: options.logLevel } + ], 'the constructor') + } } } @@ -179,7 +185,8 @@ export class ReclaimProofRequest { return proofRequestInstance } catch (error) { - logger.info('Failed to initialize ReclaimProofRequest', error as Error); + logger.error('Failed to initialize ReclaimProofRequest', error as Error); + logger.warn("Try changing values of applicationId, applicationSecret, ProviderId, Option's Parameters") throw new InitError('Failed to initialize ReclaimProofRequest', error as Error) } } @@ -234,7 +241,7 @@ export class ReclaimProofRequest { proofRequestInstance.sdkVersion = sdkVersion; return proofRequestInstance } catch (error) { - logger.info('Failed to parse JSON string in fromJsonString:', error); + logger.error('Failed to parse JSON string in fromJsonString:', error); throw new InvalidParamError('Invalid JSON string provided to fromJsonString'); } } @@ -258,7 +265,8 @@ export class ReclaimProofRequest { ], 'addContext'); this.context = { contextAddress: address, contextMessage: message }; } catch (error) { - logger.info("Error adding context", error) + logger.error("Error adding context", error) + logger.info(`contextId: A unique identifier for the context (hex address) \nContext message: Additional information about the proof request (string)`) throw new AddContextError("Error adding context", error as Error) } } @@ -285,7 +293,8 @@ export class ReclaimProofRequest { } this.requestedProof.parameters = { ...requestedProof.parameters, ...params } } catch (error) { - logger.info('Error Setting Params:', error); + logger.error('Error Setting Params:', error); + logger.warn(`Arguments passed in setParams must be object which contains key => value pairs`) throw new SetParamsError("Error setting params", error as Error) } } @@ -296,7 +305,8 @@ export class ReclaimProofRequest { validateFunctionParams([{ input: this.sessionId, paramName: 'sessionId', isString: true }], 'getAppCallbackUrl'); return this.appCallbackUrl || `${constants.DEFAULT_RECLAIM_CALLBACK_URL}${this.sessionId}` } catch (error) { - logger.info("Error getting app callback url", error) + logger.error("Error getting app callback url", error) + logger.warn(`Make sure to pass URL and as a string`) throw new GetAppCallbackUrlError("Error getting app callback url", error as Error) } } @@ -306,7 +316,8 @@ export class ReclaimProofRequest { validateFunctionParams([{ input: this.sessionId, paramName: 'sessionId', isString: true }], 'getStatusUrl'); return `${constants.DEFAULT_RECLAIM_STATUS_URL}${this.sessionId}` } catch (error) { - logger.info("Error fetching Status Url", error) + logger.error("Error fetching Status Url", error) + logger.warn(`Make sure to pass URL and as a string`) throw new GetStatusUrlError("Error fetching status url", error as Error) } } @@ -318,7 +329,7 @@ export class ReclaimProofRequest { this.signature = signature; logger.info(`Signature set successfully for applicationId: ${this.applicationId}`); } catch (error) { - logger.info("Error setting signature", error) + logger.error("Error setting signature", error) throw new SetSignatureError("Error setting signature", error as Error) } } @@ -337,7 +348,7 @@ export class ReclaimProofRequest { return await wallet.signMessage(ethers.getBytes(messageHash)); } catch (err) { - logger.info(`Error generating proof request for applicationId: ${this.applicationId}, providerId: ${this.providerId}, signature: ${this.signature}, timeStamp: ${this.timeStamp}`, err); + logger.error(`Error generating proof request for applicationId: ${this.applicationId}, providerId: ${this.providerId}, signature: ${this.signature}, timeStamp: ${this.timeStamp}`, err); throw new SignatureGeneratingError(`Error generating signature for applicationSecret: ${applicationSecret}`) } } @@ -347,7 +358,7 @@ export class ReclaimProofRequest { this.requestedProof = generateRequestedProof(provider); return this.requestedProof; } catch (err: Error | unknown) { - logger.info(err instanceof Error ? err.message : String(err)); + logger.error(err instanceof Error ? err.message : String(err)); throw new BuildProofRequestError('Something went wrong while generating proof request', err as Error); } } @@ -370,7 +381,7 @@ export class ReclaimProofRequest { return [...new Set(availableParamsStore)]; } catch (error) { - logger.info("Error fetching available params", error) + logger.error("Error fetching available params", error) throw new AvailableParamsError("Error fetching available params", error as Error) } } @@ -428,16 +439,16 @@ export class ReclaimProofRequest { await updateSession(this.sessionId, SessionStatus.SESSION_STARTED) return link } catch (error) { - logger.info('Error creating Request Url:', error) + logger.error('Error creating Request Url:', error) throw error } } async startSession({ onSuccess, onError }: StartSessionParams): Promise { if (!this.sessionId) { - const message = "Session can't be started due to undefined value of sessionId"; - logger.info(message); - throw new SessionNotStartedError(message); + const message = "Session can't be started due to undefined value of statusUrl and sessionId" + logger.warn(message) + throw new SessionNotStartedError(message) } logger.info('Starting session'); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index a0aa03c..aa92c27 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -11,5 +11,6 @@ export const constants = { DEFAULT_RECLAIM_STATUS_URL: `${BACKEND_BASE_URL}/api/sdk/session/`, // URL for sharing Reclaim templates - RECLAIM_SHARE_URL: 'https://share.reclaimprotocol.org/verifier/?template=' + RECLAIM_SHARE_URL: 'https://share.reclaimprotocol.org/verifier/?template=', + }; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index da78917..f5dd477 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,21 +1,22 @@ // Define the possible log levels -export type LogLevel = 'info' | 'warn' | 'error' | 'silent'; +export type LogLevel = 'info' | 'warn' | 'error' ; +export type ExtendedLog = LogLevel | 'all' // Define a simple logger class class SimpleLogger { - private level: LogLevel = 'info'; + private level: ExtendedLog = 'info'; - setLevel(level: LogLevel) { + setLevel(level: LogLevel | 'all') { this.level = level; } private shouldLog(messageLevel: LogLevel): boolean { - const levels: LogLevel[] = ['error', 'warn', 'info', 'silent']; + const levels: ExtendedLog[] = ['error', 'warn', 'info', 'all']; return levels.indexOf(this.level) >= levels.indexOf(messageLevel); } private log(level: LogLevel, message: string, ...args: any[]) { - if (this.shouldLog(level) && this.level !== 'silent') { + if (this.shouldLog(level)) { const logFunction = this.getLogFunction(level); console.log('current level', this.level); logFunction(`[${level.toUpperCase()}]`, message, ...args); @@ -31,7 +32,11 @@ class SimpleLogger { case 'info': return console.info; default: - return () => {}; // No-op for 'silent' + return (message: string, ...optionalParams: any[]) => { + console.info('info',message, ...optionalParams); + console.warn('warn',message, ...optionalParams); + console.error('error',message, ...optionalParams); + }; } } @@ -52,7 +57,7 @@ class SimpleLogger { const logger = new SimpleLogger(); // Function to set the log level -export function setLogLevel(level: LogLevel) { +export function setLogLevel(level: LogLevel | 'all') { logger.setLevel(level); } diff --git a/src/utils/proofUtils.ts b/src/utils/proofUtils.ts index 83e4fe9..601cc9a 100644 --- a/src/utils/proofUtils.ts +++ b/src/utils/proofUtils.ts @@ -61,13 +61,13 @@ export async function getShortenedUrl(url: string): Promise { }) const res = await response.json() if (!response.ok) { - logger.info(`Failed to shorten URL: ${url}, Response: ${JSON.stringify(res)}`); + logger.error(`Failed to shorten URL: ${url}, Response: ${JSON.stringify(res)}`); return url; } const shortenedVerificationUrl = res.result.shortUrl return shortenedVerificationUrl } catch (err) { - logger.info(`Error shortening URL: ${url}, Error: ${err}`); + logger.error(`Error shortening URL: ${url}, Error: ${err}`); return url } } @@ -87,7 +87,7 @@ export async function createLinkWithTemplateData(templateData: TemplateData): Pr const shortenedLink = await getShortenedUrl(fullLink) return shortenedLink; } catch (err) { - logger.info(`Error creating link for sessionId: ${templateData.sessionId}, Error: ${err}`); + logger.error(`Error creating link for sessionId: ${templateData.sessionId}, Error: ${err}`); return fullLink; } } @@ -107,7 +107,7 @@ export async function getWitnessesForClaim( ): Promise { const beacon = makeBeacon() if (!beacon) { - logger.info('No beacon available for getting witnesses'); + logger.warn('No beacon available for getting witnesses'); throw new Error('No beacon available'); } const state = await beacon.getState(epoch) @@ -153,7 +153,7 @@ export function assertValidSignedClaim( if (witnessesNotSeen.size > 0) { const missingWitnesses = Array.from(witnessesNotSeen).join(', '); - logger.info(`Claim validation failed. Missing signatures from: ${missingWitnesses}`); + logger.warn(`Claim validation failed. Missing signatures from: ${missingWitnesses}`); throw new ProofNotVerifiedError( `Missing signatures from ${missingWitnesses}` ) diff --git a/src/utils/sessionUtils.ts b/src/utils/sessionUtils.ts index 37edcb1..9b70cf4 100644 --- a/src/utils/sessionUtils.ts +++ b/src/utils/sessionUtils.ts @@ -35,13 +35,13 @@ export async function initSession( const res = await response.json(); if (!response.ok) { - logger.info(`Session initialization failed: ${res.message || 'Unknown error'}`); - throw new InitSessionError(res.message || `Error initializing session with providerId: ${providerId}`); + logger.error(`Session initialization failed: ${res.message || 'Unknown error'}`); + throw new InitSessionError(); } return res as InitSessionResponse; } catch (err) { - logger.info(`Failed to initialize session for providerId: ${providerId}, appId: ${appId}`, err); + logger.error(`Failed to initialize session for providerId: ${providerId}, appId: ${appId}`); throw err; } } @@ -71,7 +71,7 @@ export async function updateSession(sessionId: string, status: SessionStatus) { if (!response.ok) { const errorMessage = `Error updating session with sessionId: ${sessionId}. Status Code: ${response.status}`; - logger.info(errorMessage, res); + logger.error(errorMessage, res); throw new UpdateSessionError(errorMessage); } @@ -79,7 +79,7 @@ export async function updateSession(sessionId: string, status: SessionStatus) { return res; } catch (err) { const errorMessage = `Failed to update session with sessionId: ${sessionId}`; - logger.info(errorMessage, err); + logger.error(errorMessage, err); throw new UpdateSessionError(`Error updating session with sessionId: ${sessionId}`); } } @@ -106,14 +106,14 @@ export async function fetchStatusUrl(sessionId: string): Promise & AnyClaimInfo; + + export type SignedClaim = { claim: CompleteClaimData; signatures: Uint8Array[]; @@ -29,9 +33,12 @@ export type StartSessionParams = { export type OnSuccess = (proof?: Proof | string) => void; export type OnError = (error: Error) => void; + + export type ProofRequestOptions = { log?: boolean; - acceptAiProviders?: boolean; + logLevel?: LogLevel; + acceptAiProviders?: boolean; }; // Session and response types diff --git a/src/utils/validationUtils.ts b/src/utils/validationUtils.ts index 953b9fd..2805834 100644 --- a/src/utils/validationUtils.ts +++ b/src/utils/validationUtils.ts @@ -2,10 +2,11 @@ import { ethers } from "ethers"; import { InvalidParamError, InvalidSignatureError, ProviderNotFoundError } from "./errors"; import canonicalize from 'canonicalize' import { Context, RequestedProof } from "./interfaces"; -import loggerModule from './logger'; +import loggerModule, {LogLevel} from './logger'; import { ProofRequestOptions } from "./types"; const logger = loggerModule.logger; + /** * Validates function parameters based on specified criteria * @param params - An array of objects containing input, paramName, and optional isString flag @@ -15,15 +16,18 @@ const logger = loggerModule.logger; export function validateFunctionParams(params: { input: any, paramName: string, isString?: boolean }[], functionName: string): void { params.forEach(({ input, paramName, isString }) => { if (input == null) { - logger.info(`Validation failed: ${paramName} in ${functionName} is null or undefined`); + logger.warn(`Validation failed: Expected ${paramName} in ${functionName} is null or undefined`); throw new InvalidParamError(`${paramName} passed to ${functionName} must not be null or undefined.`); } + if(paramName === 'acceptAiProvider' || paramName === 'log' || paramName || paramName === 'logLevel') { + validateOptions(input) + } if (isString && typeof input !== 'string') { - logger.info(`Validation failed: ${paramName} in ${functionName} is not a string`); + logger.warn(`Validation failed: ${paramName} in ${functionName} is not a string`); throw new InvalidParamError(`${paramName} passed to ${functionName} must be a string.`); } if (isString && input.trim() === '') { - logger.info(`Validation failed: ${paramName} in ${functionName} is an empty string`); + logger.warn(`Validation failed: ${paramName} in ${functionName} is an empty string`); throw new InvalidParamError(`${paramName} passed to ${functionName} must not be an empty string.`); } }); @@ -39,7 +43,8 @@ export function validateURL(url: string, functionName: string): void { try { new URL(url); } catch (e) { - logger.info(`URL validation failed for ${url} in ${functionName}: ${(e as Error).message}`); + logger.error(`URL validation failed for ${url} in ${functionName}: ${(e as Error).message}`); + logger.warn('Make sure to pass URL as string') throw new InvalidParamError(`Invalid URL format ${url} passed to ${functionName}.`, e as Error); } } @@ -58,8 +63,8 @@ export function validateSignature(providerId: string, signature: string, applica const message = canonicalize({ providerId, timestamp }); if (!message) { - logger.info('Failed to canonicalize message for signature validation'); - throw new Error('Failed to canonicalize message'); + logger.warn('Failed to canonicalize message for signature validation'); + throw new Error(`Failed to canonicalize message because message is ${message}`); } const messageHash = ethers.keccak256(new TextEncoder().encode(message)); let appId = ethers.verifyMessage( @@ -68,13 +73,13 @@ export function validateSignature(providerId: string, signature: string, applica ).toLowerCase(); if (ethers.getAddress(appId) !== ethers.getAddress(applicationId)) { - logger.info(`Signature validation failed: Mismatch between derived appId (${appId}) and provided applicationId (${applicationId})`); + logger.warn(`Signature validation failed: Mismatch between derived appId (${appId}) and provided applicationId (${applicationId})`); throw new InvalidSignatureError(`Signature does not match the application id: ${appId}`); } logger.info(`Signature validated successfully for applicationId: ${applicationId}`); } catch (err) { - logger.info(`Signature validation failed: ${(err as Error).message}`); + logger.error(`Signature validation failed: ${(err as Error).message}`); if (err instanceof InvalidSignatureError) { throw err; } @@ -90,12 +95,13 @@ export function validateSignature(providerId: string, signature: string, applica */ export function validateRequestedProof(requestedProof: RequestedProof): void { if (!requestedProof.url) { - logger.info(`Requested proof validation failed: Provided url in requested proof is not valid`); + logger.warn(`Requested proof validation failed: Provided url in requested proof is not valid`); throw new InvalidParamError(`The provided url in requested proof is not valid`); } if (requestedProof.parameters && typeof requestedProof.parameters !== 'object') { - logger.info(`Requested proof validation failed: Provided parameters in requested proof is not valid`); + logger.warn(`Requested proof validation failed: Provided parameters in requested proof is not valid`); + logger.warn(`Requested Proof's parameters must be an Object`) throw new InvalidParamError(`The provided parameters in requested proof is not valid`); } } @@ -107,12 +113,12 @@ export function validateRequestedProof(requestedProof: RequestedProof): void { */ export function validateContext(context: Context): void { if (!context.contextAddress) { - logger.info(`Context validation failed: Provided context address in context is not valid`); + logger.warn(`Context validation failed: Provided context address in context is not valid`); throw new InvalidParamError(`The provided context address in context is not valid`); } if (!context.contextMessage) { - logger.info(`Context validation failed: Provided context message in context is not valid`); + logger.warn(`Context validation failed: Provided context message in context is not valid`); throw new InvalidParamError(`The provided context message in context is not valid`); } @@ -129,12 +135,20 @@ export function validateContext(context: Context): void { */ export function validateOptions(options: ProofRequestOptions): void { if (options.acceptAiProviders && typeof options.acceptAiProviders !== 'boolean') { - logger.info(`Options validation failed: Provided acceptAiProviders in options is not valid`); + logger.warn(`Options validation failed: Provided acceptAiProviders in options is not valid`); + logger.info(`acceptAiProviders option must be boolean value`) throw new InvalidParamError(`The provided acceptAiProviders in options is not valid`); } if (options.log && typeof options.log !== 'boolean') { - logger.info(`Options validation failed: Provided log in options is not valid`); + logger.warn(`Options validation failed: Provided log in options is not valid`); + logger.info(`log option must be boolean value`) + throw new InvalidParamError(`The provided log in options is not valid`); + } + + if (options.logLevel && !['info', 'warn', 'error'].includes(options.logLevel)) { + logger.warn(`Options validation failed: Provided loglevel in options is not valid`); + logger.info(`Available options for log is 'info' , 'warn' , 'error' `) throw new InvalidParamError(`The provided log in options is not valid`); } }