From c0c99fdfef96312bc49abda99925c2b04f9b661d Mon Sep 17 00:00:00 2001 From: 0xmad <0xmad@users.noreply.github.com> Date: Thu, 16 May 2024 13:28:55 -0500 Subject: [PATCH] chore(coordinator): add logger - [x] Add logger for auth guard - [x] Add logger for crypto service - [x] Add logger for proof generator service - [x] Add logger for app controller - [x] Minor swagger fix --- coordinator/package.json | 2 +- coordinator/tests/app.test.ts | 2 +- coordinator/ts/app.controller.ts | 26 ++++++++++++++----- .../ts/auth/AccountSignatureGuard.service.ts | 16 +++++++++--- coordinator/ts/crypto/crypto.service.ts | 11 +++++++- coordinator/ts/main.ts | 5 +++- coordinator/ts/proof/proof.service.ts | 17 +++++++++--- 7 files changed, 63 insertions(+), 16 deletions(-) diff --git a/coordinator/package.json b/coordinator/package.json index 5668d6ddf6..8f361eb8ee 100644 --- a/coordinator/package.json +++ b/coordinator/package.json @@ -79,7 +79,7 @@ "**/*.(t|j)s", "!/ts/main.ts", "!/ts/jest/*.js", - "!/hardhat.config.ts" + "!/hardhat.config.js" ], "coverageDirectory": "/coverage", "testEnvironment": "node" diff --git a/coordinator/tests/app.test.ts b/coordinator/tests/app.test.ts index 17697e6fa2..390f068f3b 100644 --- a/coordinator/tests/app.test.ts +++ b/coordinator/tests/app.test.ts @@ -44,7 +44,7 @@ describe("AppController (e2e)", () => { const publicKey = await fs.promises.readFile(process.env.COORDINATOR_PUBLIC_KEY_PATH!); const signature = await signer.signMessage("message"); const digest = Buffer.from(getBytes(hashMessage("message"))).toString("hex"); - return CryptoService.getInstance().encrypt(publicKey, `${signature}:${digest}`); + return `Bearer ${CryptoService.getInstance().encrypt(publicKey, `${signature}:${digest}`)}`; }; beforeAll(async () => { diff --git a/coordinator/ts/app.controller.ts b/coordinator/ts/app.controller.ts index 4172febfea..71f5847797 100644 --- a/coordinator/ts/app.controller.ts +++ b/coordinator/ts/app.controller.ts @@ -1,5 +1,5 @@ -import { Body, Controller, HttpException, HttpStatus, Post, UseGuards } from "@nestjs/common"; -import { ApiBody, ApiHeader, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { Body, Controller, HttpException, HttpStatus, Logger, Post, UseGuards } from "@nestjs/common"; +import { ApiBearerAuth, ApiBody, ApiResponse, ApiTags } from "@nestjs/swagger"; import { AccountSignatureGuard } from "./auth/AccountSignatureGuard.service"; import { GenerateProofDto } from "./proof/dto"; @@ -7,15 +7,28 @@ import { ProofGeneratorService } from "./proof/proof.service"; import { IGenerateData } from "./proof/types"; @ApiTags("v1/proof") -@ApiHeader({ - name: "Authorization", - description: "The value is encrypted with RSA public key you generated before (see README.md)", -}) +@ApiBearerAuth() @Controller("v1/proof") @UseGuards(AccountSignatureGuard) export class AppController { + /** + * Logger + */ + private readonly logger = new Logger(AppController.name); + + /** + * Initialize AppController + * + * @param proofGeneratorService - proof generator service + */ constructor(private readonly proofGeneratorService: ProofGeneratorService) {} + /** + * Generate proofs api method + * + * @param args - generate proof dto + * @returns + */ @ApiBody({ type: GenerateProofDto }) @ApiResponse({ status: HttpStatus.CREATED, description: "The proofs have been successfully generated" }) @ApiResponse({ status: HttpStatus.FORBIDDEN, description: "Forbidden" }) @@ -23,6 +36,7 @@ export class AppController { @Post("generate") async generate(@Body() args: GenerateProofDto): Promise { return this.proofGeneratorService.generate(args).catch((error: Error) => { + this.logger.error(`Error:`, error); throw new HttpException(error.message, HttpStatus.BAD_REQUEST); }); } diff --git a/coordinator/ts/auth/AccountSignatureGuard.service.ts b/coordinator/ts/auth/AccountSignatureGuard.service.ts index 1b8580943a..4d3d933327 100644 --- a/coordinator/ts/auth/AccountSignatureGuard.service.ts +++ b/coordinator/ts/auth/AccountSignatureGuard.service.ts @@ -1,4 +1,4 @@ -import { CanActivate, type ExecutionContext, Injectable } from "@nestjs/common"; +import { Logger, CanActivate, type ExecutionContext, Injectable } from "@nestjs/common"; import { ethers } from "ethers"; import fs from "fs"; @@ -27,7 +27,12 @@ export class AccountSignatureGuard implements CanActivate { /** * Crypto service */ - private cryptoService = CryptoService.getInstance(); + private readonly cryptoService = CryptoService.getInstance(); + + /** + * Logger + */ + private readonly logger = new Logger(AccountSignatureGuard.name); /** * This function should return a boolean, indicating whether the request is allowed or not based on message signature and digest. @@ -41,13 +46,17 @@ export class AccountSignatureGuard implements CanActivate { const encryptedHeader = request.headers.authorization; if (!encryptedHeader) { + this.logger.warn("No authorization header"); return false; } const privateKey = await fs.promises.readFile(path.resolve(process.env.COORDINATOR_PRIVATE_KEY_PATH!)); - const [signature, digest] = this.cryptoService.decrypt(privateKey, encryptedHeader).split(":"); + const [signature, digest] = this.cryptoService + .decrypt(privateKey, encryptedHeader.replace("Bearer", "").trim()) + .split(":"); if (!signature || !digest) { + this.logger.warn("No signature or digest"); return false; } @@ -56,6 +65,7 @@ export class AccountSignatureGuard implements CanActivate { return address === coordinatorAddress; } catch (error) { + this.logger.error("Error", error); return false; } } diff --git a/coordinator/ts/crypto/crypto.service.ts b/coordinator/ts/crypto/crypto.service.ts index b4a8c00fe3..c6f7748fe4 100644 --- a/coordinator/ts/crypto/crypto.service.ts +++ b/coordinator/ts/crypto/crypto.service.ts @@ -1,3 +1,5 @@ +import { Logger } from "@nestjs/common"; + import { publicEncrypt, privateDecrypt, type KeyLike } from "crypto"; import { ErrorCodes } from "../common"; @@ -11,11 +13,16 @@ export class CryptoService { */ private static INSTANCE?: CryptoService; + /** + * Logger + */ + private readonly logger: Logger; + /** * Empty constructor */ private constructor() { - // use singleton initialization + this.logger = new Logger(CryptoService.name); } /** @@ -44,6 +51,7 @@ export class CryptoService { return encrypted.toString("base64"); } catch (error) { + this.logger.error(`Error: ${ErrorCodes.ENCRYPTION}`, error); throw new Error(ErrorCodes.ENCRYPTION); } } @@ -61,6 +69,7 @@ export class CryptoService { return decryptedData.toString(); } catch (error) { + this.logger.error(`Error: ${ErrorCodes.DECRYPTION}`, error); throw new Error(ErrorCodes.DECRYPTION); } } diff --git a/coordinator/ts/main.ts b/coordinator/ts/main.ts index b46dbd2743..5c670fe705 100644 --- a/coordinator/ts/main.ts +++ b/coordinator/ts/main.ts @@ -10,7 +10,9 @@ dotenv.config({ path: [path.resolve(__dirname, "../.env"), path.resolve(__dirnam async function bootstrap() { const { AppModule } = await import("./app.module"); - const app = await NestFactory.create(AppModule); + const app = await NestFactory.create(AppModule, { + logger: ["log", "fatal", "error", "warn"], + }); app.useGlobalPipes(new ValidationPipe({ transform: true })); app.use( @@ -32,6 +34,7 @@ async function bootstrap() { .setDescription("Coordinator service API methods") .setVersion("1.0") .addTag("coordinator") + .addBearerAuth() .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup("api", app, document); diff --git a/coordinator/ts/proof/proof.service.ts b/coordinator/ts/proof/proof.service.ts index 56d9e57347..b10cf1c7ad 100644 --- a/coordinator/ts/proof/proof.service.ts +++ b/coordinator/ts/proof/proof.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from "@nestjs/common"; +import { Logger, Injectable } from "@nestjs/common"; import { ZeroAddress } from "ethers"; import hre from "hardhat"; import { Deployment, EContracts, ProofGenerator, type Poll, type MACI, type AccQueue } from "maci-contracts"; @@ -20,12 +20,17 @@ export class ProofGeneratorService { /** * Deployment helper */ - private deployment: Deployment; + private readonly deployment: Deployment; /** * CryptoService for user sensitive data decryption */ - private cryptoService: CryptoService; + private readonly cryptoService: CryptoService; + + /** + * Logger + */ + private readonly logger: Logger; /** * Proof generator initialization @@ -34,6 +39,7 @@ export class ProofGeneratorService { this.deployment = Deployment.getInstance(hre); this.deployment.setHre(hre); this.cryptoService = CryptoService.getInstance(); + this.logger = new Logger(ProofGeneratorService.name); } /** @@ -61,6 +67,7 @@ export class ProofGeneratorService { const pollAddress = await maciContract.polls(poll); if (pollAddress.toLowerCase() === ZeroAddress.toLowerCase()) { + this.logger.error(`Error: ${ErrorCodes.POLL_NOT_FOUND}, Poll ${poll} not found`); throw new Error(ErrorCodes.POLL_NOT_FOUND); } @@ -77,6 +84,7 @@ export class ProofGeneratorService { const isStateAqMerged = await pollContract.stateMerged(); if (!isStateAqMerged) { + this.logger.error(`Error: ${ErrorCodes.NOT_MERGED_STATE_TREE}, state tree is not merged`); throw new Error(ErrorCodes.NOT_MERGED_STATE_TREE); } @@ -85,6 +93,7 @@ export class ProofGeneratorService { const mainRoot = await messageAq.getMainRoot(messageTreeDepth.toString()); if (mainRoot.toString() === "0") { + this.logger.error(`Error: ${ErrorCodes.NOT_MERGED_MESSAGE_TREE}, message tree is not merged`); throw new Error(ErrorCodes.NOT_MERGED_MESSAGE_TREE); } @@ -97,6 +106,7 @@ export class ProofGeneratorService { ]); if (!coordinatorKeypair.pubKey.equals(publicKey)) { + this.logger.error(`Error: ${ErrorCodes.PRIVATE_KEY_MISMATCH}, wrong private key`); throw new Error(ErrorCodes.PRIVATE_KEY_MISMATCH); } @@ -118,6 +128,7 @@ export class ProofGeneratorService { const foundPoll = maciState.polls.get(BigInt(poll)); if (!foundPoll) { + this.logger.error(`Error: ${ErrorCodes.POLL_NOT_FOUND}, Poll ${poll} not found in maci state`); throw new Error(ErrorCodes.POLL_NOT_FOUND); }