diff --git a/src/file/file.controller.ts b/src/file/file.controller.ts
index 47ae6cc..1782d37 100644
--- a/src/file/file.controller.ts
+++ b/src/file/file.controller.ts
@@ -3,9 +3,11 @@ import { FileService } from './file.service';
import { Post } from '@nestjs/common';
import { UploadedObjectInfo } from 'minio';
import { FileInterceptor } from '@nestjs/platform-express';
-import { ApiConsumes } from '@nestjs/swagger';
+import { ApiConsumes, ApiTags } from '@nestjs/swagger';
+import { InjectMetric } from '@willsoto/nestjs-prometheus';
@Controller('file')
+@ApiTags('file')
export class FileController {
constructor(private readonly fileService: FileService) { }
diff --git a/src/kv/kv.controller.ts b/src/kv/kv.controller.ts
index 720924e..df3a721 100644
--- a/src/kv/kv.controller.ts
+++ b/src/kv/kv.controller.ts
@@ -12,7 +12,7 @@ import {
} from '@nestjs/common';
import { KvService } from './kv.service';
import { Request } from 'express';
-import { ApiTags } from '@nestjs/swagger';
+import { ApiQuery, ApiTags } from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
@Controller('kv')
@@ -21,6 +21,8 @@ export class KvController {
constructor(private readonly kvService: KvService) { }
@Get(':namespace/:key/metadata')
+ @ApiQuery({ name: 'namespace', required: true })
+ @ApiQuery({ name: 'key', required: true })
async getMetadata(
@Param('namespace') namespace: string,
@Param('key') key: string,
diff --git a/src/pow/pow.controller.ts b/src/pow/pow.controller.ts
index 6025148..5de3656 100644
--- a/src/pow/pow.controller.ts
+++ b/src/pow/pow.controller.ts
@@ -1,4 +1,4 @@
-import { Body, Controller, Get, Post, Query } from '@nestjs/common';
+import { Body, Controller, Get, Post, Query, Render } from '@nestjs/common';
import { PowService } from './pow.service';
import { ApiBody, ApiQuery, ApiTags } from '@nestjs/swagger';
@@ -9,6 +9,13 @@ export class PowController {
private readonly powService: PowService,
) { }
+ @Get('')
+ @Render('pow/index')
+ async index() {
+ return {
+ difficulty: this.powService.getDifficulty(),
+ };
+ }
@Get('challenge')
async generateChallenge() {
diff --git a/src/pow/pow.module.ts b/src/pow/pow.module.ts
index 6a81dbd..b9c581e 100644
--- a/src/pow/pow.module.ts
+++ b/src/pow/pow.module.ts
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { PowService } from './pow.service';
import { CacheModule } from '@nestjs/cache-manager';
import { PowController } from './pow.controller';
+import { PrometheusModule, makeGaugeProvider } from '@willsoto/nestjs-prometheus';
@Module({
imports: [
@@ -9,8 +10,35 @@ import { PowController } from './pow.controller';
ttl: 5,
max: 10,
}),
+ PrometheusModule.register({
+ customMetricPrefix: 'pow',
+ defaultMetrics: {
+ enabled: false,
+ },
+ }),
+ ],
+ providers: [PowService,
+ makeGaugeProvider({
+ name: 'challenges_generated',
+ help: 'The total number of POW challenges generated',
+ }),
+ makeGaugeProvider({
+ name: 'challenges_completed',
+ help: 'The total number of POW challenges completed',
+ }),
+ makeGaugeProvider({
+ name: 'successful_verifies',
+ help: 'The total number of successful POW challenge verifications',
+ }),
+ makeGaugeProvider({
+ name: 'failed_verifies',
+ help: 'The total number of failed POW challenge verifications',
+ }),
+ makeGaugeProvider({
+ name: 'difficulty',
+ help: 'The current POW difficulty',
+ }),
],
- providers: [PowService],
controllers: [PowController]
})
export class PowModule { }
diff --git a/src/pow/pow.service.ts b/src/pow/pow.service.ts
index 2722930..fff8ea9 100644
--- a/src/pow/pow.service.ts
+++ b/src/pow/pow.service.ts
@@ -1,5 +1,6 @@
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Inject, Injectable, Logger } from '@nestjs/common';
+import { InjectMetric } from '@willsoto/nestjs-prometheus';
import { Cache } from 'cache-manager';
import { randomBytes, createHash } from 'crypto';
@@ -11,7 +12,14 @@ export class PowService {
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);
+ }
/**
* Generate a proof of work challenge, stored to redis for verification within
@@ -19,7 +27,8 @@ export class PowService {
*/
async generateChallenge() {
const challenge = this.generateRandom256BitString();
- console.log(challenge)
+ this.logger.verbose(`Generated challenge: ${challenge}`);
+ this.challengesGenerated.inc();
await this.cacheManager.set(challenge, true, 60 * 1000);
return challenge;
}
@@ -44,10 +53,17 @@ export class PowService {
*/
async verifyChallenge(challenge: string, proof: string): Promise
+ This is a simple API that allows you to generate a proof of work
+ challenges and verify the proof of work.
+
+ The API has two endpoints:
+ PoW API
+
+
+
+ The proof of work challenge is to find a proof which, when concatenated + and hashed (via SHA 512) with a challenge string that starts with a + certain number of zeros. The number of zeros is determined by the + difficulty level. The difficulty level is a number between 1 and 10. The + higher the difficulty level, the more zeros the string must start with. +
++ The current difficulty is + {{difficulty}}. +
++ The example code for performing a proof of work challenge is: +
+ const crypto = require('crypto'); + const axios = require('axios'); + + async function getChallenge() { + const response = await axios.get('https://api.us.dev/pow/challenge'); + return response.data; + } + + async function submitSolution(challenge, proof) { + const response = await axios.post('https://api.us.dev/pow/challenge', { + challenge, + proof + }); + return response.data; + } + + function hashProof(challenge, proof) { + return crypto.createHash('sha512').update(challenge + proof).digest('hex'); + } + + function verifyChallenge(hash: string, difficulty: number) { + return hash.startsWith('0'.repeat(difficulty)); + } + + async function proofOfWork() { + const challenge = await getChallenge(); + let proof = 0; + let hash = ''; + do { + proof++; + hash = hashProof(challenge, proof); + } while (!verifyChallenge(hash, 5)); + return await submitSolution(challenge, proof); + } + + proofOfWork().then(console.log).catch(console.error); ++ +
+ If you're a developer you should use the GET /pow/challenge endpoint to + get the challenge, then pass the challenge to your client, then have the + client find a solution to the challenge and then submit their proof as + part of their later action. Your service should then verify the solution + using the POST /pow/challenge endpoint (or your own local implementation) + to verify the solution. +
+ + \ No newline at end of file