import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Inject, Injectable, Logger } from '@nestjs/common'; import { Cache } from 'cache-manager'; import { randomBytes, createHash } from 'crypto'; @Injectable() export class PowService { private readonly logger = new Logger(PowService.name); private difficulty = 5; constructor( @Inject(CACHE_MANAGER) private cacheManager: Cache, ) { } /** * Generate a proof of work challenge, stored to redis for verification within * the next 10 seconds. */ async generateChallenge() { const challenge = this.generateRandom256BitString(); console.log(challenge) await this.cacheManager.set(challenge, true, 60 * 1000); return challenge; } generateRandom256BitString() { const randomString = randomBytes(32).toString('hex'); return randomString; } hashAndCheck(string: string) { return this.hashPassesDifficulty(this.hashString(string), this.difficulty); } hashPassesDifficulty(hash: string, difficulty: number) { return hash.startsWith('0'.repeat(difficulty)); } /** * Verify that the proof of work submitted has a leading number of * zeroes equal to the challenge length and the challenge exists. */ async verifyChallenge(challenge: string, proof: string): Promise { const expected = await this.cacheManager.get(challenge); return expected ? this.hashAndCheck(proof + challenge) : false; } async markChallengeAsComplete(challenge: string) { await this.cacheManager.del(challenge); } /** * Perform a proof of work challenge to find a proof that hashes to a value */ async performChallenge(challenge: string) { let proof = this.generateRandom256BitString(); let hash = this.hashString(proof + challenge); while (!this.hashPassesDifficulty(hash, this.difficulty)) { proof = this.generateRandom256BitString(); hash = this.hashString(proof + challenge); } return { proof, hash }; } /** * sha512 the provided string and return the result. */ hashString(input: string) { return createHash('sha512').update(input).digest('hex'); } /** * Set the difficulty of the proof of work challenge. */ setDifficulty(difficulty: number) { this.difficulty = difficulty; } /** * Get the current difficulty of the proof of work challenge. */ getDifficulty() { return this.difficulty; } }