mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 17:15:03 -07:00
Harden accounting and stratum production settings
This commit is contained in:
@@ -4,7 +4,6 @@ import { createClient, RedisClientType } from 'redis';
|
||||
|
||||
import { IBlockTemplate } from '../models/bitcoin-rpc/IBlockTemplate';
|
||||
import { IMiningInfo } from '../models/bitcoin-rpc/IMiningInfo';
|
||||
import { logTiming, timingStart } from '../utils/timing.utils';
|
||||
|
||||
const MINING_INFO_CHANNEL = 'mining-info.updated';
|
||||
const MINING_INFO_KEY = 'mining-info:latest';
|
||||
@@ -264,10 +263,8 @@ export class RedisMessagingService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
|
||||
private async getPresenceFromSet(setKey: string): Promise<ClientPresence[]> {
|
||||
const start = timingStart();
|
||||
const clientIds = await this.publisher.sMembers(setKey);
|
||||
if (clientIds.length === 0) {
|
||||
logTiming('redis presence set load', start, { setKey, clientIds: 0, presences: 0, staleClientIds: 0 });
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -297,12 +294,6 @@ export class RedisMessagingService implements OnModuleInit, OnModuleDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
logTiming('redis presence set load', start, {
|
||||
setKey,
|
||||
clientIds: clientIds.length,
|
||||
presences: presences.length,
|
||||
staleClientIds: staleClientIds.length,
|
||||
});
|
||||
return presences;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ describe('StratumV1Service', () => {
|
||||
const originalBackpressureEnabled = process.env.STRATUM_BACKPRESSURE_ENABLED;
|
||||
const originalMaxConnectionsPerListener = process.env.STRATUM_MAX_CONNECTIONS_PER_LISTENER;
|
||||
const originalTlsHandshakeTimeoutMs = process.env.STRATUM_TLS_HANDSHAKE_TIMEOUT_MS;
|
||||
const originalSocketTimeoutMs = process.env.STRATUM_SOCKET_TIMEOUT_MS;
|
||||
const originalTcpKeepAliveInitialDelayMs = process.env.STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS;
|
||||
|
||||
let service: StratumV1Service;
|
||||
let clientService;
|
||||
@@ -57,6 +59,8 @@ describe('StratumV1Service', () => {
|
||||
restoreEnv('STRATUM_BACKPRESSURE_ENABLED', originalBackpressureEnabled);
|
||||
restoreEnv('STRATUM_MAX_CONNECTIONS_PER_LISTENER', originalMaxConnectionsPerListener);
|
||||
restoreEnv('STRATUM_TLS_HANDSHAKE_TIMEOUT_MS', originalTlsHandshakeTimeoutMs);
|
||||
restoreEnv('STRATUM_SOCKET_TIMEOUT_MS', originalSocketTimeoutMs);
|
||||
restoreEnv('STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS', originalTcpKeepAliveInitialDelayMs);
|
||||
consoleLogSpy.mockRestore();
|
||||
consoleWarnSpy.mockRestore();
|
||||
jest.useRealTimers();
|
||||
@@ -164,6 +168,30 @@ describe('StratumV1Service', () => {
|
||||
expect((service as any).getTlsHandshakeTimeoutMs()).toBe(5000);
|
||||
});
|
||||
|
||||
it('should keep quiet miners connected for one hour by default', () => {
|
||||
delete process.env.STRATUM_SOCKET_TIMEOUT_MS;
|
||||
|
||||
expect((service as any).getSocketTimeoutMs()).toBe(1000 * 60 * 60);
|
||||
});
|
||||
|
||||
it('should allow configuring Stratum socket idle timeout', () => {
|
||||
process.env.STRATUM_SOCKET_TIMEOUT_MS = '7200000';
|
||||
|
||||
expect((service as any).getSocketTimeoutMs()).toBe(7200000);
|
||||
});
|
||||
|
||||
it('should enable TCP keepalive quickly by default', () => {
|
||||
delete process.env.STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS;
|
||||
|
||||
expect((service as any).getTcpKeepAliveInitialDelayMs()).toBe(60000);
|
||||
});
|
||||
|
||||
it('should allow configuring TCP keepalive initial delay', () => {
|
||||
process.env.STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS = '30000';
|
||||
|
||||
expect((service as any).getTcpKeepAliveInitialDelayMs()).toBe(30000);
|
||||
});
|
||||
|
||||
it('should detect JSON-RPC as Stratum V1', () => {
|
||||
const firstChunk = Buffer.from('{"id":1,"method":"mining.subscribe","params":[]}\n');
|
||||
|
||||
|
||||
@@ -35,6 +35,8 @@ const DEFAULT_BACKPRESSURE_RESUME_RSS_MB = 2000;
|
||||
const DEFAULT_BACKPRESSURE_HEALTHY_CHECKS = 3;
|
||||
const DEFAULT_MAX_CONNECTIONS_PER_LISTENER = 10000;
|
||||
const DEFAULT_TLS_HANDSHAKE_TIMEOUT_MS = 10000;
|
||||
const DEFAULT_SOCKET_TIMEOUT_MS = 1000 * 60 * 60;
|
||||
const DEFAULT_TCP_KEEPALIVE_INITIAL_DELAY_MS = 1000 * 60;
|
||||
|
||||
|
||||
|
||||
@@ -118,8 +120,8 @@ export class StratumV1Service implements OnModuleInit {
|
||||
|
||||
private createSocketServer(): Server {
|
||||
const server = new Server(async (socket: Socket) => {
|
||||
// Set 15-minute timeout
|
||||
socket.setTimeout(1000 * 60 * 15);
|
||||
socket.setTimeout(this.getSocketTimeoutMs());
|
||||
socket.setKeepAlive(true, this.getTcpKeepAliveInitialDelayMs());
|
||||
|
||||
let client: StratumV1Client | StratumV2Client = null;
|
||||
let protocol: 'v1' | 'v2' | null = null;
|
||||
@@ -235,8 +237,8 @@ export class StratumV1Service implements OnModuleInit {
|
||||
};
|
||||
|
||||
const server = createServer(tlsOptions, async (socket: TLSSocket) => {
|
||||
// Set 15-minute timeout
|
||||
socket.setTimeout(1000 * 60 * 15);
|
||||
socket.setTimeout(this.getSocketTimeoutMs());
|
||||
socket.setKeepAlive(true, this.getTcpKeepAliveInitialDelayMs());
|
||||
|
||||
const client = this.createV1Client(socket);
|
||||
|
||||
@@ -421,6 +423,14 @@ export class StratumV1Service implements OnModuleInit {
|
||||
return this.getPositiveIntegerEnv('STRATUM_TLS_HANDSHAKE_TIMEOUT_MS', DEFAULT_TLS_HANDSHAKE_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private getSocketTimeoutMs() {
|
||||
return this.getPositiveIntegerEnv('STRATUM_SOCKET_TIMEOUT_MS', DEFAULT_SOCKET_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
private getTcpKeepAliveInitialDelayMs() {
|
||||
return this.getPositiveIntegerEnv('STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS', DEFAULT_TCP_KEEPALIVE_INITIAL_DELAY_MS);
|
||||
}
|
||||
|
||||
private detectProtocol(firstChunk: Buffer): 'v1' | 'v2' | null {
|
||||
if (firstChunk.length === 0) {
|
||||
return null;
|
||||
|
||||
@@ -22,6 +22,9 @@ import { NotificationService } from './notification.service';
|
||||
import { RedisMessagingService } from './redis-messaging.service';
|
||||
import { StratumV1JobsService } from './stratum-v1-jobs.service';
|
||||
|
||||
const DEFAULT_SOCKET_TIMEOUT_MS = 1000 * 60 * 60;
|
||||
const DEFAULT_TCP_KEEPALIVE_INITIAL_DELAY_MS = 1000 * 60;
|
||||
|
||||
@Injectable()
|
||||
export class StratumV2Service implements OnModuleInit {
|
||||
private readonly servers: Server[] = [];
|
||||
@@ -171,6 +174,7 @@ export class StratumV2Service implements OnModuleInit {
|
||||
private startSocketServer(port: number): void {
|
||||
const server = new Server((socket: Socket) => {
|
||||
socket.setTimeout(this.getSocketTimeoutMs());
|
||||
socket.setKeepAlive(true, this.getTcpKeepAliveInitialDelayMs());
|
||||
socket.setNoDelay(true);
|
||||
|
||||
let client: StratumV2Client = null;
|
||||
@@ -213,7 +217,21 @@ export class StratumV2Service implements OnModuleInit {
|
||||
}
|
||||
|
||||
private getSocketTimeoutMs(): number {
|
||||
const configured = parseInt(this.configService.get<string>('STRATUM_V2_SOCKET_TIMEOUT_MS') ?? '', 10);
|
||||
return Number.isFinite(configured) && configured > 0 ? configured : 1000 * 60 * 15;
|
||||
const configured = parseInt(
|
||||
this.configService.get<string>('STRATUM_V2_SOCKET_TIMEOUT_MS')
|
||||
?? this.configService.get<string>('STRATUM_SOCKET_TIMEOUT_MS')
|
||||
?? '',
|
||||
10,
|
||||
);
|
||||
return Number.isFinite(configured) && configured > 0 ? configured : DEFAULT_SOCKET_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
private getTcpKeepAliveInitialDelayMs(): number {
|
||||
const configured = parseInt(
|
||||
this.configService.get<string>('STRATUM_TCP_KEEPALIVE_INITIAL_DELAY_MS')
|
||||
?? '',
|
||||
10,
|
||||
);
|
||||
return Number.isFinite(configured) && configured > 0 ? configured : DEFAULT_TCP_KEEPALIVE_INITIAL_DELAY_MS;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user