From 2547b06a48a8581bfe9a9e5f6ff91682d53e4951 Mon Sep 17 00:00:00 2001 From: Ben Wilson Date: Wed, 21 Jun 2023 14:32:33 -0400 Subject: [PATCH] cors, bug --- src/app.controller.ts | 35 ++++++++++++++++--- src/bitcoin-rpc.service.ts | 2 ++ src/main.ts | 18 ++++++++-- src/models/StratumV1Client.ts | 35 +++++++++++++++---- src/models/StratumV1ClientStatistics.ts | 45 +++++++++++++++++++++++++ src/stratum-v1.service.ts | 15 +++------ 6 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 src/models/StratumV1ClientStatistics.ts diff --git a/src/app.controller.ts b/src/app.controller.ts index cce879e..3348493 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,12 +1,39 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, Param } from '@nestjs/common'; + import { AppService } from './app.service'; +import { StratumV1Service } from './stratum-v1.service'; @Controller() export class AppController { - constructor(private readonly appService: AppService) {} + constructor(private readonly appService: AppService, private readonly stratumV1Service: StratumV1Service) { } @Get() - getHello(): string { - return this.appService.getHello(); + getInfo() { + return { + clients: this.stratumV1Service.clients.length + } + } + + @Get('client/:clientId') + getClientInfo(@Param('clientId') clientId: string) { + const workers = this.stratumV1Service.clients.filter(client => client.clientAuthorization.address === clientId); + return { + workers: workers.length, + workerIds: workers.map(worker => { + return { + id: worker.id, + bestDifficulty: worker.statistics.bestDifficulty, + hashrate: worker.statistics.getHashrate() + } + }) + } + } + + @Get('client/:clientId/:workerId') + getWorkerInfo(@Param('clientId') clientId: string, @Param('workerId') workerId: string) { + const worker = this.stratumV1Service.clients.find(client => client.clientAuthorization.address === clientId && client.id === workerId); + return { + id: worker.id + } } } diff --git a/src/bitcoin-rpc.service.ts b/src/bitcoin-rpc.service.ts index 46b5e05..f4cef77 100644 --- a/src/bitcoin-rpc.service.ts +++ b/src/bitcoin-rpc.service.ts @@ -70,6 +70,8 @@ export class BitcoinRpcService { hexdata }); console.log(`BLOCK SUBMISSION RESPONSE: ${res}`); + // console.log(JSON.stringify(res)); + // process.exit(); } catch (e) { console.log(`BLOCK SUBMISSION RESPONSE ERROR: ${e}`); } diff --git a/src/main.ts b/src/main.ts index 8bc88a3..f374332 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,24 @@ +import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import { AppModule } from './app.module'; -const PORT = 40557; + async function bootstrap() { const app = await NestFactory.create(AppModule, new FastifyAdapter()); - await app.listen(PORT, '0.0.0.0'); - console.log(`http listening on port ${PORT}`) + app.setGlobalPrefix('api') + app.useGlobalPipes( + new ValidationPipe({ + transform: true, + whitelist: true, + forbidNonWhitelisted: true, + forbidUnknownValues: true + }), + ); + app.enableCors(); + + await app.listen(process.env.PORT); + console.log(`http listening on port ${process.env.PORT}`) } bootstrap(); diff --git a/src/models/StratumV1Client.ts b/src/models/StratumV1Client.ts index f61432f..4446329 100644 --- a/src/models/StratumV1Client.ts +++ b/src/models/StratumV1Client.ts @@ -16,16 +16,17 @@ import { ConfigurationMessage } from './stratum-messages/ConfigurationMessage'; import { MiningSubmitMessage } from './stratum-messages/MiningSubmitMessage'; import { SubscriptionMessage } from './stratum-messages/SubscriptionMessage'; import { SuggestDifficulty } from './stratum-messages/SuggestDifficultyMessage'; +import { StratumV1ClientStatistics } from './StratumV1ClientStatistics'; export class StratumV1Client extends EasyUnsubscribe { - private clientSubscription: SubscriptionMessage; - private clientConfiguration: ConfigurationMessage; - private clientAuthorization: AuthorizationMessage; - private clientSuggestedDifficulty: SuggestDifficulty; + public clientSubscription: SubscriptionMessage; + public clientConfiguration: ConfigurationMessage; + public clientAuthorization: AuthorizationMessage; + public clientSuggestedDifficulty: SuggestDifficulty; - // public clientSubmission: BehaviorSubject = new BehaviorSubject(null); + public statistics: StratumV1ClientStatistics = new StratumV1ClientStatistics(); public id: string; public stratumInitialized = false; @@ -37,6 +38,8 @@ export class StratumV1Client extends EasyUnsubscribe { public jobRefreshInterval: NodeJS.Timer; + + constructor( public readonly socket: Socket, private readonly stratumV1JobsService: StratumV1JobsService, @@ -46,7 +49,7 @@ export class StratumV1Client extends EasyUnsubscribe { super(); this.id = this.getRandomHexString(); - console.log(`id: ${this.id}`); + console.log(`New client ID: : ${this.id}`); this.socket.on('data', this.handleData.bind(this, this.socket)); @@ -234,10 +237,17 @@ export class StratumV1Client extends EasyUnsubscribe { private handleMiningSubmission(submission: MiningSubmitMessage) { const job = this.stratumV1JobsService.getJobById(submission.jobId); + // a miner may submit a job that doesn't exist anymore if it was removed by a new block notification + if (job == null) { + return; + } const diff = submission.calculateDifficulty(this.id, job, submission); console.log(`DIFF: ${diff}`); + if (diff >= this.clientDifficulty) { - if (diff >= job.networkDifficulty) { + const networkDifficulty = this.calculateNetworkDifficulty(parseInt(job.blockTemplate.bits, 16)); + this.statistics.addSubmission(this.clientDifficulty, diff, networkDifficulty); + if (diff >= networkDifficulty) { console.log('!!! BOCK FOUND !!!'); this.constructBlockAndBroadcast(job, submission); } @@ -247,6 +257,17 @@ export class StratumV1Client extends EasyUnsubscribe { } + private calculateNetworkDifficulty(nBits: number) { + const mantissa: number = nBits & 0x007fffff; // Extract the mantissa from nBits + const exponent: number = (nBits >> 24) & 0xff; // Extract the exponent from nBits + + const target: number = mantissa * Math.pow(256, (exponent - 3)); // Calculate the target value + + const difficulty: number = (Math.pow(2, 208) * 65535) / target; // Calculate the difficulty + + return difficulty; + } + private constructBlockAndBroadcast(job: MiningJob, submission: MiningSubmitMessage) { const block = new Block(); diff --git a/src/models/StratumV1ClientStatistics.ts b/src/models/StratumV1ClientStatistics.ts new file mode 100644 index 0000000..58cfcad --- /dev/null +++ b/src/models/StratumV1ClientStatistics.ts @@ -0,0 +1,45 @@ +export class StratumV1ClientStatistics { + + public bestDifficulty: number = 0; + + public historicSubmissions = []; + + constructor() { + + } + + public addSubmission(targetDifficulty: number, submissionDifficulty: number, networkDifficulty: number) { + + if (this.historicSubmissions.length >= 1000) { + this.historicSubmissions.shift(); + } + + this.historicSubmissions.push({ + difficulty: targetDifficulty, + time: new Date() + }); + + if (submissionDifficulty > this.bestDifficulty) { + this.bestDifficulty = submissionDifficulty; + } + } + + public getHashrate() { + return this.historicSubmissions.reduce((pre, cur, idx, arr) => { + if (idx === 0) { + pre.time = cur.time; + } + pre.difficulty += cur.difficulty; + + if (arr.length - 1 === idx) { + const duration = cur.time.getTime() - pre.time.getTime(); + const sumDifficulty = pre.difficulty; + return (sumDifficulty * 4294967296) / (duration * 1000000) + } + return pre; + }, { + difficulty: 0, + time: undefined + }) + } +} \ No newline at end of file diff --git a/src/stratum-v1.service.ts b/src/stratum-v1.service.ts index 3e05edd..754041f 100644 --- a/src/stratum-v1.service.ts +++ b/src/stratum-v1.service.ts @@ -10,13 +10,8 @@ import { StratumV1JobsService } from './stratum-v1-jobs.service'; @Injectable() export class StratumV1Service implements OnModuleInit { - private miningNotifyInterval: NodeJS.Timer; - - public clients: StratumV1Client[] = []; - - constructor( private readonly bitcoinRpcService: BitcoinRpcService, private readonly blockTemplateService: BlockTemplateService @@ -28,29 +23,29 @@ export class StratumV1Service implements OnModuleInit { this.startSocketServer(); - //this.listenForNewBlocks(); - } private startSocketServer() { new Server((socket: Socket) => { - console.log('New client connected:', socket.remoteAddress); + const client = new StratumV1Client(socket, new StratumV1JobsService(), this.blockTemplateService, this.bitcoinRpcService); this.clients.push(client); + console.log(`New client connected: ${socket.remoteAddress}, ${this.clients.length} total clients`); socket.on('end', () => { // Handle socket disconnection - console.log('Client disconnected:', socket.remoteAddress); this.clients = this.clients.filter(c => c.id == client.id); + console.log(`Client disconnected: ${socket.remoteAddress}, ${this.clients.length} total clients`); }); socket.on('error', (error: Error) => { // Handle socket error - console.error('Socket error:', error); + console.error(`Socket error:`, error); this.clients = this.clients.filter(c => c.id == client.id); + console.log(`Client disconnected: ${socket.remoteAddress}, ${this.clients.length} total clients`); }); }).listen(3333, () => {