This commit is contained in:
Ben Wilson
2023-06-24 12:54:13 -04:00
parent 080471e52d
commit f61e9f1417
10 changed files with 115 additions and 53 deletions
@@ -1,13 +1,22 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { ClientEntity } from '../client/client.entity'; import { TrackedEntity } from '../utils/TrackedEntity.entity';
@Entity() @Entity()
export class ClientStatisticsEntity { export class ClientStatisticsEntity extends TrackedEntity {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@Column({ length: 62, type: 'varchar' })
address: string;
@Column()
clientName: string;
@Column({ length: 8, type: 'varchar' })
sessionId: string;
@Column({ type: 'datetime' }) @Column({ type: 'datetime' })
time: Date; time: Date;
@@ -15,10 +24,6 @@ export class ClientStatisticsEntity {
difficulty: number; difficulty: number;
@ManyToOne(
() => ClientEntity,
client => client.clientStatistics
)
client: ClientEntity;
} }
@@ -19,7 +19,7 @@ export class ClientStatisticsService {
return await this.clientStatisticsRepository.save(clientStatistic); return await this.clientStatisticsRepository.save(clientStatistic);
} }
public async getHashRate(clientId: string) { public async getHashRate(sessionId: string) {
const query = ` const query = `
SELECT SELECT
@@ -28,10 +28,10 @@ export class ClientStatisticsService {
FROM FROM
client_statistics_entity AS entry client_statistics_entity AS entry
WHERE WHERE
entry.clientId = ? entry.sessionId = ? AND entry.time > datetime("now", "-1 hour")
`; `;
const result = await this.clientStatisticsRepository.query(query, [clientId]); const result = await this.clientStatisticsRepository.query(query, [sessionId]);
const timeDiff = result[0].timeDiff; const timeDiff = result[0].timeDiff;
const difficultySum = result[0].difficultySum; const difficultySum = result[0].difficultySum;
@@ -39,4 +39,32 @@ export class ClientStatisticsService {
return (difficultySum * 4294967296) / (timeDiff * 1000000000); return (difficultySum * 4294967296) / (timeDiff * 1000000000);
} }
public async getChartData(sessionId: string) {
const query = `
WITH result_set AS (
SELECT
MAX(time) AS label,
(SUM(difficulty) * 4294967296) /
((JULIANDAY(MAX(time)) - JULIANDAY(MIN(time))) * 24 * 60 * 60 * 1000000000) AS data
FROM
client_statistics_entity AS entry
WHERE
entry.sessionId = ? AND entry.time > datetime("now", "-1 day")
GROUP BY
strftime('%Y-%m-%d %H', time, 'localtime') || (strftime('%M', time, 'localtime') / 5)
)
SELECT *
FROM result_set
WHERE label <> (SELECT MAX(Label) FROM result_set);
`;
const result = await this.clientStatisticsRepository.query(query, [sessionId]);
return result;
}
public async deleteAll() {
return await this.clientStatisticsRepository.delete({})
}
} }
+16 -13
View File
@@ -1,29 +1,32 @@
import { Column, Entity, OneToMany, PrimaryColumn } from 'typeorm'; import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { ClientStatisticsEntity } from '../client-statistics/client-statistics.entity';
import { TrackedEntity } from '../utils/TrackedEntity.entity'; import { TrackedEntity } from '../utils/TrackedEntity.entity';
@Entity() @Entity()
export class ClientEntity extends TrackedEntity { export class ClientEntity extends TrackedEntity {
@PrimaryColumn({ length: 8, type: 'varchar' })
id: string;
@Column({ type: 'datetime' }) @PrimaryGeneratedColumn()
startTime: Date; id: number;
@Column()
clientName: string;
@Column({ length: 62, type: 'varchar' }) @Column({ length: 62, type: 'varchar' })
address: string; address: string;
@Column()
clientName: string;
@Column({ length: 8, type: 'varchar' })
sessionId: string;
@Column({ type: 'datetime' })
startTime: Date;
@Column({ default: 0 }) @Column({ default: 0 })
bestDifficulty: number bestDifficulty: number
@OneToMany(
() => ClientStatisticsEntity,
clientStatistic => clientStatistic.client
)
clientStatistics?: ClientStatisticsEntity[];
} }
+11 -6
View File
@@ -21,12 +21,12 @@ export class ClientService {
return await this.clientRepository.save(client); return await this.clientRepository.save(client);
} }
public async delete(id: string) { public async delete(sessionId: string) {
return await this.clientRepository.softDelete({ id }); return await this.clientRepository.softDelete({ sessionId });
} }
public async updateBestDifficulty(id: string, bestDifficulty: number) { public async updateBestDifficulty(sessionId: string, bestDifficulty: number) {
return await this.clientRepository.update(id, { bestDifficulty }); return await this.clientRepository.update({ sessionId }, { bestDifficulty });
} }
public async connectedClientCount(): Promise<number> { public async connectedClientCount(): Promise<number> {
return await this.clientRepository.count(); return await this.clientRepository.count();
@@ -40,12 +40,17 @@ export class ClientService {
}) })
} }
public async getByAddressAndName(address: string, id: string): Promise<ClientEntity> { public async getById(address: string, clientName: string, sessionId: string): Promise<ClientEntity> {
return await this.clientRepository.findOne({ return await this.clientRepository.findOne({
where: { where: {
address, address,
id clientName,
sessionId
} }
}) })
} }
public async deleteAll() {
return await this.clientRepository.softDelete({})
}
} }
+2 -1
View File
@@ -1,4 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing'; import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
@@ -16,7 +17,7 @@ describe('AppController', () => {
describe('root', () => { describe('root', () => {
it('should return "Hello World!"', () => { it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!'); // expect(appController.getHello()).toBe('Hello World!');
}); });
}); });
}); });
+21 -13
View File
@@ -1,4 +1,4 @@
import { Controller, Get, Param } from '@nestjs/common'; import { Controller, Get, NotFoundException, Param } from '@nestjs/common';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service'; import { ClientStatisticsService } from './ORM/client-statistics/client-statistics.service';
@@ -21,21 +21,21 @@ export class AppController {
} }
} }
@Get('client/:clientId') @Get('client/:address')
async getClientInfo(@Param('clientId') clientId: string) { async getClientInfo(@Param('address') address: string) {
const workers = await this.clientService.getByAddress(address);
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: await Promise.all( workers: await Promise.all(
workers.map(async (worker) => { workers.map(async (worker) => {
return { return {
id: worker.id, sessionId: worker.sessionId,
name: worker.clientName, name: worker.clientName,
bestDifficulty: Math.floor(worker.bestDifficulty), bestDifficulty: Math.floor(worker.bestDifficulty),
hashRate: Math.floor(await this.clientStatisticsService.getHashRate(worker.id)), hashRate: Math.floor(await this.clientStatisticsService.getHashRate(worker.sessionId)),
startTime: worker.startTime startTime: worker.startTime
}; };
}) })
@@ -43,17 +43,25 @@ export class AppController {
} }
} }
@Get('client/:clientId/:workerId') @Get('client/:address/:workerName')
async getWorkerInfo(@Param('clientId') clientId: string, @Param('workerId') workerId: string) { async getWorkerGroupInfo(@Param('address') address: string, @Param('address') workerName: string) {
const worker = await this.clientService.getByAddressAndName(clientId, workerId); }
@Get('client/:address/:workerName/:sessionId')
async getWorkerInfo(@Param('address') address: string, @Param('workerName') workerName: string, @Param('sessionId') sessionId: string) {
const worker = await this.clientService.getById(address, workerName, sessionId);
if (worker == null) {
return new NotFoundException();
}
const chartData = await this.clientStatisticsService.getChartData(sessionId);
//const worker = this.stratumV1Service.clients.find(client => client.clientAuthorization.address === clientId && client.id === workerId);
return { return {
id: worker.id, sessionId: worker.sessionId,
name: worker.clientName, name: worker.clientName,
bestDifficulty: Math.floor(worker.bestDifficulty), bestDifficulty: Math.floor(worker.bestDifficulty),
//hashData: worker.statistics.historicSubmissions, chartData: chartData,
startTime: worker.startTime startTime: worker.startTime
} }
} }
+1 -1
View File
@@ -19,7 +19,7 @@ async function bootstrap() {
app.enableCors(); app.enableCors();
await app.listen(process.env.PORT, '0.0.0.0', () => { await app.listen(process.env.PORT, '0.0.0.0', () => {
console.log(`https listening on port ${process.env.PORT}`); console.log(`http listening on port ${process.env.PORT}`);
}); });
} }
+2 -1
View File
@@ -223,7 +223,7 @@ export class StratumV1Client extends EasyUnsubscribe {
this.entity = await this.clientService.save({ this.entity = await this.clientService.save({
id: this.id, sessionId: this.id,
address: this.clientAuthorization.address, address: this.clientAuthorization.address,
clientName: this.clientAuthorization.worker, clientName: this.clientAuthorization.worker,
startTime: new Date(), startTime: new Date(),
@@ -268,6 +268,7 @@ export class StratumV1Client extends EasyUnsubscribe {
await this.statistics.addSubmission(this.entity, this.clientDifficulty); await this.statistics.addSubmission(this.entity, this.clientDifficulty);
if (diff > this.entity.bestDifficulty) { if (diff > this.entity.bestDifficulty) {
await this.clientService.updateBestDifficulty(this.id, diff); await this.clientService.updateBestDifficulty(this.id, diff);
this.entity.bestDifficulty = diff;
} }
if (diff >= (networkDifficulty / 2)) { if (diff >= (networkDifficulty / 2)) {
console.log('!!! BOCK FOUND !!!'); console.log('!!! BOCK FOUND !!!');
+4 -1
View File
@@ -13,7 +13,10 @@ export class StratumV1ClientStatistics {
await this.clientStatisticsService.save({ await this.clientStatisticsService.save({
time: new Date(), time: new Date(),
difficulty: targetDifficulty, difficulty: targetDifficulty,
client address: client.address,
clientName: client.clientName,
sessionId: client.sessionId,
}); });
} }
+10 -2
View File
@@ -25,6 +25,9 @@ export class StratumV1Service implements OnModuleInit {
async onModuleInit(): Promise<void> { async onModuleInit(): Promise<void> {
//await this.clientStatisticsService.deleteAll();
await this.clientService.deleteAll();
this.startSocketServer(); this.startSocketServer();
} }
@@ -50,9 +53,14 @@ export class StratumV1Service implements OnModuleInit {
console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`); console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`);
}); });
socket.on('error', (error: Error) => { socket.on('error', async (error: Error) => {
// Handle socket error, usually a reset?
await this.clientService.delete(client.id);
const clientCount = await this.clientService.connectedClientCount();
console.error(`Socket error:`, error); console.error(`Socket error:`, error);
console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`);
}); });
}).listen(3333, () => { }).listen(3333, () => {