mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
data persistance init
This commit is contained in:
@@ -35,3 +35,6 @@ settings.json
|
|||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
#DB
|
||||||
|
**.sqlite
|
||||||
Generated
+2461
-116
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -24,6 +24,7 @@
|
|||||||
"@nestjs/config": "^2.3.2",
|
"@nestjs/config": "^2.3.2",
|
||||||
"@nestjs/core": "^9.0.0",
|
"@nestjs/core": "^9.0.0",
|
||||||
"@nestjs/platform-fastify": "^9.4.2",
|
"@nestjs/platform-fastify": "^9.4.2",
|
||||||
|
"@nestjs/typeorm": "^10.0.0",
|
||||||
"big.js": "^6.2.1",
|
"big.js": "^6.2.1",
|
||||||
"bitcoinjs-lib": "^6.1.3",
|
"bitcoinjs-lib": "^6.1.3",
|
||||||
"bs58": "^5.0.0",
|
"bs58": "^5.0.0",
|
||||||
@@ -32,7 +33,9 @@
|
|||||||
"merkletreejs": "^0.3.10",
|
"merkletreejs": "^0.3.10",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"rpc-bitcoin": "^2.0.0",
|
"rpc-bitcoin": "^2.0.0",
|
||||||
"rxjs": "^7.2.0"
|
"rxjs": "^7.2.0",
|
||||||
|
"sqlite3": "^5.1.6",
|
||||||
|
"typeorm": "^0.3.17"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^9.0.0",
|
"@nestjs/cli": "^9.0.0",
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
import { ClientEntity } from '../client/client.entity';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class ClientStatisticsEntity {
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ type: 'datetime' })
|
||||||
|
time: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
difficulty: number;
|
||||||
|
|
||||||
|
|
||||||
|
@ManyToOne(
|
||||||
|
() => ClientEntity,
|
||||||
|
client => client.clientStatistics
|
||||||
|
)
|
||||||
|
client: ClientEntity;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { ClientStatisticsEntity } from './client-statistics.entity';
|
||||||
|
import { ClientStatisticsService } from './client-statistics.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([ClientStatisticsEntity])],
|
||||||
|
providers: [ClientStatisticsService],
|
||||||
|
exports: [TypeOrmModule, ClientStatisticsService],
|
||||||
|
})
|
||||||
|
export class ClientStatisticsModule { }
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ClientStatisticsEntity } from './client-statistics.entity';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ClientStatisticsService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ClientStatisticsEntity)
|
||||||
|
private clientStatisticsRepository: Repository<ClientStatisticsEntity>
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async save(clientStatistic: Partial<ClientStatisticsEntity>) {
|
||||||
|
return await this.clientStatisticsRepository.save(clientStatistic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getHashRate(clientId: string) {
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
SELECT
|
||||||
|
(JULIANDAY(MAX(entry.time)) - JULIANDAY(MIN(entry.time))) * 24 * 60 * 60 AS timeDiff,
|
||||||
|
SUM(entry.difficulty) AS difficultySum
|
||||||
|
FROM
|
||||||
|
client_statistics_entity AS entry
|
||||||
|
WHERE
|
||||||
|
entry.clientId = ?
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = await this.clientStatisticsRepository.query(query, [clientId]);
|
||||||
|
|
||||||
|
const timeDiff = result[0].timeDiff;
|
||||||
|
const difficultySum = result[0].difficultySum;
|
||||||
|
|
||||||
|
return (difficultySum * 4294967296) / (timeDiff * 1000000000);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { Column, Entity, OneToMany, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
|
import { ClientStatisticsEntity } from '../client-statistics/client-statistics.entity';
|
||||||
|
import { TrackedEntity } from '../utils/TrackedEntity.entity';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class ClientEntity extends TrackedEntity {
|
||||||
|
@PrimaryColumn({ length: 8, type: 'varchar' })
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column({ type: 'datetime' })
|
||||||
|
startTime: Date;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
clientName: string;
|
||||||
|
|
||||||
|
@Column({ length: 62, type: 'varchar' })
|
||||||
|
address: string;
|
||||||
|
|
||||||
|
@Column({ default: 0 })
|
||||||
|
bestDifficulty: number
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
() => ClientStatisticsEntity,
|
||||||
|
clientStatistic => clientStatistic.client
|
||||||
|
)
|
||||||
|
clientStatistics?: ClientStatisticsEntity[];
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { ClientEntity } from './client.entity';
|
||||||
|
import { ClientService } from './client.service';
|
||||||
|
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([ClientEntity])],
|
||||||
|
providers: [ClientService],
|
||||||
|
exports: [TypeOrmModule, ClientService],
|
||||||
|
})
|
||||||
|
export class ClientModule { }
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
|
import { ClientEntity } from './client.entity';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ClientService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(ClientEntity)
|
||||||
|
private clientRepository: Repository<ClientEntity>
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async save(client: Partial<ClientEntity>) {
|
||||||
|
return await this.clientRepository.save(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async delete(id: string) {
|
||||||
|
return await this.clientRepository.softDelete({ id });
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateBestDifficulty(id: string, bestDifficulty: number) {
|
||||||
|
return await this.clientRepository.update(id, { bestDifficulty });
|
||||||
|
}
|
||||||
|
public async connectedClientCount(): Promise<number> {
|
||||||
|
return await this.clientRepository.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getByAddress(address: string): Promise<ClientEntity[]> {
|
||||||
|
return await this.clientRepository.find({
|
||||||
|
where: {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getByAddressAndName(address: string, id: string): Promise<ClientEntity> {
|
||||||
|
return await this.clientRepository.findOne({
|
||||||
|
where: {
|
||||||
|
address,
|
||||||
|
id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { CreateDateColumn, DeleteDateColumn, UpdateDateColumn } from 'typeorm';
|
||||||
|
|
||||||
|
export abstract class TrackedEntity {
|
||||||
|
@DeleteDateColumn({ nullable: true, type: 'datetime' })
|
||||||
|
public deletedAt?: Date;
|
||||||
|
|
||||||
|
@CreateDateColumn({ type: 'datetime' })
|
||||||
|
public createdAt?: Date
|
||||||
|
|
||||||
|
@UpdateDateColumn({ type: 'datetime' })
|
||||||
|
public updatedAt?: Date
|
||||||
|
}
|
||||||
+30
-15
@@ -1,44 +1,59 @@
|
|||||||
import { Controller, Get, Param } from '@nestjs/common';
|
import { Controller, Get, Param } from '@nestjs/common';
|
||||||
|
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service';
|
||||||
|
import { ClientService } from './ORM/client/client.service';
|
||||||
import { StratumV1Service } from './stratum-v1.service';
|
import { StratumV1Service } from './stratum-v1.service';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
constructor(private readonly appService: AppService, private readonly stratumV1Service: StratumV1Service) { }
|
constructor(
|
||||||
|
private readonly appService: AppService,
|
||||||
|
private readonly stratumV1Service: StratumV1Service,
|
||||||
|
private readonly clientService: ClientService,
|
||||||
|
private readonly clientStatisticsService: ClientStatisticsService
|
||||||
|
) { }
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
getInfo() {
|
async getInfo() {
|
||||||
return {
|
return {
|
||||||
clients: this.stratumV1Service.clients.length
|
clients: await this.clientService.connectedClientCount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('client/:clientId')
|
@Get('client/:clientId')
|
||||||
getClientInfo(@Param('clientId') clientId: string) {
|
async getClientInfo(@Param('clientId') clientId: string) {
|
||||||
const workers = this.stratumV1Service.clients.filter(client => client.clientAuthorization.address === clientId);
|
|
||||||
|
const workers = await this.clientService.getByAddress(clientId);
|
||||||
|
|
||||||
|
//const workers = this.stratumV1Service.clients.filter(client => client.clientAuthorization.address === clientId);
|
||||||
return {
|
return {
|
||||||
workersCount: workers.length,
|
workersCount: workers.length,
|
||||||
workers: workers.map(worker => {
|
workers: await Promise.all(
|
||||||
|
workers.map(async (worker) => {
|
||||||
return {
|
return {
|
||||||
id: worker.id,
|
id: worker.id,
|
||||||
name: worker.clientAuthorization.worker,
|
name: worker.clientName,
|
||||||
bestDifficulty: Math.floor(worker.statistics.bestDifficulty),
|
bestDifficulty: Math.floor(worker.bestDifficulty),
|
||||||
hashRate: Math.floor(worker.statistics.getHashRate()),
|
hashRate: Math.floor(await this.clientStatisticsService.getHashRate(worker.id)),
|
||||||
startTime: worker.startTime
|
startTime: worker.startTime
|
||||||
}
|
};
|
||||||
})
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('client/:clientId/:workerId')
|
@Get('client/:clientId/:workerId')
|
||||||
getWorkerInfo(@Param('clientId') clientId: string, @Param('workerId') workerId: string) {
|
async getWorkerInfo(@Param('clientId') clientId: string, @Param('workerId') workerId: string) {
|
||||||
const worker = this.stratumV1Service.clients.find(client => client.clientAuthorization.address === clientId && client.id === workerId);
|
|
||||||
|
const worker = await this.clientService.getByAddressAndName(clientId, workerId);
|
||||||
|
|
||||||
|
//const worker = this.stratumV1Service.clients.find(client => client.clientAuthorization.address === clientId && client.id === workerId);
|
||||||
return {
|
return {
|
||||||
id: worker.id,
|
id: worker.id,
|
||||||
name: worker.clientAuthorization.worker,
|
name: worker.clientName,
|
||||||
bestDifficulty: Math.floor(worker.statistics.bestDifficulty),
|
bestDifficulty: Math.floor(worker.bestDifficulty),
|
||||||
hashData: worker.statistics.historicSubmissions,
|
//hashData: worker.statistics.historicSubmissions,
|
||||||
startTime: worker.startTime
|
startTime: worker.startTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-2
@@ -1,17 +1,30 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { BitcoinRpcService } from './bitcoin-rpc.service';
|
import { BitcoinRpcService } from './bitcoin-rpc.service';
|
||||||
import { BlockTemplateService } from './BlockTemplateService';
|
import { BlockTemplateService } from './BlockTemplateService';
|
||||||
|
import { ClientStatisticsModule } from './ORM/client-statistics/client-statistics.module';
|
||||||
|
import { ClientModule } from './ORM/client/client.module';
|
||||||
import { StratumV1Service } from './stratum-v1.service';
|
import { StratumV1Service } from './stratum-v1.service';
|
||||||
|
|
||||||
|
const ORMModules = [
|
||||||
|
ClientStatisticsModule,
|
||||||
|
ClientModule
|
||||||
|
]
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule.forRoot()
|
ConfigModule.forRoot(),
|
||||||
|
TypeOrmModule.forRoot({
|
||||||
|
type: 'sqlite',
|
||||||
|
database: './DB/public-pool.sqlite',
|
||||||
|
synchronize: true,
|
||||||
|
autoLoadEntities: true,
|
||||||
|
}),
|
||||||
|
...ORMModules
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ export class BitcoinRpcService {
|
|||||||
hexdata
|
hexdata
|
||||||
});
|
});
|
||||||
console.log(`BLOCK SUBMISSION RESPONSE: ${res}`);
|
console.log(`BLOCK SUBMISSION RESPONSE: ${res}`);
|
||||||
// console.log(JSON.stringify(res));
|
console.log(JSON.stringify(res));
|
||||||
// process.exit();
|
process.exit();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`BLOCK SUBMISSION RESPONSE ERROR: ${e}`);
|
console.log(`BLOCK SUBMISSION RESPONSE ERROR: ${e}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { combineLatest, interval, startWith } from 'rxjs';
|
|||||||
|
|
||||||
import { BitcoinRpcService } from '../bitcoin-rpc.service';
|
import { BitcoinRpcService } from '../bitcoin-rpc.service';
|
||||||
import { BlockTemplateService } from '../BlockTemplateService';
|
import { BlockTemplateService } from '../BlockTemplateService';
|
||||||
|
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
|
||||||
|
import { ClientEntity } from '../ORM/client/client.entity';
|
||||||
|
import { ClientService } from '../ORM/client/client.service';
|
||||||
import { StratumV1JobsService } from '../stratum-v1-jobs.service';
|
import { StratumV1JobsService } from '../stratum-v1-jobs.service';
|
||||||
import { EasyUnsubscribe } from '../utils/AutoUnsubscribe';
|
import { EasyUnsubscribe } from '../utils/AutoUnsubscribe';
|
||||||
import { eRequestMethod } from './enums/eRequestMethod';
|
import { eRequestMethod } from './enums/eRequestMethod';
|
||||||
@@ -22,12 +25,14 @@ import { StratumV1ClientStatistics } from './StratumV1ClientStatistics';
|
|||||||
|
|
||||||
export class StratumV1Client extends EasyUnsubscribe {
|
export class StratumV1Client extends EasyUnsubscribe {
|
||||||
|
|
||||||
|
public startTime: Date;
|
||||||
|
|
||||||
public clientSubscription: SubscriptionMessage;
|
public clientSubscription: SubscriptionMessage;
|
||||||
public clientConfiguration: ConfigurationMessage;
|
public clientConfiguration: ConfigurationMessage;
|
||||||
public clientAuthorization: AuthorizationMessage;
|
public clientAuthorization: AuthorizationMessage;
|
||||||
public clientSuggestedDifficulty: SuggestDifficulty;
|
public clientSuggestedDifficulty: SuggestDifficulty;
|
||||||
|
|
||||||
public statistics: StratumV1ClientStatistics = new StratumV1ClientStatistics();
|
public statistics: StratumV1ClientStatistics;
|
||||||
|
|
||||||
public id: string;
|
public id: string;
|
||||||
public stratumInitialized = false;
|
public stratumInitialized = false;
|
||||||
@@ -38,16 +43,20 @@ export class StratumV1Client extends EasyUnsubscribe {
|
|||||||
|
|
||||||
public jobRefreshInterval: NodeJS.Timer;
|
public jobRefreshInterval: NodeJS.Timer;
|
||||||
|
|
||||||
|
public entity: ClientEntity;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly socket: Socket,
|
public readonly socket: Socket,
|
||||||
private readonly stratumV1JobsService: StratumV1JobsService,
|
private readonly stratumV1JobsService: StratumV1JobsService,
|
||||||
private readonly blockTemplateService: BlockTemplateService,
|
private readonly blockTemplateService: BlockTemplateService,
|
||||||
private readonly bitcoinRpcService: BitcoinRpcService
|
private readonly bitcoinRpcService: BitcoinRpcService,
|
||||||
|
private readonly clientService: ClientService,
|
||||||
|
private readonly clientStatisticsService: ClientStatisticsService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
this.startTime = new Date();
|
||||||
|
this.statistics = new StratumV1ClientStatistics(this.clientStatisticsService);
|
||||||
this.id = this.getRandomHexString();
|
this.id = this.getRandomHexString();
|
||||||
|
|
||||||
console.log(`New client ID: : ${this.id}`);
|
console.log(`New client ID: : ${this.id}`);
|
||||||
@@ -194,7 +203,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
|||||||
const errors = await validate(miningSubmitMessage, validatorOptions);
|
const errors = await validate(miningSubmitMessage, validatorOptions);
|
||||||
|
|
||||||
if (errors.length === 0) {
|
if (errors.length === 0) {
|
||||||
this.handleMiningSubmission(miningSubmitMessage);
|
await this.handleMiningSubmission(miningSubmitMessage);
|
||||||
socket.write(JSON.stringify(miningSubmitMessage.response()) + '\n');
|
socket.write(JSON.stringify(miningSubmitMessage.response()) + '\n');
|
||||||
} else {
|
} else {
|
||||||
console.error(errors);
|
console.error(errors);
|
||||||
@@ -213,6 +222,15 @@ export class StratumV1Client extends EasyUnsubscribe {
|
|||||||
this.stratumInitialized = true;
|
this.stratumInitialized = true;
|
||||||
|
|
||||||
|
|
||||||
|
this.entity = await this.clientService.save({
|
||||||
|
id: this.id,
|
||||||
|
address: this.clientAuthorization.address,
|
||||||
|
clientName: this.clientAuthorization.worker,
|
||||||
|
startTime: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let lastIntervalCount = undefined;
|
let lastIntervalCount = undefined;
|
||||||
combineLatest([this.blockTemplateService.currentBlockTemplate$, interval(60000).pipe(startWith(-1))]).subscribe(([{ miningInfo, blockTemplate }, interValCount]) => {
|
combineLatest([this.blockTemplateService.currentBlockTemplate$, interval(60000).pipe(startWith(-1))]).subscribe(([{ miningInfo, blockTemplate }, interValCount]) => {
|
||||||
let clearJobs = false;
|
let clearJobs = false;
|
||||||
@@ -235,7 +253,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private handleMiningSubmission(submission: MiningSubmitMessage) {
|
private async handleMiningSubmission(submission: MiningSubmitMessage) {
|
||||||
|
|
||||||
const job = this.stratumV1JobsService.getJobById(submission.jobId);
|
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
|
// a miner may submit a job that doesn't exist anymore if it was removed by a new block notification
|
||||||
@@ -247,8 +265,11 @@ export class StratumV1Client extends EasyUnsubscribe {
|
|||||||
|
|
||||||
if (diff >= this.clientDifficulty) {
|
if (diff >= this.clientDifficulty) {
|
||||||
const networkDifficulty = this.calculateNetworkDifficulty(parseInt(job.blockTemplate.bits, 16));
|
const networkDifficulty = this.calculateNetworkDifficulty(parseInt(job.blockTemplate.bits, 16));
|
||||||
this.statistics.addSubmission(this.clientDifficulty, diff, networkDifficulty);
|
await this.statistics.addSubmission(this.entity, this.clientDifficulty);
|
||||||
if (diff >= networkDifficulty) {
|
if (diff > this.entity.bestDifficulty) {
|
||||||
|
await this.clientService.updateBestDifficulty(this.id, diff);
|
||||||
|
}
|
||||||
|
if (diff >= (networkDifficulty / 2)) {
|
||||||
console.log('!!! BOCK FOUND !!!');
|
console.log('!!! BOCK FOUND !!!');
|
||||||
this.constructBlockAndBroadcast(job, submission);
|
this.constructBlockAndBroadcast(job, submission);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,22 @@
|
|||||||
|
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
|
||||||
|
import { ClientEntity } from '../ORM/client/client.entity';
|
||||||
|
|
||||||
export class StratumV1ClientStatistics {
|
export class StratumV1ClientStatistics {
|
||||||
|
|
||||||
public bestDifficulty: number = 0;
|
|
||||||
|
|
||||||
public historicSubmissions = [];
|
constructor(private readonly clientStatisticsService: ClientStatisticsService) {
|
||||||
|
|
||||||
constructor() {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public addSubmission(targetDifficulty: number, submissionDifficulty: number, networkDifficulty: number) {
|
public async addSubmission(client: ClientEntity, targetDifficulty: number) {
|
||||||
|
|
||||||
if (this.historicSubmissions.length >= 1000) {
|
await this.clientStatisticsService.save({
|
||||||
this.historicSubmissions.shift();
|
time: new Date(),
|
||||||
}
|
|
||||||
|
|
||||||
this.historicSubmissions.push({
|
|
||||||
difficulty: targetDifficulty,
|
difficulty: targetDifficulty,
|
||||||
time: new Date()
|
client
|
||||||
});
|
});
|
||||||
|
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -4,17 +4,21 @@ import { Server, Socket } from 'net';
|
|||||||
import { BitcoinRpcService } from './bitcoin-rpc.service';
|
import { BitcoinRpcService } from './bitcoin-rpc.service';
|
||||||
import { BlockTemplateService } from './BlockTemplateService';
|
import { BlockTemplateService } from './BlockTemplateService';
|
||||||
import { StratumV1Client } from './models/StratumV1Client';
|
import { StratumV1Client } from './models/StratumV1Client';
|
||||||
|
import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service';
|
||||||
|
import { ClientService } from './ORM/client/client.service';
|
||||||
import { StratumV1JobsService } from './stratum-v1-jobs.service';
|
import { StratumV1JobsService } from './stratum-v1-jobs.service';
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StratumV1Service implements OnModuleInit {
|
export class StratumV1Service implements OnModuleInit {
|
||||||
|
|
||||||
public clients: StratumV1Client[] = [];
|
// public clients: StratumV1Client[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly bitcoinRpcService: BitcoinRpcService,
|
private readonly bitcoinRpcService: BitcoinRpcService,
|
||||||
private readonly blockTemplateService: BlockTemplateService
|
private readonly blockTemplateService: BlockTemplateService,
|
||||||
|
private readonly clientService: ClientService,
|
||||||
|
private readonly clientStatisticsService: ClientStatisticsService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,19 +30,24 @@ export class StratumV1Service implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private startSocketServer() {
|
private startSocketServer() {
|
||||||
new Server((socket: Socket) => {
|
new Server(async (socket: Socket) => {
|
||||||
|
|
||||||
|
|
||||||
|
const client = new StratumV1Client(socket, new StratumV1JobsService(), this.blockTemplateService, this.bitcoinRpcService, this.clientService, this.clientStatisticsService);
|
||||||
|
|
||||||
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`);
|
const clientCount = await this.clientService.connectedClientCount();
|
||||||
|
|
||||||
socket.on('end', () => {
|
//this.clients.push(client);
|
||||||
|
|
||||||
|
console.log(`New client connected: ${socket.remoteAddress}, ${clientCount} total clients`);
|
||||||
|
|
||||||
|
socket.on('end', async () => {
|
||||||
// Handle socket disconnection
|
// Handle socket disconnection
|
||||||
this.clients = this.clients.filter(c => c.id == client.id);
|
await this.clientService.delete(client.id);
|
||||||
console.log(`Client disconnected: ${socket.remoteAddress}, ${this.clients.length} total clients`);
|
|
||||||
|
const clientCount = await this.clientService.connectedClientCount();
|
||||||
|
console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('error', (error: Error) => {
|
socket.on('error', (error: Error) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user