Add contact, upgrade versions, add docs
This commit is contained in:
@@ -4,102 +4,99 @@ import { InjectMetric } from '@willsoto/nestjs-prometheus';
|
||||
import { Cache } from 'cache-manager';
|
||||
import { randomBytes, createHash } from 'crypto';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class PowService {
|
||||
private readonly logger = new Logger(PowService.name);
|
||||
private difficulty = 5;
|
||||
private readonly logger = new Logger(PowService.name);
|
||||
private difficulty = 5;
|
||||
|
||||
constructor(
|
||||
@Inject(CACHE_MANAGER) private cacheManager: Cache,
|
||||
@InjectMetric('challenges_generated') private challengesGenerated: any,
|
||||
@InjectMetric('challenges_completed') private challengesCompleted: any,
|
||||
@InjectMetric('successful_verifies') private successfulVerifies: any,
|
||||
@InjectMetric('failed_verifies') private failedVerifies: any,
|
||||
@InjectMetric('difficulty') private powDifficulty: any,
|
||||
) {
|
||||
this.powDifficulty.set(this.difficulty);
|
||||
constructor(
|
||||
@Inject(CACHE_MANAGER) private cacheManager: Cache,
|
||||
@InjectMetric('pow_challenges_generated') private challengesGenerated: any,
|
||||
@InjectMetric('pow_challenges_completed') private challengesCompleted: any,
|
||||
@InjectMetric('pow_successful_verifies') private successfulVerifies: any,
|
||||
@InjectMetric('pow_failed_verifies') private failedVerifies: any,
|
||||
@InjectMetric('pow_difficulty') private powDifficulty: any,
|
||||
) {
|
||||
this.powDifficulty.set(this.difficulty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a proof of work challenge, stored to redis for verification within
|
||||
* the next 60 seconds.
|
||||
*/
|
||||
async generateChallenge() {
|
||||
const challenge = this.generateRandom256BitString();
|
||||
this.logger.verbose(`Generated challenge: ${challenge}`);
|
||||
this.challengesGenerated.inc();
|
||||
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<boolean> {
|
||||
const expected = await this.cacheManager.get<boolean>(challenge);
|
||||
const success = expected ? this.hashAndCheck(proof + challenge) : false;
|
||||
if (success) {
|
||||
this.successfulVerifies.inc();
|
||||
} else {
|
||||
this.failedVerifies.inc();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a proof of work challenge, stored to redis for verification within
|
||||
* the next 10 seconds.
|
||||
*/
|
||||
async generateChallenge() {
|
||||
const challenge = this.generateRandom256BitString();
|
||||
this.logger.verbose(`Generated challenge: ${challenge}`);
|
||||
this.challengesGenerated.inc();
|
||||
await this.cacheManager.set(challenge, true, 60 * 1000);
|
||||
return challenge;
|
||||
async markChallengeAsComplete(challenge: string) {
|
||||
this.challengesCompleted.inc();
|
||||
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 };
|
||||
}
|
||||
|
||||
generateRandom256BitString() {
|
||||
const randomString = randomBytes(32).toString('hex');
|
||||
return randomString;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
this.powDifficulty.set(this.difficulty);
|
||||
}
|
||||
|
||||
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<boolean> {
|
||||
const expected = await this.cacheManager.get<boolean>(challenge);
|
||||
const success = expected ? this.hashAndCheck(proof + challenge) : false;
|
||||
if (success) {
|
||||
this.successfulVerifies.inc();
|
||||
} else {
|
||||
this.failedVerifies.inc();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
async markChallengeAsComplete(challenge: string) {
|
||||
this.challengesCompleted.inc();
|
||||
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;
|
||||
this.powDifficulty.set(this.difficulty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current difficulty of the proof of work challenge.
|
||||
*/
|
||||
getDifficulty() {
|
||||
return this.difficulty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current difficulty of the proof of work challenge.
|
||||
*/
|
||||
getDifficulty() {
|
||||
return this.difficulty;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user